def _fret_string_combos(note): r""" Given a (theoretical) `note`, return all supported fret/string combos, along with flags indicating whether the returned values are usable. This assumes that a note can occur in no more than six locations. To ease testing, entries go from low strings to high strings. >>> _fret_string_combos(('E', 3)) (12, 5, 1, 7, 4, 1, 2, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0) """ result_builder = [] for location in frets(note): result_builder.append(location[0]) result_builder.append(STANDARD_STRINGS.index(location[1])) result_builder.append(1) missing_values = 18 - len(result_builder) result_builder.extend([0] * missing_values) return tuple(result_builder)
def test_not_too_high(self): results = list(notemappings.frets(('A', 5))) for result in results: self.assertTrue(result[0] <= 24)
def test_g_5_all_occurrences(self, mock_lowest_string): mock_lowest_string.return_value = ('B', 3) expected = [(20, ('B', 3)), (15, ('E', 4))] for pair in zip(expected, notemappings.frets(('G', 5))): self.assertEqual(pair[0], pair[1])
def test_d_4_all_occurrences(self, mock_lowest_string): mock_lowest_string.return_value = ('E', 2) expected = [(22, ('E', 2)), (17, ('A', 2)), (12, ('D', 3)), (7, ('G', 3)), (3, ('B', 3))] for pair in zip(expected, notemappings.frets(('D', 4))): self.assertEqual(pair[0], pair[1])
def test_middle_c_all_occurrences(self, mock_lowest_string): mock_lowest_string.return_value = ('E', 2) expected = [(8, ('E', 2)), (3, ('A', 2))] for pair in zip(expected, notemappings.frets(('C', 3))): self.assertEqual(pair[0], pair[1])
def _sample_data(preceding, current_chord, following, recent_notes, current_chord_index, limiting_note): r""" Transform a given chord context into an input-output pair for an ANN. Given a dataset and a chord context, this function yields pairs of input and output values *for each note in the current chord*. `recent_notes` is a dict-like object which, for each (theoretical) note, specifies the most recent fret and string values (bundled in an indexed sequence). `limiting_note`, if any value, is a tuple whose elements are: #. the next (theoretical) limiting note, i.e. the first note that comes after `current_chord` and which imposes strict constraints on where it can be played #. the chord index of that note Chords belonging to the chord context are specified in their physical form. This includes the current chord and successor chords. """ LOG.debug('Current chord: {current}'.format(current=current_chord)) highest_note_chord = _highest_note_chord(current_chord) lowest_note_chord = _lowest_note_chord(current_chord) avg_frets = _avg_frets(preceding) preceding_notes = _pad_physical_notes(_adjacent_notes(preceding, 4), 4) following_notes = _pad_physical_notes(_adjacent_notes(following, 6, fwd=True), 6) LOG.debug('Following notes: {f}'.format(f=following_notes)) following_notes = [fret2note(phys[1], phys[0]) for phys in list(following_notes)] if limiting_note: to_limiting_note = limiting_note[1] - current_chord_index minimal_fret_limiting_note = None for fret in frets(limiting_note[0]): if minimal_fret_limiting_note is None or fret[0] < minimal_fret_limiting_note: minimal_fret_limiting_note = fret[0] minimal_string_limiting_note = STANDARD_STRINGS.index(fret[1]) limiting_note_flag = 1 else: to_limiting_note = 1000 # just use something so big it is irrelevant minimal_fret_limiting_note = 0 minimal_string_limiting_note = 0 limiting_note_flag = 0 if preceding: num_notes_prev_chord = len(preceding[-1]) else: num_notes_prev_chord = 0 if len(preceding) > 1: num_notes_prev_chord_2 = len(preceding[-2]) else: num_notes_prev_chord_2 = 0 if following: num_notes_next_chord = len(following[0]) else: num_notes_next_chord = 0 if len(following) > 1: num_notes_next_chord_2 = len(following[1]) else: num_notes_next_chord_2 = 0 for string_num in sorted(current_chord.keys()): note = fret2note(current_chord[string_num], string_num) recent_note = recent_notes.get(note, (0, 0)) fret_string_combos = _fret_string_combos(note) bits = [0] * SUPPORTED_FRETS bits[current_chord[string_num]] = 1 # fret alone is fine inputs = (# 1: notes in current chord len(current_chord.keys()), # 2-3: current note octave and value int(note[1]), NOTE_SEQUENCE.index(note[0]), # 4-5: highest note in chord's octave and value highest_note_chord[1], NOTE_SEQUENCE.index(highest_note_chord[0]), # 6-7: lowest note in chord's octave and value lowest_note_chord[1], NOTE_SEQUENCE.index(lowest_note_chord[0]), # 8-10: average fret for three most recent chords avg_frets[0], avg_frets[1], avg_frets[2], # 11-12: fret and string for most recent occurrence note recent_note[0], recent_note[1], # 13-20: string and fret for four most recent notes preceding_notes[0][0], preceding_notes[0][1], preceding_notes[1][0], preceding_notes[1][1], preceding_notes[2][0], preceding_notes[2][1], preceding_notes[3][0], preceding_notes[3][1], # 21: number of chords played since last four notes _chords_for_notes(4, preceding, fwd=False), # 22-23: number of notes in previous two chords num_notes_prev_chord, num_notes_prev_chord_2, # 24-29: booleans indicating whether string are in use int(0 in current_chord), int(1 in current_chord), int(2 in current_chord), int(3 in current_chord), int(4 in current_chord), int(5 in current_chord), # 30-47: all string/fret combinations for the current note # **ADDED** each third entry indicates whether combo is real # **ADDED** supports up to 24 real frets, so 6 locations fret_string_combos[0], fret_string_combos[1], fret_string_combos[2], fret_string_combos[3], fret_string_combos[4], fret_string_combos[5], fret_string_combos[6], fret_string_combos[7], fret_string_combos[8], fret_string_combos[9], fret_string_combos[10], fret_string_combos[11], fret_string_combos[12], fret_string_combos[13], fret_string_combos[14], fret_string_combos[15], fret_string_combos[16], fret_string_combos[17], # 48-49: number of notes in next two chords num_notes_next_chord, num_notes_next_chord_2, # 50-61: octave and note for next six notes NOTE_SEQUENCE.index(following_notes[0][0]), following_notes[0][1], NOTE_SEQUENCE.index(following_notes[1][0]), following_notes[1][1], NOTE_SEQUENCE.index(following_notes[2][0]), following_notes[2][1], NOTE_SEQUENCE.index(following_notes[3][0]), following_notes[3][1], NOTE_SEQUENCE.index(following_notes[4][0]), following_notes[4][1], NOTE_SEQUENCE.index(following_notes[5][0]), following_notes[5][1], # 62: number of chords between current note and next six notes _chords_for_notes(6, following, fwd=True), # 63-65: fret/string furthest from body for limiting note minimal_fret_limiting_note, minimal_string_limiting_note, limiting_note_flag, # 66: number of chords until next limiting note to_limiting_note, ) sample = (inputs, tuple(bits)) LOG.debug('Yielding sample: {sample}'.format(sample=sample)) yield sample