def __init__(self, offset=(0, 1), record_state=False, cyclic=False, colorscheme=0): """ Stores all of the required vars to send signals to the launchpad Offset arg refers to x, y offset Color scale runs from 0-14 (fewer options, but acts as a simple spectrum) methods include a runner, which accepts a function to act on the object """ self.lp = Launchpad() self.lp.Open() self.lp.LedCtrlXY(0, 0, 0, 3) self.lp.LedCtrlXY(0, 0, 0, 0) self.offset = offset cols = ([(0, 3), (1, 3), (1, 2), (2, 2), (2, 1), (3, 1), (3, 0)], [(3, 0), (3, 1), (2, 1), (2, 1), (2, 0), (1, 0), (0, 0)]) tmp = cols[colorscheme] if cyclic == True: self.colorscale = { x: y for x, y in enumerate(tmp[:-1] + tmp[::-1][:-1]) } else: self.colorscale = {x: y for x, y in enumerate(tmp)} self.lp.Reset() self.record_state = record_state self.lp.state = {x: {y: -1 for y in xrange(8)} for x in xrange(8)}
def __init__(self): logging.basicConfig(level=logging.INFO) self.sub = [] self.handlers = [] self.load_all_modules_from_dir("subscriber") self.register_handlers() self.lp = Launchpad() self.lp.reset()
class Connector(object): def __init__(self): logging.basicConfig(level=logging.INFO) self.sub = [] self.handlers = [] self.load_all_modules_from_dir("subscriber") self.register_handlers() self.lp = Launchpad() self.lp.reset() def load_all_modules_from_dir(self, dirname): for importer, package_name, _ in pkgutil.iter_modules([dirname]): full_package_name = '%s.%s' % (dirname, package_name) if full_package_name not in sys.modules: module = importer.find_module(package_name).load_module( full_package_name) class_object = getattr( module, str(module.__name__).split('.')[-1].title()) class_instance = class_object() self.sub.append(class_instance) logger.info("imported subscriber {}".format(class_object)) def register_handlers(self): for elem in self.sub: self.handlers.append(elem.consume) def loop(self): logger.info("event loop running") while True: try: buttonEvent = self.lp.receive() if buttonEvent is not None: for func in self.handlers: func(buttonEvent) else: time.sleep(0.1) except KeyboardInterrupt: for elem in self.sub: elem.close() self.lp.close() sys.exit(0) except Exception as e: logger.error(e) raise
def __init__(self, os_name, package_name, flair='flair', log_level=logging.INFO, is_compress=True): self._flair = Flair(flair) self._archive = Launchpad() self._os_name = os_name self._package_name = package_name self._is_compress = is_compress self._logger = logging.getLogger('Allirt') self._logger.setLevel(log_level) stream_handler = logging.StreamHandler() formatter = logging.Formatter('[%(levelname)s] %(message)s') stream_handler.setFormatter(formatter) self._logger.addHandler(stream_handler)
def __init__(self): args = _parse_args() if not args.port: print("ERROR: Please use -p option to specify device midi port.") sys.exit(1) # connect with the launchpad print("Connecting with launchpad") self.launchpad = Launchpad(args.port, self.process_midi_event) # load the plugins print("Loading plugins using config file: %s" % args.configfile) load_plugins(self.launchpad, args.configfile) # start reading midi events self.launchpad.read_midi_event()
def __init__(self): self.pad = Launchpad() #None indicates home screen self.active_column = None self.columns = [] self.position_x = 0 self.position_y = 0 self.input_context = None sequencer.add(self)
class Twister(object): def __init__(self): args = _parse_args() if not args.port: print("ERROR: Please use -p option to specify device midi port.") sys.exit(1) # connect with the launchpad print("Connecting with launchpad") self.launchpad = Launchpad(args.port, self.process_midi_event) # load the plugins print("Loading plugins using config file: %s" % args.configfile) load_plugins(self.launchpad, args.configfile) # start reading midi events self.launchpad.read_midi_event() def process_midi_event(self, data): if data is None: return # for note events there is note and velocity, # for control events those are called param and value # in alsa, but we just call them note and velocity also midi_type, channel, note, velocity = data plugin = self.launchpad.get_plugin(note) if not plugin: return # figure out which callback to call if midi_type == Launchpad.TYPE_NOTEON and velocity == 0: plugin.pad_up_callback(note - plugin.position) elif midi_type == Launchpad.TYPE_NOTEON: plugin.pad_down_callback(note - plugin.position) elif midi_type == Launchpad.TYPE_CONTROL: launchpad.shutdown() reactor.shutdown()
def __init__(self): self.audio = PyAudio() self.page = 0 self.samples = [None] * 8 self.sampleI = 0 self.playing = False self.playingPage = 0 self.playingCol = 0 self.pages = [[[0] * 8 for i in xrange(8)] for i in xrange(8)] self.padPlayMode = False self.recordMode = False self.recording = None self.showBPM = False self.bpm = 240 self.bpmPressed = False self.bpmTimePressed = None self.dev = Launchpad() self.dev.handler = self.handler self.setLight = self.dev.setLight self.repaint() bpmState = True while True: if self.showBPM: since = clock() - self.bpmTimePressed if since >= 2 and not self.bpmPressed: self.showBPM = False self.setLight(4, 0, False, self.page == 4) else: per = 0.5 / self.bpm * 60 since %= per * 2 if since < per: if bpmState == True: self.setLight(4, 0, True, False) bpmState = False sleep(per / 2.0) elif bpmState == False: self.setLight(4, 0, False, self.page == 4) bpmState = True sleep(per / 2.0) else: sleep(0.5)
class Allirt(): _flair = None _archive = None _os_name = '' _package_name = '' _is_compress = True _a_name = '' _logger = None _SKIPS = {'arch': ['sparc', 'hppa']} def __init__(self, os_name, package_name, a_name, flair='flair', log_level=logging.INFO, is_compress=True): self._flair = Flair(flair) self._archive = Launchpad() self._os_name = os_name self._package_name = package_name self._a_name = a_name self._is_compress = is_compress self._logger = logging.getLogger('Allirt') self._logger.setLevel(log_level) stream_handler = logging.StreamHandler() formatter = logging.Formatter('[%(levelname)s] %(message)s') stream_handler.setFormatter(formatter) self._logger.addHandler(stream_handler) def download_all(self, out_dir=''): return self.download(out_dir) def download(self, out_dir='', start=0, end=0): os_name = self._os_name package_name = self._package_name self._logger.info('OS : ' + os_name) self._logger.info('Package : ' + package_name) series_list = self._archive.get_os_series(os_name) if start or end: series_list = series_list[start:end] print() os_dir_name = os.path.join(out_dir, os_name) not os.path.exists(os_dir_name) and os.mkdir(os_dir_name) with TemporaryDirectory() as deb_tmp_path: for series_idx, series in enumerate(series_list): series_name, series_version = series print() series_dir_name = os.path.join( os_dir_name, '{} ({})'.format(series_version, series_name)) not os.path.exists(series_dir_name) and os.mkdir( series_dir_name) self._logger.info('OS Series ({}/{}) : {} ({})'.format( series_idx + 1, len(series_list), series_name, series_version)) archs = self._archive.get_os_architectures( os_name, series_name) for arch_idx, arch in enumerate(archs): print() self._logger.info('Architecture ({}/{}) : {}'.format( arch_idx + 1, len(archs), arch)) if arch in self._SKIPS['arch']: self._logger.warning('SKIPPED') continue arch_dir_name = os.path.join(series_dir_name, arch) not os.path.exists(arch_dir_name) and os.mkdir( arch_dir_name) package_versions = self._archive.get_pacakge_versions( os_name, series_name, arch, package_name) for package_version_idx, package_version in enumerate( package_versions): print() self._logger.info( 'Package Version ({}/{}) : {}'.format( package_version_idx + 1, len(package_versions), package_version)) self._logger.info('{} {} {} {} {} {}'.format( os_name, series_version, package_name, arch, package_version, datetime.now())) info = self._archive.download_package( os_name, series_name, arch, package_name, package_version, deb_tmp_path) size = info['size'] filename = info['filename'] if info['size']: self._logger.info( 'Download Completed : {} ({} bytes)'.format( info['url'], size)) sig_desc = '{} {} {} ({}/{})'.format( os_name, series_version, package_name.replace('-dev', ''), package_version, arch) try: sig_dir_name = arch_dir_name sig_name = '{}.sig'.format( os.path.splitext(filename)[0]) sig_name = os.path.join(sig_dir_name, sig_name) deb_path = os.path.join(deb_tmp_path, filename) info = self._flair.deb_to_sig( deb_path, self._a_name, sig_name, sig_desc, self._is_compress) self._logger.info('Target library : {}'.format( info['a'])) self._logger.info( 'Signature has been generated. -> {}'. format(info['sig'])) except FileExistsError as e: self._logger.warning( 'Signature already exists.') except (FlairError, FlairNotSupportedError) as e: self._logger.error(e) except Exception as e: self._logger.error(e) traceback.print_tb(e.__traceback__) finally: os.remove(deb_path) else: self._logger.warning('Package deleted') self._logger.info('Finished') return True
class LaunchPadHandler(): """ """ def __init__(self, offset=(0, 1), record_state=False, cyclic=False, colorscheme=0): """ Stores all of the required vars to send signals to the launchpad Offset arg refers to x, y offset Color scale runs from 0-14 (fewer options, but acts as a simple spectrum) methods include a runner, which accepts a function to act on the object """ self.lp = Launchpad() self.lp.Open() self.lp.LedCtrlXY(0, 0, 0, 3) self.lp.LedCtrlXY(0, 0, 0, 0) self.offset = offset cols = ([(0, 3), (1, 3), (1, 2), (2, 2), (2, 1), (3, 1), (3, 0)], [(3, 0), (3, 1), (2, 1), (2, 1), (2, 0), (1, 0), (0, 0)]) tmp = cols[colorscheme] if cyclic == True: self.colorscale = { x: y for x, y in enumerate(tmp[:-1] + tmp[::-1][:-1]) } else: self.colorscale = {x: y for x, y in enumerate(tmp)} self.lp.Reset() self.record_state = record_state self.lp.state = {x: {y: -1 for y in xrange(8)} for x in xrange(8)} def __del__(self): """ On garbage collection, reset lights """ self.lp.Reset() # raise SystemExit def _coords(self, x, y): return x + self.offset[0], y + self.offset[1] def _colors(self, c): """ Handles color scale. returns a color for all values except -1, which is equivalent to off (0, 0) """ if c == -1: return (0, 0) else: return self.colorscale[c % len(self.colorscale)] def send(self, x, y, c=0): """ Send co-ords to launchpad, records state if necessary """ x1, y1 = self._coords(x, y) c2, c1 = self._colors(c) self.lp.LedCtrlXY(x1, y1, c1, c2) if self.record_state: self[x][y] = [c] def recieve(self): state = self.lp.ButtonStateXY() if state: state[0] -= self.offset[0] state[1] -= self.offset[1] return state else: return None def _run_loop(self, func): while True: func(self) def run(self, func, loop=False): """ Takes function as argument - function should take handler as argument """ try: if loop: self._run_loop(func) else: func(self) except (KeyboardInterrupt, SystemExit): self.lp.Reset() raise SystemExit except: self.lp.Reset() def get_state(self, x=None, y=None): if x and y: return self.state[x][y] else: return self.state
class LaunchpadInput(Input): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.light_button_map = {} self.current_light = None self.lights = {} self.data_queue = {} self.recording = {'states': []} self.recording_ptr = -1 self.recording_to = self.config.get('RECORD') self.dim_speed = 0 if self.recording_to: if os.path.exists(self.recording_to): with open(self.recording_to, 'r') as fp: self.recording = json.load(fp) self.recording_ptr = len(self.recording['states']) - 1 if self.recording['states']: self.set_recording_state(-1) else: self.recording['states'].append({'lights': {}}) self.recording_ptr = 0 light_buttons = [] x = y = 0 for i in self.config.get('OUTPUTS', []): if not i['DEVICE'].endswith('Gobo'): # FIXME: this is hacky continue c = i.get('LABEL_COLOR') if c: if isinstance(c, str): c = Color(getattr(Colors, c), intensity=3) else: c = RGBColor(*c) else: c = Color(Colors.WHITE, intensity=4) light_buttons.append(Momentary(on_color=Color(Colors.WHITE, intensity=1), off_color=c, callback=self.select_light, buttons=ButtonGroup(x, y, x, y))) self.light_button_map[(x, y)] = i['NAME'] x += 1 if x > 7: x = 0 y += 1 record_buttons = [] if self.recording_to: record_buttons = [ Momentary(on_color=Color(Colors.WHITE), off_color=Color(Colors.GREEN), callback=self.record_prev, buttons=ButtonGroup('TOP', 'UP')), Momentary(on_color=Color(Colors.WHITE), off_color=Color(Colors.GREEN), callback=self.record_next, buttons=ButtonGroup('TOP', 'DOWN')), Momentary(on_color=Color(Colors.WHITE), off_color=Color(Colors.RED), callback=self.record_save, buttons=ButtonGroup('TOP', 'LEFT')), Momentary(on_color=Color(Colors.WHITE), off_color=Color(Colors.GREEN), callback=self.record_new_state, buttons=ButtonGroup('TOP', 'RIGHT')), ] reset_buttons = [ # Reset pan Momentary(on_color=Color(Colors.BLUE), off_color=Color(Colors.WHITE), callback=self.gobo_pan_reset, buttons=ButtonGroup('RIGHT', 'VOLUME')), # Reset tilt Momentary(on_color=Color(Colors.BLUE), off_color=Color(Colors.WHITE), callback=self.gobo_tilt_reset, buttons=ButtonGroup('RIGHT', 'PAN')), # Reset speed Momentary(on_color=Color(Colors.BLUE), off_color=Color(Colors.GREEN), callback=self.gobo_speed_reset, buttons=ButtonGroup('RIGHT', 'STOP')), # Reset dim Momentary(on_color=Color(Colors.BLUE), off_color=Color(Colors.YELLOW), callback=self.gobo_dim_reset, buttons=ButtonGroup('RIGHT', 'MUTE')), # Reset dim speed Momentary(on_color=Color(Colors.BLUE), off_color=Color(Colors.ORANGE), callback=self.gobo_dim_speed_reset, buttons=ButtonGroup('RIGHT', 'SOLO')), # Reset all Momentary(on_color=Color(Colors.BLUE), off_color=Color(Colors.RED), callback=self.gobo_all_reset, buttons=ButtonGroup('RIGHT', 'RECORDARM')), ] self.lp = Launchpad() fade = (0.1, 0.4, 0.6, 1) self.pages = { 'select_light': Page( include_top=True, include_right=True, controls=[ Momentary(on_color=Color(Colors.BLUE), off_color=Color(Colors.GREEN), callback=self._no_op, buttons=ButtonGroup('TOP', 'SESSION')), ] + light_buttons + reset_buttons + record_buttons ), 'control_gobo': Page( include_top=True, include_right=True, controls=[ # Back to light sel Momentary(on_color=Color(Colors.BLUE), off_color=Color(Colors.WHITE), callback=self.select_light, buttons=ButtonGroup('TOP', 'SESSION')), # Mode indicator Momentary(on_color=Color(Colors.BLUE), off_color=Color(Colors.GREEN), callback=self._no_op, buttons=ButtonGroup('TOP', 'MIXER')), # pan - decrease Momentary(on_color=Color(Colors.WHITE), off_color=[RGBColor(v, 0, 0) for v in reversed(fade)], callback=self.gobo_pan_decrease, buttons=ButtonGroup(0, 0, 3, 0)), # pan - increase Momentary(on_color=Color(Colors.WHITE), off_color=[RGBColor(0, v, 0) for v in fade], callback=self.gobo_pan_increase, buttons=ButtonGroup(4, 0, 7, 0)), # tilt - decrease Momentary(on_color=Color(Colors.WHITE), off_color=[RGBColor(v, 0, 0) for v in reversed(fade)], callback=self.gobo_tilt_decrease, buttons=ButtonGroup(0, 1, 3, 1)), # tilt - increase Momentary(on_color=Color(Colors.WHITE), off_color=[RGBColor(0, v, 0) for v in fade], callback=self.gobo_tilt_increase, buttons=ButtonGroup(4, 1, 7, 1)), # Colors # TODO: use real colors Momentary(on_color=Color(Colors.WHITE), off_color=[Color(getattr(Colors, v)) for v in ('WHITE', 'RED', 'YELLOW', 'GREEN', 'CYAN1', 'BLUE', 'PURPLE', 'PINK')], callback=self.gobo_color, buttons=ButtonGroup(0, 2, 7, 2)), # Gobo Momentary(on_color=Color(Colors.WHITE), off_color=Color(Colors.PURPLE), callback=self.gobo_gobo, buttons=ButtonGroup(0, 3, 7, 3)), # speed - decrease Momentary(on_color=Color(Colors.WHITE), off_color=[RGBColor(v, 0, 0) for v in reversed(fade)], callback=self.gobo_speed_decrease, buttons=ButtonGroup(0, 4, 3, 4)), # speed - increase Momentary(on_color=Color(Colors.WHITE), off_color=[RGBColor(0, v, 0) for v in fade], callback=self.gobo_speed_increase, buttons=ButtonGroup(4, 4, 7, 4)), # dim - decrease Momentary(on_color=Color(Colors.WHITE), off_color=[RGBColor(v, 0, 0) for v in reversed(fade)], callback=self.gobo_dim_decrease, buttons=ButtonGroup(0, 5, 3, 5)), # dim - increase Momentary(on_color=Color(Colors.WHITE), off_color=[RGBColor(0, v, 0) for v in fade], callback=self.gobo_dim_increase, buttons=ButtonGroup(4, 5, 7, 5)), # dim speed - decrease Momentary(on_color=Color(Colors.WHITE), off_color=[RGBColor(v, 0, 0) for v in reversed(fade)], callback=self.gobo_dim_speed_decrease, buttons=ButtonGroup(0, 6, 3, 6)), # dim speed - increase Momentary(on_color=Color(Colors.WHITE), off_color=[RGBColor(0, v, 0) for v in fade], callback=self.gobo_dim_speed_increase, buttons=ButtonGroup(4, 6, 7, 6)), # Strobe Momentary(on_color=Color(Colors.WHITE), off_color=[Color(Colors.RED), Color(Colors.GREEN)], callback=self.gobo_strobe, buttons=ButtonGroup(0, 7, 1, 7)), ] + reset_buttons + record_buttons ) } self.lp.push_page(self.pages['select_light']) def add_output(self, output): self.lights[output.name] = output def set_state(self, **params): all_ok = params.pop('_all', False) light = [] if self.current_light: light = self.lights.get(self.current_light) if light: light = [light] elif all_ok: # FIXME: this is also a hack light = [v for v in self.lights.values() if v.__class__.__name__.endswith('Gobo')] for l in light: state = dict(l.state) state.update(params) self.data_queue['gobo_state__' + l.name] = state def alter_state(self, **params): all_ok = params.pop('_all', False) light = [] if self.current_light: light = self.lights.get(self.current_light) if light: light = [light] elif all_ok: # FIXME: this is also a hack light = [v for v in self.lights.values() if v.__class__.__name__.endswith('Gobo')] for l in light: state = dict(l.state) for k, v in params.items(): state[k] = max(0, min(255, state[k] + v)) self.data_queue['gobo_state__' + l.name] = state def run(self, data): data.update(self.data_queue) self.data_queue = {} self.lp.poll() def stop(self): self.lp.reset() def _no_op(self, lp, type_, button, value): # print(type_, button, value) pass def select_light(self, lp, type_, button, value): if value: if button == 'SESSION': self.current_light = None self.lp.pop_page() else: self.current_light = self.light_button_map[button] self.lp.push_page(self.pages['control_gobo']) def gobo_pan_decrease(self, lp, type_, button, value): if value: v = 2 ** (3 - button[0]) self.alter_state(pan=-v) def gobo_pan_increase(self, lp, type_, button, value): if value: v = 2 ** (button[0] - 4) self.alter_state(pan=v) def gobo_tilt_decrease(self, lp, type_, button, value): if value: v = 2 ** (3 - button[0]) self.alter_state(tilt=-v) def gobo_tilt_increase(self, lp, type_, button, value): if value: v = 2 ** (button[0] - 4) self.alter_state(tilt=v) def gobo_color(self, lp, type_, button, value): if value: v = button[0] + 1 self.set_state(color=v) def gobo_gobo(self, lp, type_, button, value): if value: v = button[0] + 1 self.set_state(gobo=v) def gobo_speed_decrease(self, lp, type_, button, value): if value: v = 2 ** (3 - button[0]) self.alter_state(speed=-v) def gobo_speed_increase(self, lp, type_, button, value): if value: v = 2 ** (button[0] - 4) self.alter_state(speed=v) def gobo_dim_decrease(self, lp, type_, button, value): if value: v = 2 ** (3 - button[0]) self.alter_state(dim=-v) def gobo_dim_increase(self, lp, type_, button, value): if value: v = 2 ** (button[0] - 4) self.alter_state(dim=v) def gobo_dim_speed_decrease(self, lp, type_, button, value): if value: v = (2 ** (3 - button[0])) / 4 self.dim_speed = max(0, self.dim_speed - v) print("Dim speed:", self.dim_speed, "s") def gobo_dim_speed_increase(self, lp, type_, button, value): if value: v = (2 ** (button[0] - 4)) / 4 self.dim_speed += v print("Dim speed:", self.dim_speed, "s") def gobo_strobe(self, lp, type_, button, value): if value: v = button[0] self.set_state(strobe=v) def gobo_pan_reset(self, lp, type_, button, value): if value: v = 0 self.set_state(pan=v, _all=True) def gobo_tilt_reset(self, lp, type_, button, value): if value: v = 0 self.set_state(tilt=v, _all=True) def gobo_speed_reset(self, lp, type_, button, value): if value: v = 255 self.set_state(speed=v, _all=True) def gobo_dim_reset(self, lp, type_, button, value): if value: v = 255 self.set_state(dim=v, _all=True) def gobo_dim_speed_reset(self, lp, type_, button, value): if value: v = 0 self.dim_speed = v print("Dim speed:", v, "s") def gobo_all_reset(self, lp, type_, button, value): if value: self.set_state(pan=0, tilt=0, color=0, gobo=0, speed=255, dim=255, strobe=0, _all=True) def set_recording_state(self, ptr=None): if ptr is None: ptr = self.recording_ptr if ptr < 0: return for name, data in self.recording['states'][ptr]['lights'].items(): self.current_light = name self.set_state(**data) self.current_light = None def record_prev(self, lp, type_, button, value): if value: if self.recording_ptr > 0: self.recording_ptr -= 1 print("Switch state:", self.recording_ptr) self.set_recording_state() def record_next(self, lp, type_, button, value): if value: if self.recording_ptr < len(self.recording['states']) - 1: self.recording_ptr += 1 print("Switch state:", self.recording_ptr) self.set_recording_state() def record_save(self, lp, type_, button, value): if value: # FIXME: hack lights = [v for v in self.lights.values() if v.__class__.__name__.endswith('Gobo')] if self.recording_ptr < 0: self.record_new_state(lp, type_, button, value) print("Save state:", self.recording_ptr) self.recording['states'][self.recording_ptr]['dim_speed'] = self.dim_speed for l in lights: self.recording['states'][self.recording_ptr]['lights'][l.name] = l.state with open(self.recording_to, 'w') as fp: json.dump(self.recording, fp, indent=2) def record_new_state(self, lp, type_, button, value): if value: self.recording['states'].insert(self.recording_ptr + 1, {'lights': {}}) self.recording_ptr += 1 print("New state:", self.recording_ptr)
def __init__(self): super(MainWidget, self).__init__() self.audio = Audio(2)# two channel self.mixer = Mixer() self.audio.set_generator(self.mixer) # Initialize and create all the wave buffers from the regions self.wave_buffers = make_wave_buffers("data/unforgettable_regions.txt","data/unforgettable_beat.wav") keys = ["shelter","workit","harder","doit","stronger","drop","drop2","drop3","drop4","drop5","getdown","getdownbuild","getup","overtime","again","build","high A","high G","Ab","Gb","Db","Eb","entire", "oo", "blurp", "goochi", "lulpump", "yeuh", "gchgang"] self.songs = {} for key in keys: if os.path.exists("data/" + key + ".wav") is True: self.songs[key] = WaveFile("data/" + key + ".wav") # Initialize and create all the wave buffers from the regions self.wave_buffers2 = make_wave_buffers("data/gucci_gang_regions.txt","data/gucci_gang.wav") self.songs["high A"] = self.wave_buffers["high A"] self.songs["high G"] = self.wave_buffers["high G"] self.songs["Ab"] = self.wave_buffers["Ab"] self.songs["Gb"] = self.wave_buffers["Gb"] self.songs["Db"] = self.wave_buffers["Db"] self.songs["Eb"] = self.wave_buffers["Eb"] self.songs["entire"] = self.wave_buffers["entire"] self.songs["oo"] = self.wave_buffers2["oo"] self.songs["blurp"] = self.wave_buffers2["blurp"] self.songs["goochi"] = self.wave_buffers2["goochi"] self.songs["lulpump"] = self.wave_buffers2["lulpump"] self.songs["yeuh"] = self.wave_buffers2["yeuh"] self.songs["gchgang"] = self.wave_buffers2["gchgang"] self.shelter_gen = WaveGenerator(self.songs['shelter'],True) self.workit_gen = WaveGenerator(self.songs['workit']) self.doit_gen = WaveGenerator(self.songs['doit']) self.harder_gen = WaveGenerator(self.songs['harder']) self.stronger_gen = WaveGenerator(self.songs['stronger']) self.drop_gen = WaveGenerator(self.songs['drop'],True) self.drop_gen2 = WaveGenerator(self.songs['drop2'],True) self.drop_gen3 = WaveGenerator(self.songs['drop3'],True) self.drop_gen4 = WaveGenerator(self.songs['drop4'],True) self.drop_gen5 = WaveGenerator(self.songs['drop5'],True) self.getdown_gen = WaveGenerator(self.songs['getdown']) self.getdownbuild_gen = WaveGenerator(self.songs['getdownbuild'],True) self.getup_gen = WaveGenerator(self.songs['getup']) self.overtime_gen = WaveGenerator(self.songs['overtime']) self.again_gen = WaveGenerator(self.songs['again']) self.build_gen = WaveGenerator(self.songs['build'],True) # Create all of the generators for the song # sections allow for that self.high_a_gen = WaveGenerator(self.wave_buffers["high A"]) self.high_g_gen = WaveGenerator(self.wave_buffers["high G"]) self.ab_gen = WaveGenerator(self.wave_buffers["Ab"],True) self.gb_gen = WaveGenerator(self.wave_buffers["Gb"],True) self.db_gen = WaveGenerator(self.wave_buffers["Db"],True) self.eb_gen = WaveGenerator(self.wave_buffers["Eb"],True) self.entire_gen = WaveGenerator(self.wave_buffers["entire"],True) self.oo_gen = WaveGenerator(self.wave_buffers2["oo"]) self.blurp_gen = WaveGenerator(self.wave_buffers2["blurp"]) self.gooch_gen = WaveGenerator(self.wave_buffers2["goochi"]) self.lulpump_gen = WaveGenerator(self.wave_buffers2["lulpump"]) self.yeh_gen = WaveGenerator(self.wave_buffers2["yeuh"]) self.gucci_gang_gen = WaveGenerator(self.wave_buffers2["gchgang"], True) self.gens = [] self.gens.append(self.shelter_gen) self.gens.append(self.workit_gen) self.gens.append(self.doit_gen) self.gens.append(self.harder_gen) self.gens.append(self.stronger_gen) self.gens.append(self.drop_gen) self.gens.append(self.drop_gen2) self.gens.append(self.drop_gen3) self.gens.append(self.drop_gen4) self.gens.append(self.drop_gen5) self.gens.append(self.getdown_gen) self.gens.append(self.getdownbuild_gen) self.gens.append(self.getup_gen) self.gens.append(self.overtime_gen) self.gens.append(self.again_gen) self.gens.append(self.build_gen) self.gens.append(self.high_a_gen) self.gens.append(self.high_g_gen) self.gens.append(self.ab_gen) self.gens.append(self.gb_gen) self.gens.append(self.db_gen) self.gens.append(self.eb_gen) self.gens.append(self.entire_gen) self.gens.append(self.oo_gen) self.gens.append(self.blurp_gen) self.gens.append(self.gooch_gen) self.gens.append(self.lulpump_gen) self.gens.append(self.yeh_gen) self.gens.append(self.gucci_gang_gen) self.launchpad = {} for i in range(len(self.gens)): print(keys[i]) self.launchpad[keys[i]] = self.gens[i] self.mixer.add(self.gens[i]) self.gens[i].pause() self.L = Launchpad(self) # print('HI THERE\n HI THERE') self.L.calibration_mode()
class MainWidget(BaseWidget) : def __init__(self): super(MainWidget, self).__init__() self.audio = Audio(2)# two channel self.mixer = Mixer() self.audio.set_generator(self.mixer) # Initialize and create all the wave buffers from the regions self.wave_buffers = make_wave_buffers("data/unforgettable_regions.txt","data/unforgettable_beat.wav") keys = ["shelter","workit","harder","doit","stronger","drop","drop2","drop3","drop4","drop5","getdown","getdownbuild","getup","overtime","again","build","high A","high G","Ab","Gb","Db","Eb","entire", "oo", "blurp", "goochi", "lulpump", "yeuh", "gchgang"] self.songs = {} for key in keys: if os.path.exists("data/" + key + ".wav") is True: self.songs[key] = WaveFile("data/" + key + ".wav") # Initialize and create all the wave buffers from the regions self.wave_buffers2 = make_wave_buffers("data/gucci_gang_regions.txt","data/gucci_gang.wav") self.songs["high A"] = self.wave_buffers["high A"] self.songs["high G"] = self.wave_buffers["high G"] self.songs["Ab"] = self.wave_buffers["Ab"] self.songs["Gb"] = self.wave_buffers["Gb"] self.songs["Db"] = self.wave_buffers["Db"] self.songs["Eb"] = self.wave_buffers["Eb"] self.songs["entire"] = self.wave_buffers["entire"] self.songs["oo"] = self.wave_buffers2["oo"] self.songs["blurp"] = self.wave_buffers2["blurp"] self.songs["goochi"] = self.wave_buffers2["goochi"] self.songs["lulpump"] = self.wave_buffers2["lulpump"] self.songs["yeuh"] = self.wave_buffers2["yeuh"] self.songs["gchgang"] = self.wave_buffers2["gchgang"] self.shelter_gen = WaveGenerator(self.songs['shelter'],True) self.workit_gen = WaveGenerator(self.songs['workit']) self.doit_gen = WaveGenerator(self.songs['doit']) self.harder_gen = WaveGenerator(self.songs['harder']) self.stronger_gen = WaveGenerator(self.songs['stronger']) self.drop_gen = WaveGenerator(self.songs['drop'],True) self.drop_gen2 = WaveGenerator(self.songs['drop2'],True) self.drop_gen3 = WaveGenerator(self.songs['drop3'],True) self.drop_gen4 = WaveGenerator(self.songs['drop4'],True) self.drop_gen5 = WaveGenerator(self.songs['drop5'],True) self.getdown_gen = WaveGenerator(self.songs['getdown']) self.getdownbuild_gen = WaveGenerator(self.songs['getdownbuild'],True) self.getup_gen = WaveGenerator(self.songs['getup']) self.overtime_gen = WaveGenerator(self.songs['overtime']) self.again_gen = WaveGenerator(self.songs['again']) self.build_gen = WaveGenerator(self.songs['build'],True) # Create all of the generators for the song # sections allow for that self.high_a_gen = WaveGenerator(self.wave_buffers["high A"]) self.high_g_gen = WaveGenerator(self.wave_buffers["high G"]) self.ab_gen = WaveGenerator(self.wave_buffers["Ab"],True) self.gb_gen = WaveGenerator(self.wave_buffers["Gb"],True) self.db_gen = WaveGenerator(self.wave_buffers["Db"],True) self.eb_gen = WaveGenerator(self.wave_buffers["Eb"],True) self.entire_gen = WaveGenerator(self.wave_buffers["entire"],True) self.oo_gen = WaveGenerator(self.wave_buffers2["oo"]) self.blurp_gen = WaveGenerator(self.wave_buffers2["blurp"]) self.gooch_gen = WaveGenerator(self.wave_buffers2["goochi"]) self.lulpump_gen = WaveGenerator(self.wave_buffers2["lulpump"]) self.yeh_gen = WaveGenerator(self.wave_buffers2["yeuh"]) self.gucci_gang_gen = WaveGenerator(self.wave_buffers2["gchgang"], True) self.gens = [] self.gens.append(self.shelter_gen) self.gens.append(self.workit_gen) self.gens.append(self.doit_gen) self.gens.append(self.harder_gen) self.gens.append(self.stronger_gen) self.gens.append(self.drop_gen) self.gens.append(self.drop_gen2) self.gens.append(self.drop_gen3) self.gens.append(self.drop_gen4) self.gens.append(self.drop_gen5) self.gens.append(self.getdown_gen) self.gens.append(self.getdownbuild_gen) self.gens.append(self.getup_gen) self.gens.append(self.overtime_gen) self.gens.append(self.again_gen) self.gens.append(self.build_gen) self.gens.append(self.high_a_gen) self.gens.append(self.high_g_gen) self.gens.append(self.ab_gen) self.gens.append(self.gb_gen) self.gens.append(self.db_gen) self.gens.append(self.eb_gen) self.gens.append(self.entire_gen) self.gens.append(self.oo_gen) self.gens.append(self.blurp_gen) self.gens.append(self.gooch_gen) self.gens.append(self.lulpump_gen) self.gens.append(self.yeh_gen) self.gens.append(self.gucci_gang_gen) self.launchpad = {} for i in range(len(self.gens)): print(keys[i]) self.launchpad[keys[i]] = self.gens[i] self.mixer.add(self.gens[i]) self.gens[i].pause() self.L = Launchpad(self) # print('HI THERE\n HI THERE') self.L.calibration_mode() def reset(self): for i in range(len(self.gens)): self.gens[i].reset() # in the case that this a note that hasn't played yet def reset_audio(self,key): #make a new instance self.launchpad[key] = WaveGenerator(self.songs[key]) def play_audio(self, key): #print("PLAY THIS AUDIO " , key, " and exists: ",key in self.launchpad.keys()) if(key in self.launchpad.keys()): if(self.launchpad[key].loop): self.launchpad[key].play_toggle() else: self.reset_audio(key) self.launchpad[key].play() self.mixer.add(self.launchpad[key]) def is_loop(self,key): if(key in self.launchpad.keys()): if(self.launchpad[key].loop): return True return False # Each string should be a different line of the file (leaving in newline characters are fine for now) def lines_from_file(filename): lines = open(filename) line = lines.readlines() return line # Return its tab-delimited entries as a list of strings (keep out new lines and tabs). def tokens_from_line(line): new_line = line.strip() new_line = new_line.split("\t") return new_line # Return a dictionary of WaveBuffers based on the regions we define def make_wave_buffers(regions_path, wave_path): buffers = {} lines = lines_from_file(regions_path) # Go through each line and extract the information needed to create each WaveBuffer for line in lines: new_list = tokens_from_line(line) starting_frame = int(Audio.sample_rate * float(new_list[0])) duration = int(Audio.sample_rate * float(new_list[2])) buffers[new_list[3]] = WaveBuffer(wave_path,starting_frame, duration) return buffers def on_update(self) : self.audio.on_update() # print(self.L.is_calibrating) if self.L.is_calibrating: pass else: self.L.main() def on_key_down(self,keycode,modifier): if(keycode[1] == 'c'): self.L.calibration_mode() elif(keycode[1] == 'q'): raise SystemExit
#!/usr/bin/env python from launchpad import Launchpad from datetime import datetime from time import sleep import sys from pytz import timezone import signal try: lp = Launchpad() except IOError: print "no Launchpad, no demo, dude." sys.exit(1) def sigint_handler(signal, frame): lp.reset() sys.exit(0) signal.signal(signal.SIGINT, sigint_handler) london = timezone('Europe/London') DIGITS = [ [ " ** ", "* *", "* *", " ** ", ], [
ACTION_TYPE = { "0": "KEYIN", "1": "EXEC", "2": "NOTE", "3": "WAVE", "9": "SPECIAL" } settings = configparser.ConfigParser() settings.read("settings.ini") midiInPort = settings['midi']['midiInPort'] midiOutPort = settings['midi']['midiOutPort'] lp = Launchpad() if DEBUG is True: il, ol = lp.getDeviceList() print("now available input is {}".format(il)) print("now available output is {}".format(ol)) print("inport is {}, outport is {}".format(midiInPort, midiOutPort)) result = lp.connect(midiInPort, midiOutPort) if result is True: doloop = True while doloop: try: msg = lp.getMsg() if msg:
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.light_button_map = {} self.current_light = None self.lights = {} self.data_queue = {} self.recording = {'states': []} self.recording_ptr = -1 self.recording_to = self.config.get('RECORD') self.dim_speed = 0 if self.recording_to: if os.path.exists(self.recording_to): with open(self.recording_to, 'r') as fp: self.recording = json.load(fp) self.recording_ptr = len(self.recording['states']) - 1 if self.recording['states']: self.set_recording_state(-1) else: self.recording['states'].append({'lights': {}}) self.recording_ptr = 0 light_buttons = [] x = y = 0 for i in self.config.get('OUTPUTS', []): if not i['DEVICE'].endswith('Gobo'): # FIXME: this is hacky continue c = i.get('LABEL_COLOR') if c: if isinstance(c, str): c = Color(getattr(Colors, c), intensity=3) else: c = RGBColor(*c) else: c = Color(Colors.WHITE, intensity=4) light_buttons.append(Momentary(on_color=Color(Colors.WHITE, intensity=1), off_color=c, callback=self.select_light, buttons=ButtonGroup(x, y, x, y))) self.light_button_map[(x, y)] = i['NAME'] x += 1 if x > 7: x = 0 y += 1 record_buttons = [] if self.recording_to: record_buttons = [ Momentary(on_color=Color(Colors.WHITE), off_color=Color(Colors.GREEN), callback=self.record_prev, buttons=ButtonGroup('TOP', 'UP')), Momentary(on_color=Color(Colors.WHITE), off_color=Color(Colors.GREEN), callback=self.record_next, buttons=ButtonGroup('TOP', 'DOWN')), Momentary(on_color=Color(Colors.WHITE), off_color=Color(Colors.RED), callback=self.record_save, buttons=ButtonGroup('TOP', 'LEFT')), Momentary(on_color=Color(Colors.WHITE), off_color=Color(Colors.GREEN), callback=self.record_new_state, buttons=ButtonGroup('TOP', 'RIGHT')), ] reset_buttons = [ # Reset pan Momentary(on_color=Color(Colors.BLUE), off_color=Color(Colors.WHITE), callback=self.gobo_pan_reset, buttons=ButtonGroup('RIGHT', 'VOLUME')), # Reset tilt Momentary(on_color=Color(Colors.BLUE), off_color=Color(Colors.WHITE), callback=self.gobo_tilt_reset, buttons=ButtonGroup('RIGHT', 'PAN')), # Reset speed Momentary(on_color=Color(Colors.BLUE), off_color=Color(Colors.GREEN), callback=self.gobo_speed_reset, buttons=ButtonGroup('RIGHT', 'STOP')), # Reset dim Momentary(on_color=Color(Colors.BLUE), off_color=Color(Colors.YELLOW), callback=self.gobo_dim_reset, buttons=ButtonGroup('RIGHT', 'MUTE')), # Reset dim speed Momentary(on_color=Color(Colors.BLUE), off_color=Color(Colors.ORANGE), callback=self.gobo_dim_speed_reset, buttons=ButtonGroup('RIGHT', 'SOLO')), # Reset all Momentary(on_color=Color(Colors.BLUE), off_color=Color(Colors.RED), callback=self.gobo_all_reset, buttons=ButtonGroup('RIGHT', 'RECORDARM')), ] self.lp = Launchpad() fade = (0.1, 0.4, 0.6, 1) self.pages = { 'select_light': Page( include_top=True, include_right=True, controls=[ Momentary(on_color=Color(Colors.BLUE), off_color=Color(Colors.GREEN), callback=self._no_op, buttons=ButtonGroup('TOP', 'SESSION')), ] + light_buttons + reset_buttons + record_buttons ), 'control_gobo': Page( include_top=True, include_right=True, controls=[ # Back to light sel Momentary(on_color=Color(Colors.BLUE), off_color=Color(Colors.WHITE), callback=self.select_light, buttons=ButtonGroup('TOP', 'SESSION')), # Mode indicator Momentary(on_color=Color(Colors.BLUE), off_color=Color(Colors.GREEN), callback=self._no_op, buttons=ButtonGroup('TOP', 'MIXER')), # pan - decrease Momentary(on_color=Color(Colors.WHITE), off_color=[RGBColor(v, 0, 0) for v in reversed(fade)], callback=self.gobo_pan_decrease, buttons=ButtonGroup(0, 0, 3, 0)), # pan - increase Momentary(on_color=Color(Colors.WHITE), off_color=[RGBColor(0, v, 0) for v in fade], callback=self.gobo_pan_increase, buttons=ButtonGroup(4, 0, 7, 0)), # tilt - decrease Momentary(on_color=Color(Colors.WHITE), off_color=[RGBColor(v, 0, 0) for v in reversed(fade)], callback=self.gobo_tilt_decrease, buttons=ButtonGroup(0, 1, 3, 1)), # tilt - increase Momentary(on_color=Color(Colors.WHITE), off_color=[RGBColor(0, v, 0) for v in fade], callback=self.gobo_tilt_increase, buttons=ButtonGroup(4, 1, 7, 1)), # Colors # TODO: use real colors Momentary(on_color=Color(Colors.WHITE), off_color=[Color(getattr(Colors, v)) for v in ('WHITE', 'RED', 'YELLOW', 'GREEN', 'CYAN1', 'BLUE', 'PURPLE', 'PINK')], callback=self.gobo_color, buttons=ButtonGroup(0, 2, 7, 2)), # Gobo Momentary(on_color=Color(Colors.WHITE), off_color=Color(Colors.PURPLE), callback=self.gobo_gobo, buttons=ButtonGroup(0, 3, 7, 3)), # speed - decrease Momentary(on_color=Color(Colors.WHITE), off_color=[RGBColor(v, 0, 0) for v in reversed(fade)], callback=self.gobo_speed_decrease, buttons=ButtonGroup(0, 4, 3, 4)), # speed - increase Momentary(on_color=Color(Colors.WHITE), off_color=[RGBColor(0, v, 0) for v in fade], callback=self.gobo_speed_increase, buttons=ButtonGroup(4, 4, 7, 4)), # dim - decrease Momentary(on_color=Color(Colors.WHITE), off_color=[RGBColor(v, 0, 0) for v in reversed(fade)], callback=self.gobo_dim_decrease, buttons=ButtonGroup(0, 5, 3, 5)), # dim - increase Momentary(on_color=Color(Colors.WHITE), off_color=[RGBColor(0, v, 0) for v in fade], callback=self.gobo_dim_increase, buttons=ButtonGroup(4, 5, 7, 5)), # dim speed - decrease Momentary(on_color=Color(Colors.WHITE), off_color=[RGBColor(v, 0, 0) for v in reversed(fade)], callback=self.gobo_dim_speed_decrease, buttons=ButtonGroup(0, 6, 3, 6)), # dim speed - increase Momentary(on_color=Color(Colors.WHITE), off_color=[RGBColor(0, v, 0) for v in fade], callback=self.gobo_dim_speed_increase, buttons=ButtonGroup(4, 6, 7, 6)), # Strobe Momentary(on_color=Color(Colors.WHITE), off_color=[Color(Colors.RED), Color(Colors.GREEN)], callback=self.gobo_strobe, buttons=ButtonGroup(0, 7, 1, 7)), ] + reset_buttons + record_buttons ) } self.lp.push_page(self.pages['select_light'])
class HomePad(object): def __init__(self): self.pad = Launchpad() #None indicates home screen self.active_column = None self.columns = [] self.position_x = 0 self.position_y = 0 self.input_context = None sequencer.add(self) def add_column(self, column): self.columns.append(column) def _send_column_event(self, event): column_num = event.get_column() + self.position_x row_num = event.get_row() + self.position_y if column_num >= len(self.columns): return column = self.columns[column_num] if event.is_a_push() and hasattr(column, "onColumnPush"): column.onColumnPush(row_num) elif event.is_a_release() and hasattr(column, "onColumnRelease"): column.onColumnRelease(row_num) def _send_focused_event(self, event): column = self.columns[self.active_column] if event.is_a_push() and hasattr(column, "onFocusedPush"): column.onFocusedPush(event) elif event.is_a_release() and hasattr(column, "onFocusedRelease"): column.onFocusedRelease(event) def _update_lights(self): if self.active_column is None: lights = [] for column in self.columns[self.position_x:]: lights.extend(column.column_lights) else: lights = self.columns[self.active_column].focused_lights #fill the leftover spots for i in range(len(lights), 64): lights.append("off") self.pad.light_up(lights) def plan(self, span): for column in self.columns: if hasattr(column, "plan"): column.plan(span) self._update_lights() def _handle_focus(self, event): """pushing button 0,8 (the topmost row launch button) causes the homepad to toggle 'focusing.' In order to go into a focused state, the user must push the button to focus while holding down the focus toggle.""" if event.get_row() is 0 and event.get_column() is 8: if event.is_a_push(): if self.active_column is None: print "input context is now focus" self.input_context = "focus" else: self.active_column = None elif event.is_a_release(): print "no input context" self.input_context = None #check to see if a button is being pressed while focus is being held down elif self.input_context is "focus": self.active_column = event.get_column() return True return False def read_input(self): for event in self.pad.read_input(): event_handled = self._handle_focus(event) if not event_handled: if self.active_column == None: self._send_column_event(event) else: self._send_focused_event(event)
class Sequencer(object): def __init__(self): self.audio = PyAudio() self.page = 0 self.samples = [None] * 8 self.sampleI = 0 self.playing = False self.playingPage = 0 self.playingCol = 0 self.pages = [[[0] * 8 for i in xrange(8)] for i in xrange(8)] self.padPlayMode = False self.recordMode = False self.recording = None self.showBPM = False self.bpm = 240 self.bpmPressed = False self.bpmTimePressed = None self.dev = Launchpad() self.dev.handler = self.handler self.setLight = self.dev.setLight self.repaint() bpmState = True while True: if self.showBPM: since = clock() - self.bpmTimePressed if since >= 2 and not self.bpmPressed: self.showBPM = False self.setLight(4, 0, False, self.page == 4) else: per = 0.5 / self.bpm * 60 since %= per * 2 if since < per: if bpmState == True: self.setLight(4, 0, True, False) bpmState = False sleep(per / 2.0) elif bpmState == False: self.setLight(4, 0, False, self.page == 4) bpmState = True sleep(per / 2.0) else: sleep(0.5) def handler(self, dev, x, y, evt): if y == 0: if (evt and x not in (4, 5)) or (self.recordMode and x != 6): return if x == 2: # left page self.page = (self.page + 8 - 1) % 8 self.repaint() elif x == 3: # right page self.page = (self.page + 1) % 8 self.repaint() elif x == 4 or x == 5: # decrease/increase bpm if evt: self.bpmPressed = True self.showBPM = True self.bpmTimePressed = clock() self.modBPM = -1 if x == 4 else +1 else: self.bpmPressed = False self.bpmTimePressed = clock() self.modBPM = None elif x == 6: # toggle record mode if self.recordMode == 2: self.stopRecording() self.recordMode = False else: self.recordMode = 1 if self.recordMode == False else False self.setLight(6, 0, self.recordMode == 1, False) elif x == 7: # toggle pad play mode self.padPlayMode = not self.padPlayMode self.setLight(7, 0, False, self.padPlayMode) elif x == 8: pass else: if evt: return if self.padPlayMode: # just play the sample for this row pass elif self.recordMode == 1: # recording for that row self.recordMode = 2 self.record(y-1) self.setLight(6, 0, True, True) for i in xrange(8): self.setLight(i, y, True, self.pages[self.page][i][y-1]) elif self.recordMode == 2: # end recording if y - 1 != self.recording: return self.stopRecording() else: page = self.pages[self.page] page[x][y-1] = not page[x][y-1] self.setLight(x, y, False, page[x][y-1]) def record(self, row): self.recording = row self.recordingSample = '' self.input = self.audio.open(format=paInt16, channels=1, rate=44100, input=True, frames_per_buffer=441, stream_callback=self.gotSample) # 1/100th second per buffer def gotSample(self, data, frame_count, time_info, status): pass#self.recordingSample += data print 'foo?' def stopRecording(self): print len(self.recordingSample) / 2 / 44100.0 self.input.stop_stream() self.input.close() self.setLight(6, 0, False, False) for i in xrange(8): self.setLight(i, self.recording+1, False, self.pages[self.page][i][self.recording]) self.recordMode = False self.recording = None def setStatusLights(self): for i in xrange(8): self.setLight(i, 0, False, False) self.setLight(8, i+1, False, False) self.setLight(self.page, 0, self.playing and self.playingPage == self.page and self.playingCol == self.page, True) if self.playingPage == self.page and self.playingCol != self.page: self.setLight(self.playingCol, 0, True, False) if self.padPlayMode: self.setLight(7, 0, False, True) def repaint(self): self.dev.clear() self.setStatusLights() for x, row in enumerate(self.pages[self.page]): for y, val in enumerate(row): if val or x == self.recording: self.setLight(x, y+1, self.recording == x, True)