class Beat: KEY_TAP=pygame.K_SPACE KEY_PHASE=pygame.K_RETURN KEY_RESET=pygame.K_ESCAPE EFFECT_HISTORY_LENGTH=2 KEYBOARD={ pygame.K_q:(0,0), pygame.K_w:(1,0), pygame.K_e:(2,0), pygame.K_r:(3,0), pygame.K_t:(4,0), pygame.K_y:(5,0), pygame.K_u:(6,0), pygame.K_i:(7,0), pygame.K_a:(0,1), pygame.K_s:(1,1), pygame.K_d:(2,1), pygame.K_f:(3,1), pygame.K_g:(4,1), pygame.K_h:(5,1), pygame.K_j:(6,1), pygame.K_k:(7,1), pygame.K_z:(0,2), pygame.K_x:(1,2), pygame.K_c:(2,2), pygame.K_v:(3,2), pygame.K_b:(4,2), pygame.K_n:(5,2), pygame.K_m:(6,2), pygame.K_COMMA:(7,2), } FUNCTION_KEYS={ pygame.K_F1:1, pygame.K_F2:2, pygame.K_F3:3, pygame.K_F4:4, pygame.K_F5:5, pygame.K_F6:6, pygame.K_F7:7, pygame.K_F8:8, pygame.K_F9:9, pygame.K_F10:10, pygame.K_F11:11, pygame.K_F12:12, } PATTERN_KEYS={ pygame.K_1:1, pygame.K_2:2, pygame.K_3:3, pygame.K_4:4, pygame.K_5:5, pygame.K_6:6, pygame.K_7:7, pygame.K_8:8, pygame.K_9:9, pygame.K_0:0, } KB_REMAP={ pygame.K_RIGHTBRACKET:(0,1), pygame.K_QUOTE:(1,1), pygame.K_SLASH:(2,1), pygame.K_LEFTBRACKET:(0,-1), pygame.K_SEMICOLON:(1,-1), pygame.K_PERIOD:(2,-1), } COLORS=[ (255,255,255), # white (255,0,0), # red (255,20,0), # orange (255,80,0), # yellow (50,100,0), # green (0,30,80), # blue (255,0,50), # purple (0,0,0), # black ] EFFECT_NAMES=[ 'on', 'strobe', 'sweep', 'rev sweep', 'fast sweep', 'fast rev sweep', 'pulse', 'rev pulse', 'fast pulse', 'fast rev pulse', 'fade in', 'fade out', 'fast fade in', 'fast fade out', 'rainbow!', ] def __init__(self): pygame.init() self.size=1024,480 self.screen=pygame.display.set_mode(self.size) self.bg=0,0,0 self.fg=255,255,255 self.audio_color=0,0,255 self.first_click=0 self.last_click=0 self.clicks=0 self.last_tick=0 self.effects={} self.function=1 self.shift=False self.forever=False self.forever_patterns={} self.forever_fn={} self.last_stop=0 self.recording=False self.recorded={} self.coarse_grain=1 self.fine_grain=4 self.saved_patterns={} self.rec_pat=None self.kb_map=[0,1,2] self.last_effect=collections.deque() self.r=Recorder() self.tb=TimeBase() self.tb.register_tickfn(self.tick) if len(sys.argv)>=2 and sys.argv[1]=='fake': b=[FakeSingleBespeckleDevice('/dev/ttyUSB0',115200)] else: b = [] for i in range(10): try: b.append(SingleBespeckleDevice('/dev/ttyUSB%d' % i, 3000000)) if len(b) >= 4: break except: pass self.oa=OutputAdapter(b,self.tb) self.oa.add_reset() self.seq=Sequencer(self.tb,self.oa) self.oa.start() self.r.start() self.tb.start() self.seq.start() while True: quit=False for event in pygame.event.get(): if event.type==pygame.QUIT: quit=True if event.type==pygame.KEYDOWN: if event.key==self.KEY_TAP: self.tap() elif event.key==self.KEY_PHASE: self.phase() elif event.key==pygame.K_UP: self.tempo_adjust(1.) elif event.key==pygame.K_DOWN: self.tempo_adjust(-1.) elif event.key in self.KEYBOARD: self.effect(self.KEYBOARD[event.key]) elif event.key==pygame.K_TAB: self.start_recording() elif event.key in self.PATTERN_KEYS: k=self.PATTERN_KEYS[event.key] if self.rec_pat is not None: self.save_pattern(k) else: p=self.pattern_map(k) if self.forever: if k in self.forever_patterns: self.remove_pattern(self.forever_patterns[k]) self.forever_patterns.pop(k) elif p is not None: self.forever_patterns[k]=self.add_pattern(p,self.shift,self.forever) elif p is not None: self.add_pattern(p,self.shift,self.forever) elif event.key in self.FUNCTION_KEYS: k=self.FUNCTION_KEYS[event.key] p=self.function_map(k) if self.forever: if k in self.forever_fn: self.remove_pattern(self.forever_fn[k]) self.forever_fn.pop(k) elif p is not None: self.forever_fn[k]=self.add_pattern(p,self.shift,self.forever) elif p is not None: self.add_pattern(p,self.shift,self.forever) elif event.key in self.KB_REMAP: self.kb_remap(*self.KB_REMAP[event.key]) elif event.key==pygame.K_LSHIFT: self.shift=True elif event.key==pygame.K_LCTRL: self.forever=True elif event.key==self.KEY_RESET: self.oa.add_reset() elif event.type==pygame.KEYUP: if event.key in self.KEYBOARD: self.effect_release(self.KEYBOARD[event.key]) elif event.key==pygame.K_LSHIFT: self.shift=False elif event.key==pygame.K_LCTRL: self.forever=False elif event.key==pygame.K_TAB: self.stop_recording() if quit: break font = pygame.font.Font(None, 36) text = font.render("TEMPO:"+str(self.tb.get_tempo())+" BPM", 1, self.fg) if self.recording: msg='recording' elif self.rec_pat is not None: msg='press a key to store' else: msg='not recording' text2 = font.render(msg,1,self.fg) audio=graphics.audio_widget(self.r,self.tb) pattern_timeline=graphics.pattern_timeline_widget(self.tb,self.seq,float(self.r.CHUNK)/self.r.RATE) self.screen.fill(self.bg) self.screen.blit(text, (0,0)) self.screen.blit(text2, (0,30)) self.screen.blit(audio, (0,60)) self.screen.blit(pattern_timeline, (audio.get_width(),60)) kb=[font.render(self.EFFECT_NAMES[self.kb_map[row]],1,self.fg) for row in range(3)] self.screen.blit(kb[0], (0,200)) self.screen.blit(kb[1], (0,230)) self.screen.blit(kb[2], (0,260)) pygame.display.flip() time.sleep(0) self.seq.stop() self.r.stop() self.tb.stop() self.oa.stop() self.seq.join() self.r.join() self.tb.join() self.oa.join() def tick(self,num): self.r.set_beatline() self.last_tick=time.time() self.oa.add_tick() def tempo_adjust(self,amt): tempo=round(self.tb.get_tempo(),0) tempo+=amt self.tb.set_tempo(tempo) def tap(self): t=time.time() if t-self.last_click>2: self.first_click=t self.clicks=0 else: self.clicks+=1 self.tb.sync_period((t-self.first_click)/self.clicks) self.last_click=t def phase(self): t=time.time() self.tb.sync_phase(t) def kb_remap(self,remap_row,increment): self.kb_map[remap_row]=(self.kb_map[remap_row]+increment)%len(self.EFFECT_NAMES) def effect_map(self,(c,r)): if c>=len(self.COLORS): return None en=self.EFFECT_NAMES[self.kb_map[r]] opacity=0xFF if self.shift: opacity/=2 color=self.COLORS[c]+(opacity,) if en=='on': return Effect(0x10,color,render=lambda *x:graphics.full_color(graphics.strip2screen(color),*x)) if en=='strobe': return Effect(0x40,color+(0x00,0x10),False,render=lambda *x:graphics.strobe(graphics.strip2screen(color),*x)) if en=='sweep': return Effect(0x41,color+(0x02,0x00),render=lambda *x:graphics.full_color(graphics.strip2screen(color),*x)) if en=='rev sweep': return Effect(0x41,color+(0x82,0x00),render=lambda *x:graphics.full_color(graphics.strip2screen(color),*x)) if en=='pulse': return Effect(0x42,color+(0x04,0x00),False,render=lambda *x:graphics.strobe(graphics.strip2screen(color),*x)) if en=='rev pulse': return Effect(0x42,color+(0x84,0x00),False,render=lambda *x:graphics.strobe(graphics.strip2screen(color),*x)) if en=='fast sweep': return Effect(0x41,color+(0x01,0x00),render=lambda *x:graphics.full_color(graphics.strip2screen(color),*x)) if en=='fast rev sweep': return Effect(0x41,color+(0x81,0x00),render=lambda *x:graphics.full_color(graphics.strip2screen(color),*x)) if en=='fast pulse': return Effect(0x42,color+(0x02,0x00),False,render=lambda *x:graphics.strobe(graphics.strip2screen(color),*x)) if en=='fast rev pulse': return Effect(0x42,color+(0x082,0x00),False,render=lambda *x:graphics.strobe(graphics.strip2screen(color),*x)) if en=='fade in': return Effect(0x43,color+(0x02,0x00),render=lambda *x:graphics.full_color(graphics.strip2screen(color),*x)) if en=='fade out': return Effect(0x43,color+(0x82,0x00),render=lambda *x:graphics.full_color(graphics.strip2screen(color),*x)) if en=='fast fade in': return Effect(0x43,color+(0x01,0x00),render=lambda *x:graphics.full_color(graphics.strip2screen(color),*x)) if en=='fast fade out': return Effect(0x43,color+(0x81,0x00),render=lambda *x:graphics.full_color(graphics.strip2screen(color),*x)) if en=='rainbow!': return Effect(0x03,(0x00, 0x04, 0x04),render=lambda *x:graphics.full_color(graphics.strip2screen(color),*x)) return None