def draw_cell(self, step, row): libseq.isPatternModified() # Avoid refresh redrawing whole grid cellIndex = row * libseq.getSteps( ) + step # Cells are stored in array sequentially: 1st row, 2nd row... if cellIndex >= len(self.cells): return note = self.keymap[row + self.keymap_offset]["note"] velocity_colour = libseq.getNoteVelocity(step, note) if velocity_colour: velocity_colour = 70 + velocity_colour elif libseq.getScale() == 1: # Draw tramlines for white notes in chromatic scale key = note % 12 if key in (0, 2, 4, 5, 7, 9, 11): # White notes # if key in (1,3,6,8,10): # Black notes velocity_colour += 30 else: # Draw tramlines for odd rows in other scales and maps if (row + self.keymap_offset) % 2: velocity_colour += 30 duration = libseq.getNoteDuration(step, note) if not duration: duration = 1 fill_colour = "#%02x%02x%02x" % (velocity_colour, velocity_colour, velocity_colour) cell = self.cells[cellIndex] coord = self.get_cell(step, row, duration) if cell: # Update existing cell self.grid_canvas.itemconfig(cell, fill=fill_colour) self.grid_canvas.coords(cell, coord) else: # Create new cell cell = self.grid_canvas.create_rectangle( coord, fill=fill_colour, width=0, tags=("%d,%d" % (step, row), "gridcell", "step%d" % step)) self.grid_canvas.tag_bind(cell, '<ButtonPress-1>', self.on_grid_press) self.grid_canvas.tag_bind(cell, '<ButtonRelease-1>', self.on_grid_release) self.grid_canvas.tag_bind(cell, '<B1-Motion>', self.on_grid_drag) self.cells[cellIndex] = cell if step + duration > libseq.getSteps(): self.grid_canvas.itemconfig("lastnotetext%d" % row, text="+%d" % (duration - libseq.getSteps() + step), state="normal")
def populate_menu(self): self.parent.add_menu({ 'Beats in pattern': { 'method': self.parent.show_param_editor, 'params': { 'min': 1, 'max': 16, 'get_value': libseq.getBeatsInPattern, 'on_change': self.on_menu_change, 'on_assert': self.assert_beats_in_pattern } } }) self.parent.add_menu({ 'Steps per beat': { 'method': self.parent.show_param_editor, 'params': { 'min': 0, 'max': len(STEPS_PER_BEAT) - 1, 'get_value': self.get_steps_per_beat_index, 'on_change': self.on_menu_change, 'on_assert': self.assert_steps_per_beat } } }) self.parent.add_menu({'-------------------': {}}) self.parent.add_menu({ 'Copy pattern': { 'method': self.parent.show_param_editor, 'params': { 'min': 1, 'max': 64872, 'get_value': self.get_pattern, 'on_change': self.on_menu_change, 'on_assert': self.copy_pattern, 'on_cancel': self.cancel_copy } } }) self.parent.add_menu({'Clear pattern': {'method': self.clear_pattern}}) if libseq.getScale(): self.parent.add_menu({ 'Transpose pattern': { 'method': self.parent.show_param_editor, 'params': { 'min': -1, 'max': 1, 'value': 0, 'on_change': self.on_menu_change } } }) self.parent.add_menu({'-------------------': {}}) self.parent.add_menu({ 'Vertical zoom': { 'method': self.parent.show_param_editor, 'params': { 'min': 1, 'max': 127, 'get_value': self.get_vertical_zoom, 'on_change': self.on_menu_change, 'on_assert': self.assert_zoom } } }) self.parent.add_menu({ 'Scale': { 'method': self.parent.show_param_editor, 'params': { 'min': 0, 'max': self.get_scales(), 'get_value': libseq.getScale, 'on_change': self.on_menu_change } } }) self.parent.add_menu({ 'Tonic': { 'method': self.parent.show_param_editor, 'params': { 'min': -1, 'max': 12, 'get_value': libseq.getTonic, 'on_change': self.on_menu_change } } }) self.parent.add_menu({ 'Rest note': { 'method': self.parent.show_param_editor, 'params': { 'min': -1, 'max': 128, 'get_value': libseq.getInputRest, 'on_change': self.on_menu_change } } }) self.parent.add_menu({ 'Input channel': { 'method': self.parent.show_param_editor, 'params': { 'min': 0, 'max': 16, 'get_value': self.get_input_channel, 'on_change': self.on_menu_change } } }) self.parent.add_menu({'Export to SMF': {'method': self.export_smf}})
def load_keymap(self): try: base_note = int(self.keymap[self.keymap_offset]['note']) except: base_note = 60 scale = libseq.getScale() tonic = libseq.getTonic() name = None self.keymap = [] if scale == 0: # Map path = None for layer in self.zyngui.screens['layer'].layers: if layer.midi_chan == self.channel: path = layer.get_presetpath() break if path: path = path.split('#')[1] try: with open(CONFIG_ROOT + "/keymaps.json") as json_file: data = json.load(json_file) if path in data: name = data[path] xml = minidom.parse(CONFIG_ROOT + "/%s.midnam" % (name)) notes = xml.getElementsByTagName('Note') self.scale = [] for note in notes: self.keymap.append({ 'note': int(note.attributes['Number'].value), 'name': note.attributes['Name'].value }) except: logging.warning("Unable to load keymaps.json") if name == None: # Not found map # Scale if scale > 0: scale = scale - 1 libseq.setScale(scale + 1) # Use chromatic scale if map not found if scale == 0: for note in range(0, 128): new_entry = {"note": note} key = note % 12 if key in (1, 3, 6, 8, 10): # Black notes new_entry.update({"colour": "black"}) if key == 0: # 'C' new_entry.update({"name": "C%d" % (note // 12 - 1)}) self.keymap.append(new_entry) if note <= base_note: self.keymap_offset = len(self.keymap) - 1 self.selected_cell[1] = self.keymap_offset name = "Chromatic" else: with open(CONFIG_ROOT + "/scales.json") as json_file: data = json.load(json_file) if len(data) <= scale: scale = 0 for octave in range(0, 9): for offset in data[scale]['scale']: note = tonic + offset + octave * 12 if note > 127: break self.keymap.append({ "note": note, "name": "%s%d" % (self.notes[note % 12], note // 12 - 1) }) if note <= base_note: self.keymap_offset = len(self.keymap) - 1 self.selected_cell[1] = self.keymap_offset name = data[scale]['name'] return name
def on_menu_change(self, params): menu_item = self.parent.param_editor_item value = params['value'] if value < params['min']: value = params['min'] if value > params['max']: value = params['max'] params['value'] = value if menu_item == 'Pattern': self.pattern = value self.copy_source = value self.load_pattern(value) elif menu_item == 'Copy pattern': self.load_pattern(value) return "Copy %d => %d ?" % (self.copy_source, value) elif menu_item == 'Transpose pattern': if libseq.getScale() == 0: self.parent.hide_param_editor() return if libseq.getScale() > 1: # Only allow transpose when showing chromatic scale libseq.setScale(1) self.load_keymap() self.redraw_pending = 1 if (value != 0 and libseq.getScale()): libseq.transpose(value) self.parent.set_param(menu_item, 'value', 0) self.keymap_offset = self.keymap_offset + value if self.keymap_offset > 128 - self.zoom: self.keymap_offset = 128 - self.zoom elif self.keymap_offset < 0: self.keymap_offset = 0 else: self.selected_cell[1] = self.selected_cell[1] + value self.redraw_pending = 1 self.select_cell() return "Transpose +/-" elif menu_item == 'Vertical zoom': self.zoom = value elif menu_item == 'Steps per beat': steps_per_beat = STEPS_PER_BEAT[value] # libseq.setStepsPerBeat(steps_per_beat) self.redraw_pending = 2 value = steps_per_beat elif menu_item == 'Scale': libseq.setScale(value) name = self.load_keymap() self.redraw_pending = 1 return "Keymap: %s" % (name) elif menu_item == 'Tonic': if value < 0: value = 11 if value > 11: value = 0 self.parent.set_param('Tonic', 'value', value) offset = value - libseq.getTonic() libseq.setTonic(value) self.load_keymap() self.redraw_pending = 1 return "Tonic: %s" % (self.notes[value]) elif menu_item == 'Input channel': if value == 0: libseq.setInputChannel(0xFF) return 'Input channel: None' libseq.setInputChannel(value - 1) elif menu_item == 'Rest note': if value < 0 or value > 127: value = 128 libseq.setInputRest(value) if value > 127: return "Rest note: None" return "Rest note: %s%d(%d)" % ([ 'C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B' ][value % 12], int(value / 12) - 1, value) return "%s: %d" % (menu_item, value)