source: trunk/LiveControl/Sequencer.py @ 68

Revision 68, 16.3 KB checked in by st8, 6 months ago (diff)

LiveControl: Added Clip sequencer application, doubled resolution of device control. LiveOSC: Added toggles for meter/clip listeners, added transport listener

Line 
1"""
2# Copyright (C) 2009 ST8 <st8@q3f.org>
3#
4# This library is free software; you can redistribute it and/or
5# modify it under the terms of the GNU Lesser General Public
6# License as published by the Free Software Foundation; either
7# version 2.1 of the License, or (at your option) any later version.
8#
9# This library is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12# Lesser General Public License for more details.
13#
14# You should have received a copy of the GNU Lesser General Public
15# License along with this library; if not, write to the Free Software
16# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17#
18# For questions regarding this module contact
19# ST8 <st8@q3f.org> or visit http://monome.q3f.org
20"""
21
22from LiveControl import LiveControl
23import Live
24
25class Sequencer(LiveControl):
26    name = "Step Sequencer"
27   
28    def __init__(self, c_instance, oscServer, logger, id = 0):
29        self.c_instance = c_instance
30        self.oscServer = oscServer
31        self.logger = logger
32       
33        self.quan = 0.5
34        self.bank = 0
35        self.vel = 100.0
36        self.offset = 60
37        self.refresh = 0
38        self.fold = 0
39
40        self.menu = 0
41        self.update = 0
42       
43        self.sel_scene = 0
44        self.sel_track = 0
45        self.sel_clip  = None
46        self.note_cache = {}
47        self.real_notes = []
48        self.last_note = 60
49       
50        self.clip_change()
51       
52    def dest(self):
53        if self.sel_clip != None:
54            if self.sel_clip.notes_has_listener(self.note_change) == 1:
55                self.sel_clip.remove_notes_listener(self.note_change)
56       
57    def clip_change(self):
58        if len(self.song().visible_tracks) > self.sel_track:
59            cl = self.song().visible_tracks[self.sel_track].clip_slots[self.sel_scene].clip
60   
61            if cl != None and cl.is_midi_clip == 1 and cl != self.sel_clip:
62                if self.sel_clip != None:
63                    if self.sel_clip.notes_has_listener(self.note_change) == 1:
64                        self.sel_clip.remove_notes_listener(self.note_change)
65           
66                self.sel_clip = cl
67                self.update_notes()
68               
69                self.sel_clip.add_notes_listener(self.note_change)
70           
71                self.log("clip: " + str(self.sel_clip) + " t:" + str(self.sel_track) + " :" + str(self.sel_scene))
72           
73            else:
74                self.note_cache = {}
75                self.sel_clip = None
76               
77        self.build_lcd()
78
79    def update_notes(self):
80        self.sel_clip.select_all_notes()
81        all_notes = self.sel_clip.get_selected_notes()
82        self.sel_clip.deselect_all_notes()
83               
84        notes = {}
85        for note in all_notes:
86            if notes.has_key(note[1]):
87                notes[note[1]].append([note[0],note[3],note[2]])
88            else:
89                notes[note[1]] = [[note[0],note[3],note[2]]]
90               
91        self.log(str(notes))
92       
93        self.note_cache = notes
94        self.real_notes = all_notes
95       
96        self.log("ALL: " + str(all_notes))
97
98    def do_refresh_state(self):
99        pass
100       
101    def do_update_display(self):
102        cols = [0 for i in range(self.width)]
103       
104        if self.sel_clip != None:
105                cp = self.sel_clip.playing_position
106        else:
107            cp = 0
108           
109        if self.fold == 1:
110            folded_notes = self.note_keys()
111       
112        for i in range(self.width):
113            byte = 0
114            pos = self.pos(i)
115
116            if self.menu == 2:
117                if self.note_cache.has_key(pos):
118                    for nt in self.note_cache[pos]:
119                        #self.log(str(nt) + " last note: " + str(self.last_note))
120                        if nt[0] == self.last_note:
121                            vel = int(nt[1]/self.vel * (self.height - 2))
122                            vel = vel > self.height - 2 and (self.height - 2) or vel
123                            vel = vel < 0 and 0 or vel
124                            byte = ((1 << vel+1) - 1) << (self.height - 2 - vel)
125   
126            else:
127                if self.fold == 1:
128                    for j in range(self.height - 1):
129                        id = j + self.offset - 60
130                        if id < len(folded_notes) and id > -1:
131                            if self.note_cache.has_key(pos):
132                                for nt in self.note_cache[pos]:
133                                    if nt[0] == folded_notes[id]:
134                                        byte |= 1 << self.height - 2 - j
135               
136                else:
137                    if self.note_cache.has_key(pos):
138                        for j in range(len(self.note_cache[pos])):
139                            if (self.note_cache[pos][j][0] - self.offset >= 0) and (self.note_cache[pos][j][0] - self.offset < self.height - 1):
140                                val = 1 << (self.height - 2 - (self.note_cache[pos][j][0] - self.offset))
141                                byte |= val
142                               
143                                # nasty hack :/
144                                nl = self.note_cache[pos][j][2]
145                                if nl > self.quan:
146                                    for k in range(i,self.width):
147                                        id = k - i
148                                        if (id*self.quan) < nl:
149                                            cols[k] |= val
150                               
151                               
152
153                if cp > pos and cp < pos + self.quan:
154                    byte = ~byte & ((1 << self.height - 1) - 1)
155
156            if i == self.width - 2 and self.menu == 1:
157                byte |= 1 << self.height - 1
158               
159            elif i == self.width - 3 and self.menu == 2:
160                byte |= 1 << self.height - 1
161           
162            if i == self.width - 4 and self.fold == 1 and self.menu == 1:
163                byte |= 1 << self.height - 1
164
165            cols[i] |= byte
166       
167        return cols
168
169    def do_button_press(self, x, y, v):
170        if v == 1:
171            # Sequencer options
172            if y == self.height - 1:
173                if x == self.width - 2:
174                    if self.menu == 2:
175                        self.menu = 0
176                    else:
177                        self.menu += 1
178                   
179                    self.log(str(self.menu))
180                   
181                elif self.menu == 0:
182                    # Quantiation Up
183                    if x == self.width - 3:
184                        if self.quan > 0.0625:
185                            self.quan /= 2
186                            self.log(str(self.quan))
187                           
188                    # Quantisation Down
189                    if x == self.width - 4:
190                        if self.quan < 8:
191                            self.quan *= 2
192                            self.log(str(self.bank))
193                               
194                            if self.sel_clip != None:
195                                if self.sel_clip.length <= (self.quan*self.bank*self.width):
196                                    self.bank = int(self.sel_clip.length/(self.quan * self.width) - 1)
197                   
198                    # Note Offset Up
199                    if x == self.width - 5:
200                        if self.offset < 127 - self.height - 2:
201                            self.offset += 1
202                       
203                    # Note Offset Down
204                    if x == self.width - 6:
205                        if self.offset > 0:
206                            self.offset -= 1
207                       
208                    # Bank Up
209                    if x == self.width - 7:
210                        if self.sel_clip != None:
211                            if self.bank < self.sel_clip.length/(self.quan * self.width) - 1:
212                                self.bank += 1
213                               
214                    # Bank Down
215                    if x == self.width - 8:
216                        if self.bank > 0:
217                            self.bank -= 1
218                        self.log(str(self.bank))
219
220                if self.menu == 1:
221                    # Fold Notes
222                    if x == self.width - 4:
223                        if self.fold == 1:
224                            self.fold = 0
225                        else:
226                            self.fold = 1 
227                           
228                        self.offset = 60
229                               
230                    # Start/Stop Clips
231                    if x == self.width - 3:
232                        if self.sel_clip != None:
233                            if self.sel_clip.is_playing == 1:
234                                self.sel_clip.stop()
235                            else:
236                                self.sel_clip.fire()
237                                   
238                    # Track Right
239                    if x == self.width - 5:
240                        if self.sel_track < len(self.song().visible_tracks) - 1:
241                            self.sel_track += 1
242                            self.update_selection()
243                            self.clip_change()
244                   
245                    # Track Left
246                    if x == self.width - 6:
247                        if self.sel_track > 0:
248                            self.sel_track -= 1
249                            self.update_selection()
250                            self.clip_change()
251                                                                           
252                    # Scene Up
253                    if x == self.width - 7:
254                        if self.sel_scene < len(self.song().scenes):
255                            self.sel_scene += 1
256                            self.update_selection()
257                            self.clip_change()
258                           
259                    # Scene Down
260                    if x == self.width - 8:
261                        if self.sel_scene > 0:
262                            self.sel_scene -= 1
263                            self.update_selection()
264                            self.clip_change()
265                       
266                if self.menu == 2:
267                    # Lengthen Clip
268                    if x == self.width - 3:
269                        if self.sel_clip != None:
270                            self.sel_clip.loop_end = (self.sel_clip.length * 2) + self.sel_clip.loop_start
271
272                    # Shorten Clip
273                    if x == self.width - 4:
274                        if self.sel_clip != None:
275                            self.sel_clip.loop_end = (self.sel_clip.length / 2) + self.sel_clip.loop_start
276                           
277                    # Velocity Up
278                    if x == self.width - 5:
279                        if self.vel < 120:
280                            self.vel += 10
281
282                    # Velocity Down
283                    if x == self.width - 6:
284                        if self.vel > 0:
285                            self.vel -= 10
286                           
287                    # Selected Note Up
288                    if x == self.width - 7:
289                        if self.last_note < 127:
290                                self.last_note += 1
291
292                    # Selected Note Down
293                    if x == self.width - 8:
294                        if self.last_note > 0:
295                                self.last_note -= 1
296           
297            else:           
298                # Velocity editor
299                if self.menu == 2:
300                    vel = (self.vel/(self.height - 2)) * (self.height - 2 - y)
301                    self.log(str(vel))
302                    self.update_vel(self.last_note, self.pos(x), vel)
303               
304                # Add / remove notes
305                else:
306                    pos = self.pos(x)
307                   
308                    if self.fold == 1:
309                        folded_notes = self.note_keys()
310                        id = self.height - 2 - y + self.offset - 60
311                        self.log(str(id) + " " + str(folded_notes))
312                        if len(folded_notes) > id:
313                            note = folded_notes[id]
314                        else:
315                            note = -1
316                    else:
317                        note = self.height - 2 - y + self.offset
318               
319                    if note > -1:
320                        if self.note_cache.has_key(pos):
321                            found = 0
322                            for nt in self.note_cache[pos]:
323                                if nt[0] == note:
324                                    found = 1
325                       
326                            if found == 0:
327                                self.add_note(pos,note)
328                            else:
329                                self.rem_note(pos,note)
330                           
331                        else:
332                            self.add_note(pos,note)
333           
334            self.build_lcd()
335           
336    def pos(self, pos):
337        return (self.bank * self.width * self.quan) + (pos * self.quan)
338           
339    def add_note(self, pos, note):
340        notes = list(self.real_notes)
341        notes.append([note, pos, self.quan, self.vel, False])
342
343        self.sel_clip.deselect_all_notes()
344        self.sel_clip.replace_selected_notes(tuple(notes))
345               
346        self.last_note = note
347        self.update_notes()
348
349    def rem_note(self, pos, note):
350        new_notes = []
351       
352        for nt in self.real_notes:
353            if nt[0] == note and nt[1] == pos:
354                pass
355            else:
356                new_notes.append(nt)
357               
358        self.sel_clip.select_all_notes()
359        self.sel_clip.replace_selected_notes(tuple(new_notes))
360        self.sel_clip.deselect_all_notes()
361       
362        self.last_note = note
363        self.update_notes()
364   
365    def update_vel(self, note, pos, vel):
366        note_out = []
367        for nt in self.real_notes:
368            if nt[0] == note and nt[1] == pos:
369                note_out.append([nt[0], nt[1], nt[2], vel, False])
370            else:
371                note_out.append(nt)
372
373        if self.note_cache.has_key(pos):
374            for j in range(len(self.note_cache[pos])):
375                if self.note_cache[pos][j][0] == note:
376                    self.note_cache[pos][j][1] = vel
377
378        self.real_notes = note_out
379        self.sel_clip.select_all_notes()
380        self.sel_clip.replace_selected_notes(tuple(note_out))
381        self.sel_clip.deselect_all_notes()
382       
383    def note_keys(self):
384        list = {}
385       
386        for note in self.real_notes:
387            list[note[0]] = 1
388       
389        return sorted(list.keys())
390       
391    def note_change(self):
392        self.update = 3
393
394    def build_lcd(self, type = 0):
395        line2 = ""
396        last = 0
397
398        for i in range(0, self.lcd_width, 2):
399            bt = self.beat_time(self.pos(i))
400           
401            if bt == last:
402                line2 += str().ljust(10)
403            else:
404                line2 += bt.ljust(10)
405               
406            last = bt
407
408        if self.sel_clip != None:
409            size = self.quan > 4 and str(int(self.quan/4)) or ("1/" + str(int(4/self.quan)))
410            cn = str(self.sel_clip.name)
411        else:
412            size = "N/A"
413            cn = "None"
414           
415        tn = str(self.song().visible_tracks[self.sel_track].name)
416        ex = "St:" + size + " V:" + str(int(self.vel)) + " R:" + self.to_note(self.offset) + " S:" + self.to_note(self.last_note)
417
418        self.lcd = [self.trunc_string(tn + ": " + cn, self.lcd_step - len(ex)) + ex,line2]
419
420    def beat_time(self, time):
421        beats = int(time % 4)
422        bars  = int(time/4)
423        qb    = int(time * 4 % 4)
424
425        return str(bars+1) + (self.quan < 2 and ("." + str(beats+1)) or "") + (self.quan < 0.5 and ("." + str(qb+1)) or "")
426
427    def to_note(self, note):
428        notes = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B']
429        return notes[int(note%12)] + str(int(note / 12) - 2)
430
431    def do_bg(self):
432        if self.update > 0:
433            self.update -= 1
434
435        if self.update == 1:
436            self.update_notes()
437       
438    def update_selection(self):
439        self.c_instance.set_session_highlight(self.sel_track,self.sel_scene,1,1,0)
440
441    def do_recieve_midi(self, midi_bytes):
442        pass
Note: See TracBrowser for help on using the repository browser.