def group_completion(buf, cell, last_sample, sample, last_timing, timing, row_delta, max_distance, mu_factor, mu_threshold): if last_sample != sample: return True, 'sample_change (%d -> %d)' % (last_sample, sample) if last_timing != timing: return True, 'tempo_change' if row_delta > max_distance: return True, 'notes_apart' notes = [period_to_idx(c.period) for c in buf if c.sample_idx != 0] notes.append(period_to_idx(cell.period)) hi_octave = max(notes) // 12 lo_octave = min(notes) // 12 if hi_octave - lo_octave >= 2: return True, 'two_octaves' # Rows containing notes rows = [i for i, cell in enumerate(buf) if cell.sample_idx != 0] n_rows = len(rows) if n_rows < mu_threshold: return False, 'less_than_mu_threshold' diff_sum = sum(y - x for (x, y) in zip(rows, rows[1:])) avg_diff = diff_sum / (n_rows - 1) if row_delta > avg_diff * mu_factor: return True, 'exceeds_mu_factor' return False, 'no'
def move_to_c(cells): notes = [period_to_idx(c.period) for c in cells if c.period != 0] first_note_in_octave = notes[0] % 12 if first_note_in_octave <= 6: delta = -first_note_in_octave else: delta = 12 - first_note_in_octave c_offset = notes[0] % 12 for c in cells: if c.period != 0: new_note = period_to_idx(c.period) + delta assert 0 <= new_note < len(PERIODS) c.period = PERIODS[new_note] return cells
def period_to_string(period): if period == 0: return '...' idx = period_to_idx(period) octave_idx = idx // 12 note_idx = idx % 12 note = NOTES[note_idx] return '%s%d' % (note, octave_idx)
def column_to_mod_notes(rows, col_idx, volumes): tempo = DEFAULT_TEMPO speed = DEFAULT_SPEED col_period = None col_sample_idx = None notes = [] for row_idx, row in enumerate(rows): tempo, speed = update_timings(row, tempo, speed) time_ms = int(calc_row_time(tempo, speed) * 1000) cell = row[col_idx] sample_idx = cell.sample_idx period = cell.period # Neither sample nor note, skipping if not sample_idx and not period: continue if sample_idx: col_sample_idx = sample_idx if period: col_period_idx = period # Sample but no note, we skip those. if sample_idx and not period: continue # Period but no sample if period and not sample_idx: sample_idx = col_sample_idx if sample_idx is None: fmt = 'Missing sample at cell %4d:%d and ' \ + 'no channel sample. MOD bug?' SP.print(fmt % (row_idx, col_idx)) continue # fmt = 'Using last sample at cell %4d:%d' # SP.print(fmt % (row_idx, col_idx)) vol_idx = sample_idx - 1 if not 0 <= vol_idx < len(volumes): fmt = 'Sample %d out of bounds at cell %4d:%d. MOD bug?' SP.print(fmt % (sample_idx, row_idx, col_idx)) continue vol = mod_note_volume(volumes[vol_idx], cell) pitch_idx = period_to_idx(period) assert 0 <= pitch_idx < 60 note = ModNote(row_idx, col_idx, sample_idx, pitch_idx, vol, time_ms) notes.append(note) # Add durations for n1, n2 in zip(notes, notes[1:]): n1.duration = n2.row_idx - n1.row_idx if notes: notes[-1].duration = len(rows) - notes[-1].row_idx return notes
def pattern_to_matrix(pattern, percussive): mat = np.zeros((4, 64), dtype=int) for i, row in enumerate(pattern.rows): current_sample = None for j, cell in enumerate(row): if cell.sample_idx: current_sample = cell.sample_idx if cell.period and current_sample not in percussive: mat[j, i] = period_to_idx(cell.period) neg_counts = -np.count_nonzero(mat, axis=1) order = np.argsort(neg_counts) mat = mat[order] min_note = np.min(mat[mat > 0], initial=100) mat[mat > 0] -= (min_note - 1) return mat.T
def test_period_to_idx(): idx = period_to_idx(679) assert PERIODS[idx] == 678 idx = period_to_idx(56) assert PERIODS[idx] == 57