def on_zyncoder(self, encoder, value): if encoder == ENC_BACK: if self.edit_mode: self.velocity = self.velocity + value if self.velocity > 127: self.velocity = 127 return if self.velocity < 1: self.velocity = 1 return self.velocity_canvas.coords( "velocityIndicator", 0, 0, self.piano_roll_width * self.velocity / 127, PLAYHEAD_HEIGHT) note = self.keymap[self.selected_cell[1]]["note"] if libseq.getNoteDuration(self.selected_cell[0], note): libseq.setNoteVelocity(self.selected_cell[0], note, self.velocity) self.draw_cell(self.selected_cell[0], self.selected_cell[1]) self.parent.set_title("Velocity: %d" % (self.velocity), None, None, 2) else: self.select_cell(None, self.selected_cell[1] - value) elif encoder == ENC_SELECT: if self.edit_mode: if value > 0: self.duration = self.duration + 1 if value < 0: self.duration = self.duration - 1 if self.duration > libseq.getSteps(): self.duration = libseq.getSteps() return if self.duration < 1: self.duration = 1 return if libseq.getNoteDuration(self.selected_cell[0], self.selected_cell[1]): self.add_event(self.selected_cell[0], self.selected_cell[1]) else: self.select_cell() self.parent.set_title("Duration: %d steps" % (self.duration), None, None, 2) else: self.select_cell(self.selected_cell[0] + value, None) elif encoder == ENC_LAYER and not self.parent.lst_menu.winfo_viewable( ): # Show menu self.parent.toggle_menu() return
def load_pattern(self, index): libseq.clearSequence(self.bank, self.sequence) libseq.setChannel(self.bank, self.sequence, 0, self.channel) self.pattern = index libseq.selectPattern(index) libseq.addPattern(self.bank, self.sequence, 0, 0, index) if self.selected_cell[0] >= libseq.getSteps(): self.selected_cell[0] = libseq.getSteps() - 1 self.keymap_offset = libseq.getRefNote() self.load_keymap() self.redraw_pending = 2 self.select_cell(0, int(self.keymap_offset + self.zoom / 2)) self.play_canvas.coords("playCursor", 1, 0, 1 + self.step_width, PLAYHEAD_HEIGHT)
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 toggle_event(self, step, index): if step < 0 or step >= libseq.getSteps() or index >= len(self.keymap): return note = self.keymap[index]['note'] if libseq.getNoteVelocity(step, note): self.remove_event(step, index) else: self.add_event(step, index) return note
def check_pattern(self, beat_type, steps_per_beat, beats_in_pattern): steps_in_pattern = beats_in_pattern * steps_per_beat clocks_per_step = 24 / steps_per_beat self.assertEqual(libseq.getSteps(), steps_in_pattern) self.assertEqual(libseq.getBeatType(), beat_type) self.assertEqual(libseq.getStepsPerBeat(), steps_per_beat) self.assertEqual(libseq.getBeatsInPattern(), beats_in_pattern) self.assertEqual(libseq.getClocksPerStep(), clocks_per_step) self.assertEqual(libseq.getPatternLength(libseq.getPatternIndex()), clocks_per_step * steps_in_pattern)
def export_smf(self, params): smf = libsmf.addSmf() tempo = libseq.getTempo() libsmf.addTempo(smf, 0, tempo) ticks_per_step = libsmf.getTicksPerQuarterNote( smf) / libseq.getStepsPerBeat() for step in range(libseq.getSteps()): time = int(step * ticks_per_step) for note in range(128): duration = libseq.getNoteDuration(step, note) if duration == 0: continue duration = int(duration * ticks_per_step) velocity = libseq.getNoteVelocity(step, note) libsmf.addNote(smf, 0, time, duration, self.channel, note, velocity) libsmf.setEndOfTrack(smf, 0, int(libseq.getSteps() * ticks_per_step)) zynsmf.save( smf, "/zynthian/zynthian-my-data/capture/pattern%d_%s.mid" % (self.pattern, datetime.now()))
def on_grid_press(self, event): if self.parent.lst_menu.winfo_viewable(): self.parent.hide_menu() return if self.parent.param_editor_item != None: self.parent.hide_param_editor() return self.grid_drag_start = event try: col, row = self.grid_canvas.gettags( self.grid_canvas.find_withtag(tkinter.CURRENT))[0].split(',') except: return note = self.keymap[self.keymap_offset + int(row)]["note"] step = int(col) if step < 0 or step >= libseq.getSteps(): return self.drag_start_velocity = libseq.getNoteVelocity(step, note) self.drag_start_duration = libseq.getNoteDuration(step, note) self.drag_start_step = int(event.x / self.step_width) if not self.drag_start_velocity: self.play_note(note) self.select_cell(int(col), self.keymap_offset + int(row))
def select_cell(self, step=None, index=None): if len(self.keymap) == 0: return redraw = False if step == None: step = self.selected_cell[0] if index == None: index = self.selected_cell[1] if step < 0: step = 0 if step >= libseq.getSteps(): step = libseq.getSteps() - 1 if index >= len(self.keymap): index = len(self.keymap) - 1 if index >= self.keymap_offset + self.zoom: # Note is off top of display self.keymap_offset = index - self.zoom + 1 redraw = True if index < 0: index = 0 if index < self.keymap_offset: # Note is off bottom of display self.keymap_offset = index redraw = True if redraw: self.redraw_pending = 1 row = index - self.keymap_offset note = self.keymap[index]['note'] # Skip hidden (overlapping) cells for previous in range(step - 1, -1, -1): prev_duration = libseq.getNoteDuration(previous, note) if not prev_duration: continue if prev_duration > step - previous: if step > self.selected_cell[0]: step = previous + prev_duration else: step = previous break if step < 0: step = 0 if step >= libseq.getSteps(): step = libseq.getSteps() - 1 self.selected_cell = [step, index] cell = self.grid_canvas.find_withtag("selection") duration = libseq.getNoteDuration(step, row) if not duration: duration = self.duration coord = self.get_cell(step, row, duration) coord[0] = coord[0] - 1 coord[1] = coord[1] - 1 coord[2] = coord[2] coord[3] = coord[3] if not cell: cell = self.grid_canvas.create_rectangle( coord, fill="", outline=SELECT_BORDER, width=self.select_thickness, tags="selection") else: self.grid_canvas.coords(cell, coord) self.grid_canvas.tag_raise(cell)
def draw_grid(self): clear_grid = (self.redraw_pending == 2) self.redraw_pending = 0 if libseq.getSteps() == 0: return #TODO: Should we clear grid? if self.keymap_offset > len(self.keymap) - self.zoom: self.keymap_offset = len(self.keymap) - self.zoom if self.keymap_offset < 0: self.keymap_offset = 0 font = tkFont.Font(family=zynthian_gui_config.font_topbar[0], size=self.fontsize) if clear_grid: self.grid_canvas.delete(tkinter.ALL) self.step_width = (self.grid_width - 2) / libseq.getSteps() self.draw_pianoroll() self.cells = [None] * self.zoom * libseq.getSteps() self.play_canvas.coords( "playCursor", 1 + self.playhead * self.step_width, 0, 1 + self.playhead * self.step_width + self.step_width, PLAYHEAD_HEIGHT) # Draw cells of grid self.grid_canvas.itemconfig("gridcell", fill="black") # Redraw gridlines self.grid_canvas.delete("gridline") if libseq.getStepsPerBeat(): for step in range(0, libseq.getSteps() + 1, libseq.getStepsPerBeat()): self.grid_canvas.create_line(step * self.step_width, 0, step * self.step_width, self.zoom * self.row_height - 1, fill=GRID_LINE, tags=("gridline")) # Delete existing note names self.piano_roll.delete("notename") for row in range(0, self.zoom): index = row + self.keymap_offset if (index >= len(self.keymap)): break if clear_grid: # Create last note labels in grid self.grid_canvas.create_text( self.grid_width - self.select_thickness, int(self.row_height * (self.zoom - row - 0.5)), state="hidden", tags=("lastnotetext%d" % (row), "lastnotetext"), font=font, anchor="e") self.draw_row(index) # Update pianoroll keys id = "row%d" % (row) try: name = self.keymap[index]["name"] except: name = None try: colour = self.keymap[index]["colour"] except: colour = "white" self.piano_roll.itemconfig(id, fill=colour) if name: self.piano_roll.create_text( (2, self.row_height * (self.zoom - row - 0.5)), text=name, font=font, anchor="w", fill=CANVAS_BACKGROUND, tags="notename") if self.keymap[index]['note'] % 12 == libseq.getTonic(): self.grid_canvas.create_line( 0, (self.zoom - row) * self.row_height, self.grid_width, (self.zoom - row) * self.row_height, fill=GRID_LINE, tags=("gridline")) # Set z-order to allow duration to show if clear_grid: for step in range(libseq.getSteps()): self.grid_canvas.tag_lower("step%d" % step) self.select_cell()
def draw_row(self, index): row = index - self.keymap_offset self.grid_canvas.itemconfig("lastnotetext%d" % (row), state="hidden") for step in range(libseq.getSteps()): self.draw_cell(step, row)