def testExtractLeadSheetFragments(self): self.quantized_sequence.steps_per_quarter = 1 testing_lib.add_quantized_track(self.quantized_sequence, 0, [(12, 100, 2, 4), (11, 1, 6, 11)]) testing_lib.add_quantized_track(self.quantized_sequence, 1, [(12, 127, 2, 4), (14, 50, 6, 8), (50, 100, 33, 37), (52, 100, 34, 37)]) testing_lib.add_quantized_chords(self.quantized_sequence, [('C', 2), ('G7', 6), ('Cmaj7', 33)]) lead_sheets, _ = lead_sheets_lib.extract_lead_sheet_fragments( self.quantized_sequence, min_bars=1, gap_bars=2, min_unique_pitches=2, ignore_polyphonic_notes=True, require_chords=True) melodies, _ = melodies_lib.extract_melodies( self.quantized_sequence, min_bars=1, gap_bars=2, min_unique_pitches=2, ignore_polyphonic_notes=True) chord_progressions, _ = chords_lib.extract_chords_for_melodies( self.quantized_sequence, melodies) self.assertEqual(list(melodies), list(lead_sheet.melody for lead_sheet in lead_sheets)) self.assertEqual(list(chord_progressions), list(lead_sheet.chords for lead_sheet in lead_sheets))
def testExtractMelodiesStatistics(self): testing_lib.add_track_to_sequence( self.note_sequence, 0, [(12, 100, 2, 4), (11, 1, 6, 7), (10, 100, 8, 10), (9, 100, 11, 14), (8, 100, 16, 40), (7, 100, 41, 42)]) testing_lib.add_track_to_sequence( self.note_sequence, 1, [(12, 127, 2, 4), (14, 50, 2, 8)]) testing_lib.add_track_to_sequence( self.note_sequence, 2, [(12, 127, 0, 1)]) testing_lib.add_track_to_sequence( self.note_sequence, 3, [(12, 127, 2, 4), (12, 50, 6, 8)]) quantized_sequence = sequences_lib.quantize_note_sequence( self.note_sequence, steps_per_quarter=1) _, stats = melodies_lib.extract_melodies( quantized_sequence, min_bars=1, gap_bars=1, min_unique_pitches=2, ignore_polyphonic_notes=False) stats_dict = dict([(stat.name, stat) for stat in stats]) self.assertEqual(stats_dict['polyphonic_tracks_discarded'].count, 1) self.assertEqual(stats_dict['melodies_discarded_too_short'].count, 1) self.assertEqual(stats_dict['melodies_discarded_too_few_pitches'].count, 1) self.assertEqual( stats_dict['melody_lengths_in_bars'].counters, {float('-inf'): 0, 0: 0, 1: 0, 2: 0, 10: 1, 20: 0, 30: 0, 40: 0, 50: 0, 100: 0, 200: 0, 500: 0})
def testExtractMelodiesTooFewPitches(self): # Test that extract_melodies discards melodies with too few pitches where # pitches are equivalent by octave. self.quantized_sequence.steps_per_quarter = 1 testing_lib.add_quantized_track_to_sequence(self.quantized_sequence, 0, [(12, 100, 0, 1), (13, 100, 1, 2), (18, 100, 2, 3), (24, 100, 3, 4), (25, 100, 4, 5)]) testing_lib.add_quantized_track_to_sequence(self.quantized_sequence, 1, [(12, 100, 0, 1), (13, 100, 1, 2), (18, 100, 2, 3), (25, 100, 3, 4), (26, 100, 4, 5)]) expected = [[12, 13, 18, 25, 26]] melodies, _ = melodies_lib.extract_melodies( self.quantized_sequence, min_bars=1, gap_bars=1, min_unique_pitches=4, ignore_polyphonic_notes=True) melodies = [list(melody) for melody in melodies] self.assertEqual(expected, melodies)
def testExtractMelodiesSimple(self): testing_lib.add_track_to_sequence( self.note_sequence, 0, [(12, 100, 2, 4), (11, 1, 6, 7)]) testing_lib.add_track_to_sequence( self.note_sequence, 1, [(12, 127, 2, 4), (14, 50, 6, 9)]) testing_lib.add_track_to_sequence( self.note_sequence, 9, [(13, 100, 2, 4), (15, 25, 6, 8)], is_drum=True) quantized_sequence = sequences_lib.quantize_note_sequence( self.note_sequence, steps_per_quarter=1) expected = [[NO_EVENT, NO_EVENT, 12, NO_EVENT, NOTE_OFF, NO_EVENT, 11], [NO_EVENT, NO_EVENT, 12, NO_EVENT, NOTE_OFF, NO_EVENT, 14, NO_EVENT, NO_EVENT]] melodies, _ = melodies_lib.extract_melodies( quantized_sequence, min_bars=1, gap_bars=1, min_unique_pitches=2, ignore_polyphonic_notes=True) self.assertEqual(2, len(melodies)) self.assertTrue(isinstance(melodies[0], melodies_lib.Melody)) self.assertTrue(isinstance(melodies[1], melodies_lib.Melody)) melodies = sorted([list(melody) for melody in melodies]) self.assertEqual(expected, melodies)
def testExtractMultipleMelodiesFromSameTrack(self): testing_lib.add_track_to_sequence(self.note_sequence, 0, [(12, 100, 2, 4), (11, 1, 6, 11)]) testing_lib.add_track_to_sequence(self.note_sequence, 1, [(12, 127, 2, 4), (14, 50, 6, 8), (50, 100, 33, 37), (52, 100, 34, 37)]) quantized_sequence = sequences_lib.quantize_note_sequence( self.note_sequence, steps_per_quarter=1) expected = [[ NO_EVENT, NO_EVENT, 12, NO_EVENT, NOTE_OFF, NO_EVENT, 11, NO_EVENT, NO_EVENT, NO_EVENT, NO_EVENT ], [ NO_EVENT, NO_EVENT, 12, NO_EVENT, NOTE_OFF, NO_EVENT, 14, NO_EVENT ], [NO_EVENT, 50, 52, NO_EVENT, NO_EVENT]] melodies, _ = melodies_lib.extract_melodies( quantized_sequence, min_bars=1, gap_bars=2, min_unique_pitches=2, ignore_polyphonic_notes=True) melodies = sorted([list(melody) for melody in melodies]) self.assertEqual(expected, melodies)
def testExtractChordsForMelodiesCoincidentChords(self): testing_lib.add_track_to_sequence(self.note_sequence, 0, [(12, 100, 2, 4), (11, 1, 6, 11)]) testing_lib.add_track_to_sequence(self.note_sequence, 1, [(12, 127, 2, 4), (14, 50, 6, 8), (50, 100, 33, 37), (52, 100, 34, 37)]) testing_lib.add_chords_to_sequence(self.note_sequence, [('C', 2), ('G7', 6), ('E13', 8), ('Cmaj7', 8)]) quantized_sequence = sequences_lib.quantize_note_sequence( self.note_sequence, self.steps_per_quarter) melodies, _ = melodies_lib.extract_melodies( quantized_sequence, min_bars=1, gap_bars=2, min_unique_pitches=2, ignore_polyphonic_notes=True) chord_progressions, stats = chords_lib.extract_chords_for_melodies( quantized_sequence, melodies) expected = [[NO_CHORD, NO_CHORD, 'C', 'C', 'C', 'C', 'G7', 'G7'], ['Cmaj7', 'Cmaj7', 'Cmaj7', 'Cmaj7', 'Cmaj7']] stats_dict = dict([(stat.name, stat) for stat in stats]) self.assertIsNone(chord_progressions[0]) self.assertEqual(expected, [list(chords) for chords in chord_progressions[1:]]) self.assertEqual(stats_dict['coincident_chords'].count, 1)
def testExtractChordsForMelodies(self): testing_lib.add_track_to_sequence(self.note_sequence, 0, [(12, 100, 2, 4), (11, 1, 6, 11)]) testing_lib.add_track_to_sequence(self.note_sequence, 1, [(12, 127, 2, 4), (14, 50, 6, 8), (50, 100, 33, 37), (52, 100, 34, 37)]) testing_lib.add_chords_to_sequence(self.note_sequence, [('C', 2), ('G7', 6), ('Cmaj7', 33)]) quantized_sequence = sequences_lib.quantize_note_sequence( self.note_sequence, self.steps_per_quarter) melodies, _ = melodies_lib.extract_melodies( quantized_sequence, min_bars=1, gap_bars=2, min_unique_pitches=2, ignore_polyphonic_notes=True) chord_progressions, _ = chords_lib.extract_chords_for_melodies( quantized_sequence, melodies) expected = [[ NO_CHORD, NO_CHORD, 'C', 'C', 'C', 'C', 'G7', 'G7', 'G7', 'G7', 'G7' ], [NO_CHORD, NO_CHORD, 'C', 'C', 'C', 'C', 'G7', 'G7'], ['G7', 'Cmaj7', 'Cmaj7', 'Cmaj7', 'Cmaj7']] self.assertEqual(expected, [list(chords) for chords in chord_progressions])
def testExtractLeadSheetFragmentsNoChords(self): self.quantized_sequence.steps_per_quarter = 1 testing_lib.add_quantized_track_to_sequence( self.quantized_sequence, 0, [(12, 100, 2, 4), (11, 1, 6, 11)]) testing_lib.add_quantized_track_to_sequence( self.quantized_sequence, 1, [(12, 127, 2, 4), (14, 50, 6, 8), (50, 100, 33, 37), (52, 100, 34, 37)]) testing_lib.add_quantized_chords_to_sequence( self.quantized_sequence, [('C', 2), ('G7', 6), (NO_CHORD, 10)]) lead_sheets, stats = lead_sheets_lib.extract_lead_sheet_fragments( self.quantized_sequence, min_bars=1, gap_bars=2, min_unique_pitches=2, ignore_polyphonic_notes=True, require_chords=True) melodies, _ = melodies_lib.extract_melodies( self.quantized_sequence, min_bars=1, gap_bars=2, min_unique_pitches=2, ignore_polyphonic_notes=True) chord_progressions, _ = chords_lib.extract_chords_for_melodies( self.quantized_sequence, melodies) stats_dict = dict([(stat.name, stat) for stat in stats]) # Last lead sheet should be rejected for having no chords. self.assertEqual(list(melodies[:2]), list(lead_sheet.melody for lead_sheet in lead_sheets)) self.assertEqual(list(chord_progressions[:2]), list(lead_sheet.chords for lead_sheet in lead_sheets)) self.assertEqual(stats_dict['empty_chord_progressions'].count, 1)
def testExtractChordsForMelodiesCoincidentChords(self): testing_lib.add_track_to_sequence( self.note_sequence, 0, [(12, 100, 2, 4), (11, 1, 6, 11)]) testing_lib.add_track_to_sequence( self.note_sequence, 1, [(12, 127, 2, 4), (14, 50, 6, 8), (50, 100, 33, 37), (52, 100, 34, 37)]) testing_lib.add_chords_to_sequence( self.note_sequence, [('C', 2), ('G7', 6), ('E13', 8), ('Cmaj7', 8)]) quantized_sequence = sequences_lib.quantize_note_sequence( self.note_sequence, self.steps_per_quarter) melodies, _ = melodies_lib.extract_melodies( quantized_sequence, min_bars=1, gap_bars=2, min_unique_pitches=2, ignore_polyphonic_notes=True) chord_progressions, stats = chords_lib.extract_chords_for_melodies( quantized_sequence, melodies) expected = [[NO_CHORD, NO_CHORD, 'C', 'C', 'C', 'C', 'G7', 'G7'], ['Cmaj7', 'Cmaj7', 'Cmaj7', 'Cmaj7', 'Cmaj7']] stats_dict = dict((stat.name, stat) for stat in stats) self.assertIsNone(chord_progressions[0]) self.assertEqual(expected, [list(chords) for chords in chord_progressions[1:]]) self.assertEqual(stats_dict['coincident_chords'].count, 1)
def testExtractLeadSheetFragmentsCoincidentChords(self): self.quantized_sequence.steps_per_quarter = 1 testing_lib.add_quantized_track_to_sequence( self.quantized_sequence, 0, [(12, 100, 2, 4), (11, 1, 6, 11)]) testing_lib.add_quantized_track_to_sequence( self.quantized_sequence, 1, [(12, 127, 2, 4), (14, 50, 6, 8), (50, 100, 33, 37), (52, 100, 34, 37)]) testing_lib.add_quantized_chords_to_sequence( self.quantized_sequence, [('C', 2), ('G7', 6), ('Cmaj7', 33), ('F', 33)]) lead_sheets, _ = lead_sheets_lib.extract_lead_sheet_fragments( self.quantized_sequence, min_bars=1, gap_bars=2, min_unique_pitches=2, ignore_polyphonic_notes=True, require_chords=True) melodies, _ = melodies_lib.extract_melodies( self.quantized_sequence, min_bars=1, gap_bars=2, min_unique_pitches=2, ignore_polyphonic_notes=True) chord_progressions, _ = chords_lib.extract_chords_for_melodies( self.quantized_sequence, melodies) # Last lead sheet should be rejected for coincident chords. self.assertEqual(list(melodies[:2]), list(lead_sheet.melody for lead_sheet in lead_sheets)) self.assertEqual(list(chord_progressions[:2]), list(lead_sheet.chords for lead_sheet in lead_sheets))
def testExtractLeadSheetFragmentsCoincidentChords(self): testing_lib.add_track_to_sequence( self.note_sequence, 0, [(12, 100, 2, 4), (11, 1, 6, 11)]) testing_lib.add_track_to_sequence( self.note_sequence, 1, [(12, 127, 2, 4), (14, 50, 6, 8), (50, 100, 33, 37), (52, 100, 34, 37)]) testing_lib.add_chords_to_sequence( self.note_sequence, [('C', 2), ('G7', 6), ('Cmaj7', 33), ('F', 33)]) quantized_sequence = sequences_lib.quantize_note_sequence( self.note_sequence, steps_per_quarter=1) lead_sheets, _ = lead_sheets_lib.extract_lead_sheet_fragments( quantized_sequence, min_bars=1, gap_bars=2, min_unique_pitches=2, ignore_polyphonic_notes=True, require_chords=True) melodies, _ = melodies_lib.extract_melodies( quantized_sequence, min_bars=1, gap_bars=2, min_unique_pitches=2, ignore_polyphonic_notes=True) chord_progressions, _ = chords_lib.extract_chords_for_melodies( quantized_sequence, melodies) # Last lead sheet should be rejected for coincident chords. self.assertEqual(list(melodies[:2]), list(lead_sheet.melody for lead_sheet in lead_sheets)) self.assertEqual(list(chord_progressions[:2]), list(lead_sheet.chords for lead_sheet in lead_sheets))
def testExtractLeadSheetFragmentsNoChords(self): testing_lib.add_track_to_sequence( self.note_sequence, 0, [(12, 100, 2, 4), (11, 1, 6, 11)]) testing_lib.add_track_to_sequence( self.note_sequence, 1, [(12, 127, 2, 4), (14, 50, 6, 8), (50, 100, 33, 37), (52, 100, 34, 37)]) testing_lib.add_chords_to_sequence( self.note_sequence, [('C', 2), ('G7', 6), (NO_CHORD, 10)]) quantized_sequence = sequences_lib.quantize_note_sequence( self.note_sequence, steps_per_quarter=1) lead_sheets, stats = lead_sheets_lib.extract_lead_sheet_fragments( quantized_sequence, min_bars=1, gap_bars=2, min_unique_pitches=2, ignore_polyphonic_notes=True, require_chords=True) melodies, _ = melodies_lib.extract_melodies( quantized_sequence, min_bars=1, gap_bars=2, min_unique_pitches=2, ignore_polyphonic_notes=True) chord_progressions, _ = chords_lib.extract_chords_for_melodies( quantized_sequence, melodies) stats_dict = dict((stat.name, stat) for stat in stats) # Last lead sheet should be rejected for having no chords. self.assertEqual(list(melodies[:2]), list(lead_sheet.melody for lead_sheet in lead_sheets)) self.assertEqual(list(chord_progressions[:2]), list(lead_sheet.chords for lead_sheet in lead_sheets)) self.assertEqual(stats_dict['empty_chord_progressions'].count, 1)
def testExtractMelodiesStatistics(self): testing_lib.add_track_to_sequence( self.note_sequence, 0, [(12, 100, 2, 4), (11, 1, 6, 7), (10, 100, 8, 10), (9, 100, 11, 14), (8, 100, 16, 40), (7, 100, 41, 42)]) testing_lib.add_track_to_sequence( self.note_sequence, 1, [(12, 127, 2, 4), (14, 50, 2, 8)]) testing_lib.add_track_to_sequence( self.note_sequence, 2, [(12, 127, 0, 1)]) testing_lib.add_track_to_sequence( self.note_sequence, 3, [(12, 127, 2, 4), (12, 50, 6, 8)]) quantized_sequence = sequences_lib.quantize_note_sequence( self.note_sequence, steps_per_quarter=1) _, stats = melodies_lib.extract_melodies( quantized_sequence, min_bars=1, gap_bars=1, min_unique_pitches=2, ignore_polyphonic_notes=False) stats_dict = dict((stat.name, stat) for stat in stats) self.assertEqual(stats_dict['polyphonic_tracks_discarded'].count, 1) self.assertEqual(stats_dict['melodies_discarded_too_short'].count, 1) self.assertEqual(stats_dict['melodies_discarded_too_few_pitches'].count, 1) self.assertEqual( stats_dict['melody_lengths_in_bars'].counters, {float('-inf'): 0, 0: 0, 1: 0, 2: 0, 10: 1, 20: 0, 30: 0, 40: 0, 50: 0, 100: 0, 200: 0, 500: 0})
def testExtractMelodiesPadEnd(self): testing_lib.add_track_to_sequence( self.note_sequence, 0, [(12, 127, 2, 4), (14, 50, 6, 7)]) testing_lib.add_track_to_sequence( self.note_sequence, 1, [(12, 127, 2, 4), (14, 50, 6, 8)]) testing_lib.add_track_to_sequence( self.note_sequence, 2, [(12, 127, 2, 4), (14, 50, 6, 9)]) quantized_sequence = sequences_lib.quantize_note_sequence( self.note_sequence, steps_per_quarter=1) expected = [[NO_EVENT, NO_EVENT, 12, NO_EVENT, NOTE_OFF, NO_EVENT, 14, NOTE_OFF], [NO_EVENT, NO_EVENT, 12, NO_EVENT, NOTE_OFF, NO_EVENT, 14, NO_EVENT], [NO_EVENT, NO_EVENT, 12, NO_EVENT, NOTE_OFF, NO_EVENT, 14, NO_EVENT, NO_EVENT, NOTE_OFF, NO_EVENT, NO_EVENT]] melodies, _ = melodies_lib.extract_melodies( quantized_sequence, min_bars=1, gap_bars=1, min_unique_pitches=2, ignore_polyphonic_notes=True, pad_end=True) melodies = [list(melody) for melody in melodies] self.assertEqual(expected, melodies)
def testExtractMelodiesMelodyTooLong(self): testing_lib.add_track_to_sequence(self.note_sequence, 0, [(12, 127, 2, 4), (14, 50, 6, 15)]) testing_lib.add_track_to_sequence(self.note_sequence, 1, [(12, 127, 2, 4), (14, 50, 6, 18)]) quantized_sequence = sequences_lib.quantize_note_sequence( self.note_sequence, steps_per_quarter=1) expected = [ [NO_EVENT, NO_EVENT, 12, NO_EVENT, NOTE_OFF, NO_EVENT, 14] + [NO_EVENT] * 7, [NO_EVENT, NO_EVENT, 12, NO_EVENT, NOTE_OFF, NO_EVENT, 14] + [NO_EVENT] * 7 ] melodies, _ = melodies_lib.extract_melodies( quantized_sequence, min_bars=1, max_steps_truncate=14, max_steps_discard=18, gap_bars=1, min_unique_pitches=2, ignore_polyphonic_notes=True) melodies = [list(melody) for melody in melodies] self.assertEqual(expected, melodies)
def testExtractLeadSheetFragments(self): testing_lib.add_track_to_sequence( self.note_sequence, 0, [(12, 100, .5, 1), (11, 1, 1.5, 2.75)]) testing_lib.add_track_to_sequence( self.note_sequence, 1, [(12, 127, .5, 1), (14, 50, 1.5, 2), (50, 100, 8.25, 9.25), (52, 100, 8.5, 9.25)]) testing_lib.add_chords_to_sequence( self.note_sequence, [('C', .5), ('G7', 1.5), ('Cmaj7', 8.25)]) quantized_sequence = sequences_lib.quantize_note_sequence( self.note_sequence, self.steps_per_quarter) lead_sheets, _ = lead_sheets_lib.extract_lead_sheet_fragments( quantized_sequence, min_bars=1, gap_bars=2, min_unique_pitches=2, ignore_polyphonic_notes=True, require_chords=True) melodies, _ = melodies_lib.extract_melodies( quantized_sequence, min_bars=1, gap_bars=2, min_unique_pitches=2, ignore_polyphonic_notes=True) chord_progressions, _ = chords_lib.extract_chords_for_melodies( quantized_sequence, melodies) self.assertEqual(list(melodies), list(lead_sheet.melody for lead_sheet in lead_sheets)) self.assertEqual(list(chord_progressions), list(lead_sheet.chords for lead_sheet in lead_sheets))
def extract_lead_sheet_fragments(quantized_sequence, min_bars=7, gap_bars=1.0, min_unique_pitches=5, ignore_polyphonic_notes=True, require_chords=False): """Extracts a list of lead sheet fragments from a quantized NoteSequence. This function first extracts melodies using melodies_lib.extract_melodies, then extracts the chords underlying each melody using chords_lib.extract_chords_for_melodies. Args: quantized_sequence: A quantized NoteSequence object. min_bars: Minimum length of melodies in number of bars. Shorter melodies are discarded. gap_bars: A melody comes to an end when this number of bars (measures) of silence is encountered. min_unique_pitches: Minimum number of unique notes with octave equivalence. Melodies with too few unique notes are discarded. ignore_polyphonic_notes: If True, melodies will be extracted from `quantized_sequence` tracks that contain polyphony (notes start at the same time). If False, tracks with polyphony will be ignored. require_chords: If True, only return lead sheets that have at least one chord other than NO_CHORD. If False, lead sheets with only melody will also be returned. Returns: A python list of LeadSheet instances. Raises: NonIntegerStepsPerBarException: If `quantized_sequence`'s bar length (derived from its time signature) is not an integer number of time steps. """ sequences_lib.assert_is_quantized_sequence(quantized_sequence) stats = dict([('empty_chord_progressions', statistics.Counter('empty_chord_progressions'))]) melodies, melody_stats = melodies_lib.extract_melodies( quantized_sequence, min_bars=min_bars, gap_bars=gap_bars, min_unique_pitches=min_unique_pitches, ignore_polyphonic_notes=ignore_polyphonic_notes) chord_progressions, chord_stats = chords_lib.extract_chords_for_melodies( quantized_sequence, melodies) lead_sheets = [] for melody, chords in zip(melodies, chord_progressions): if chords is not None: if require_chords and all(chord == chords_lib.NO_CHORD for chord in chords): stats['empty_chord_progressions'].increment() else: lead_sheet = LeadSheet(melody, chords) lead_sheets.append(lead_sheet) return lead_sheets, stats.values() + melody_stats + chord_stats
def transform(self, quantized_sequence): try: melodies, stats = melodies_lib.extract_melodies( quantized_sequence, min_bars=self.min_bars, min_unique_pitches=self.min_unique_pitches, gap_bars=self.gap_bars, ignore_polyphonic_notes=self.ignore_polyphonic_notes) except events_lib.NonIntegerStepsPerBarException as detail: tf.logging.warning('Skipped sequence: %s', detail) melodies = [] stats = [statistics.Counter('non_integer_steps_per_bar', 1)] self._set_stats(stats) return melodies
def transform(self, quantized_sequence): try: melodies, stats = melodies_lib.extract_melodies( quantized_sequence, min_bars=self.min_bars, min_unique_pitches=self.min_unique_pitches, gap_bars=self.gap_bars, ignore_polyphonic_notes=self.ignore_polyphonic_notes) except melodies_lib.NonIntegerStepsPerBarException as detail: tf.logging.warning('Skipped sequence: %s', detail) melodies = [] stats = [statistics.Counter('non_integer_steps_per_bar', 1)] self._set_stats(stats) return melodies
def testExtractMelodiesMelodyTooShort(self): self.quantized_sequence.steps_per_quarter = 1 testing_lib.add_quantized_track_to_sequence( self.quantized_sequence, 0, [(12, 127, 2, 4), (14, 50, 6, 7)]) testing_lib.add_quantized_track_to_sequence( self.quantized_sequence, 1, [(12, 127, 2, 4), (14, 50, 6, 9)]) expected = [[NO_EVENT, NO_EVENT, 12, NO_EVENT, NOTE_OFF, NO_EVENT, 14, NO_EVENT, NO_EVENT]] melodies, _ = melodies_lib.extract_melodies( self.quantized_sequence, min_bars=2, gap_bars=1, min_unique_pitches=2, ignore_polyphonic_notes=True) melodies = [list(melody) for melody in melodies] self.assertEqual(expected, melodies)
def testExtractMelodiesLateStart(self): self.quantized_sequence.steps_per_quarter = 1 testing_lib.add_quantized_track_to_sequence( self.quantized_sequence, 0, [(12, 100, 102, 103), (13, 100, 104, 106)]) testing_lib.add_quantized_track_to_sequence( self.quantized_sequence, 1, [(12, 100, 100, 101), (13, 100, 102, 105)]) expected = [[NO_EVENT, NO_EVENT, 12, NOTE_OFF, 13, NO_EVENT], [12, NOTE_OFF, 13, NO_EVENT, NO_EVENT]] melodies, _ = melodies_lib.extract_melodies( self.quantized_sequence, min_bars=1, gap_bars=1, min_unique_pitches=2, ignore_polyphonic_notes=True) melodies = sorted([list(melody) for melody in melodies]) self.assertEqual(expected, melodies)
def testExtractMelodiesMelodyTooLongWithPad(self): self.quantized_sequence.steps_per_quarter = 1 testing_lib.add_quantized_track_to_sequence( self.quantized_sequence, 0, [(12, 127, 2, 4), (14, 50, 6, 15)]) testing_lib.add_quantized_track_to_sequence( self.quantized_sequence, 1, [(12, 127, 2, 4), (14, 50, 6, 18)]) expected = [[NO_EVENT, NO_EVENT, 12, NO_EVENT, NOTE_OFF, NO_EVENT, 14, NO_EVENT, NO_EVENT, NO_EVENT, NO_EVENT, NO_EVENT]] melodies, _ = melodies_lib.extract_melodies( self.quantized_sequence, min_bars=1, max_steps_truncate=14, max_steps_discard=18, gap_bars=1, min_unique_pitches=2, ignore_polyphonic_notes=True, pad_end=True) melodies = [list(melody) for melody in melodies] self.assertEqual(expected, melodies)
def testExtractMelodiesTooFewPitches(self): # Test that extract_melodies discards melodies with too few pitches where # pitches are equivalent by octave. self.quantized_sequence.steps_per_quarter = 1 testing_lib.add_quantized_track_to_sequence( self.quantized_sequence, 0, [(12, 100, 0, 1), (13, 100, 1, 2), (18, 100, 2, 3), (24, 100, 3, 4), (25, 100, 4, 5)]) testing_lib.add_quantized_track_to_sequence( self.quantized_sequence, 1, [(12, 100, 0, 1), (13, 100, 1, 2), (18, 100, 2, 3), (25, 100, 3, 4), (26, 100, 4, 5)]) expected = [[12, 13, 18, 25, 26]] melodies, _ = melodies_lib.extract_melodies( self.quantized_sequence, min_bars=1, gap_bars=1, min_unique_pitches=4, ignore_polyphonic_notes=True) melodies = [list(melody) for melody in melodies] self.assertEqual(expected, melodies)
def load_primer(self): """Loads default MIDI primer file. Also assigns the steps per bar of this file to be the model's defaults. """ if not os.path.exists(self.midi_primer): tf.logging.warn('ERROR! No such primer file exists! %s', self.midi_primer) return self.primer_sequence = midi_io.midi_file_to_sequence_proto(self.midi_primer) quantized_seq = sequences_lib.quantize_note_sequence( self.primer_sequence, steps_per_quarter=4) extracted_melodies, _ = melodies_lib.extract_melodies(quantized_seq, min_bars=0, min_unique_pitches=1) self.primer = extracted_melodies[0] self.steps_per_bar = self.primer.steps_per_bar
def testExtractMultipleMelodiesFromSameTrack(self): self.quantized_sequence.steps_per_quarter = 1 testing_lib.add_quantized_track_to_sequence( self.quantized_sequence, 0, [(12, 100, 2, 4), (11, 1, 6, 11)]) testing_lib.add_quantized_track_to_sequence( self.quantized_sequence, 1, [(12, 127, 2, 4), (14, 50, 6, 8), (50, 100, 33, 37), (52, 100, 34, 37)]) expected = [[NO_EVENT, NO_EVENT, 12, NO_EVENT, NOTE_OFF, NO_EVENT, 11, NO_EVENT, NO_EVENT, NO_EVENT, NO_EVENT], [NO_EVENT, NO_EVENT, 12, NO_EVENT, NOTE_OFF, NO_EVENT, 14, NO_EVENT], [NO_EVENT, 50, 52, NO_EVENT, NO_EVENT]] melodies, _ = melodies_lib.extract_melodies( self.quantized_sequence, min_bars=1, gap_bars=2, min_unique_pitches=2, ignore_polyphonic_notes=True) melodies = sorted([list(melody) for melody in melodies]) self.assertEqual(expected, melodies)
def transform(self, quantized_sequence): try: melodies, stats = melodies_lib.extract_melodies( quantized_sequence, min_bars=self._min_bars, max_steps_truncate=self._max_steps, min_unique_pitches=self._min_unique_pitches, gap_bars=self._gap_bars, ignore_polyphonic_notes=self._ignore_polyphonic_notes, filter_drums=self._filter_drums) except events_lib.NonIntegerStepsPerBarException as detail: tf.logging.warning('Skipped sequence: %s', detail) melodies = [] stats = [statistics.Counter('non_integer_steps_per_bar', 1)] except events_lib.ZeroDivisionError as detail: tf.logging.warning('Skipped sequence: %s', detail) melodies = [] stats = [statistics.Counter('zero_division_error', 1)] self._set_stats(stats) return melodies
def testExtractMelodiesMelodyTooLongWithPad(self): self.quantized_sequence.steps_per_quarter = 1 testing_lib.add_quantized_track(self.quantized_sequence, 0, [(12, 127, 2, 4), (14, 50, 6, 15)]) testing_lib.add_quantized_track(self.quantized_sequence, 1, [(12, 127, 2, 4), (14, 50, 6, 18)]) expected = [[ NO_EVENT, NO_EVENT, 12, NO_EVENT, NOTE_OFF, NO_EVENT, 14, NO_EVENT, NO_EVENT, NO_EVENT, NO_EVENT, NO_EVENT ]] melodies, _ = melodies_lib.extract_melodies( self.quantized_sequence, min_bars=1, max_steps_truncate=14, max_steps_discard=18, gap_bars=1, min_unique_pitches=2, ignore_polyphonic_notes=True, pad_end=True) melodies = [list(melody) for melody in melodies] self.assertEqual(expected, melodies)
def testExtractChordsForMelodies(self): self.quantized_sequence.steps_per_quarter = 1 testing_lib.add_quantized_track_to_sequence( self.quantized_sequence, 0, [(12, 100, 2, 4), (11, 1, 6, 11)]) testing_lib.add_quantized_track_to_sequence( self.quantized_sequence, 1, [(12, 127, 2, 4), (14, 50, 6, 8), (50, 100, 33, 37), (52, 100, 34, 37)]) testing_lib.add_quantized_chords_to_sequence( self.quantized_sequence, [('C', 2), ('G7', 6), ('Cmaj7', 33)]) melodies, _ = melodies_lib.extract_melodies( self.quantized_sequence, min_bars=1, gap_bars=2, min_unique_pitches=2, ignore_polyphonic_notes=True) chord_progressions, _ = chords_lib.extract_chords_for_melodies( self.quantized_sequence, melodies) expected = [[NO_CHORD, NO_CHORD, 'C', 'C', 'C', 'C', 'G7', 'G7', 'G7', 'G7', 'G7'], [NO_CHORD, NO_CHORD, 'C', 'C', 'C', 'C', 'G7', 'G7'], ['G7', 'Cmaj7', 'Cmaj7', 'Cmaj7', 'Cmaj7']] self.assertEqual(expected, [list(chords) for chords in chord_progressions])
def _generate(self, generate_sequence_request): if len(generate_sequence_request.generator_options.generate_sections ) != 1: raise sequence_generator.SequenceGeneratorException( 'This model supports only 1 generate_sections message, but got %s' % (len(generate_sequence_request.generator_options. generate_sections))) generate_section = ( generate_sequence_request.generator_options.generate_sections[0]) primer_sequence = generate_sequence_request.input_sequence notes_by_end_time = sorted(primer_sequence.notes, key=lambda n: n.end_time) last_end_time = notes_by_end_time[ -1].end_time if notes_by_end_time else 0 if last_end_time > generate_section.start_time_seconds: raise sequence_generator.SequenceGeneratorException( 'Got GenerateSection request for section that is before the end of ' 'the NoteSequence. This model can only extend sequences. ' 'Requested start time: %s, Final note end time: %s' % (generate_section.start_time_seconds, notes_by_end_time[-1].end_time)) # Quantize the priming sequence. quantized_sequence = sequences_lib.QuantizedSequence() quantized_sequence.from_note_sequence(primer_sequence, self._steps_per_quarter) # Setting gap_bars to infinite ensures that the entire input will be used. extracted_melodies, _ = melodies_lib.extract_melodies( quantized_sequence, min_bars=0, min_unique_pitches=1, gap_bars=float('inf'), ignore_polyphonic_notes=True) assert len(extracted_melodies) <= 1 qpm = (primer_sequence.tempos[0].qpm if primer_sequence and primer_sequence.tempos else constants.DEFAULT_QUARTERS_PER_MINUTE) start_step = self._seconds_to_steps( generate_section.start_time_seconds, qpm) end_step = self._seconds_to_steps(generate_section.end_time_seconds, qpm) if extracted_melodies and extracted_melodies[0]: melody = extracted_melodies[0] else: tf.logging.warn( 'No melodies were extracted from the priming sequence. ' 'Melodies will be generated from scratch.') melody = melodies_lib.MonophonicMelody() melody.from_event_list([ random.randint(self._melody_encoder_decoder.min_note, self._melody_encoder_decoder.max_note) ]) start_step += 1 transpose_amount = melody.squash( self._melody_encoder_decoder.min_note, self._melody_encoder_decoder.max_note, self._melody_encoder_decoder.transpose_to_key) # Ensure that the melody extends up to the step we want to start generating. melody.set_length(start_step) inputs = self._session.graph.get_collection('inputs')[0] initial_state = self._session.graph.get_collection('initial_state')[0] final_state = self._session.graph.get_collection('final_state')[0] softmax = self._session.graph.get_collection('softmax')[0] final_state_ = None for i in range(end_step - len(melody)): if i == 0: inputs_ = self._melody_encoder_decoder.get_inputs_batch( [melody], full_length=True) initial_state_ = self._session.run(initial_state) else: inputs_ = self._melody_encoder_decoder.get_inputs_batch( [melody]) initial_state_ = final_state_ feed_dict = {inputs: inputs_, initial_state: initial_state_} final_state_, softmax_ = self._session.run([final_state, softmax], feed_dict) self._melody_encoder_decoder.extend_event_sequences([melody], softmax_) melody.transpose(-transpose_amount) generate_response = generator_pb2.GenerateSequenceResponse() generate_response.generated_sequence.CopyFrom( melody.to_sequence(qpm=qpm)) return generate_response
def extract_lead_sheet_fragments(quantized_sequence, search_start_step=0, min_bars=7, max_steps_truncate=None, max_steps_discard=None, gap_bars=1.0, min_unique_pitches=5, ignore_polyphonic_notes=True, pad_end=False, filter_drums=True, require_chords=False, all_transpositions=False): """Extracts a list of lead sheet fragments from a quantized NoteSequence. This function first extracts melodies using melodies_lib.extract_melodies, then extracts the chords underlying each melody using chords_lib.extract_chords_for_melodies. Args: quantized_sequence: A quantized NoteSequence object. search_start_step: Start searching for a melody at this time step. Assumed to be the first step of a bar. min_bars: Minimum length of melodies in number of bars. Shorter melodies are discarded. max_steps_truncate: Maximum number of steps in extracted melodies. If defined, longer melodies are truncated to this threshold. If pad_end is also True, melodies will be truncated to the end of the last bar below this threshold. max_steps_discard: Maximum number of steps in extracted melodies. If defined, longer melodies are discarded. gap_bars: A melody comes to an end when this number of bars (measures) of silence is encountered. min_unique_pitches: Minimum number of unique notes with octave equivalence. Melodies with too few unique notes are discarded. ignore_polyphonic_notes: If True, melodies will be extracted from `quantized_sequence` tracks that contain polyphony (notes start at the same time). If False, tracks with polyphony will be ignored. pad_end: If True, the end of the melody will be padded with NO_EVENTs so that it will end at a bar boundary. filter_drums: If True, notes for which `is_drum` is True will be ignored. require_chords: If True, only return lead sheets that have at least one chord other than NO_CHORD. If False, lead sheets with only melody will also be returned. all_transpositions: If True, also transpose each lead sheet fragment into all 12 keys. Returns: A python list of LeadSheet instances. Raises: NonIntegerStepsPerBarException: If `quantized_sequence`'s bar length (derived from its time signature) is not an integer number of time steps. """ sequences_lib.assert_is_relative_quantized_sequence(quantized_sequence) stats = dict([('empty_chord_progressions', statistics.Counter('empty_chord_progressions'))]) melodies, melody_stats = melodies_lib.extract_melodies( quantized_sequence, search_start_step=search_start_step, min_bars=min_bars, max_steps_truncate=max_steps_truncate, max_steps_discard=max_steps_discard, gap_bars=gap_bars, min_unique_pitches=min_unique_pitches, ignore_polyphonic_notes=ignore_polyphonic_notes, pad_end=pad_end, filter_drums=filter_drums) chord_progressions, chord_stats = chords_lib.extract_chords_for_melodies( quantized_sequence, melodies) lead_sheets = [] for melody, chords in zip(melodies, chord_progressions): # If `chords` is None, it's because a chord progression could not be # extracted for this particular melody. if chords is not None: if require_chords and all(chord == chords_lib.NO_CHORD for chord in chords): stats['empty_chord_progressions'].increment() else: lead_sheet = LeadSheet(melody, chords) if all_transpositions: for amount in range(-6, 6): transposed_lead_sheet = copy.deepcopy(lead_sheet) transposed_lead_sheet.transpose(amount) lead_sheets.append(transposed_lead_sheet) else: lead_sheets.append(lead_sheet) return lead_sheets, list(stats.values()) + melody_stats + chord_stats
def extract_lead_sheet_fragments(quantized_sequence, search_start_step=0, min_bars=7, max_steps_truncate=None, max_steps_discard=None, gap_bars=1.0, min_unique_pitches=5, ignore_polyphonic_notes=True, pad_end=False, filter_drums=True, require_chords=False, all_transpositions=False): """Extracts a list of lead sheet fragments from a quantized NoteSequence. This function first extracts melodies using melodies_lib.extract_melodies, then extracts the chords underlying each melody using chords_lib.extract_chords_for_melodies. Args: quantized_sequence: A quantized NoteSequence object. search_start_step: Start searching for a melody at this time step. Assumed to be the first step of a bar. min_bars: Minimum length of melodies in number of bars. Shorter melodies are discarded. max_steps_truncate: Maximum number of steps in extracted melodies. If defined, longer melodies are truncated to this threshold. If pad_end is also True, melodies will be truncated to the end of the last bar below this threshold. max_steps_discard: Maximum number of steps in extracted melodies. If defined, longer melodies are discarded. gap_bars: A melody comes to an end when this number of bars (measures) of silence is encountered. min_unique_pitches: Minimum number of unique notes with octave equivalence. Melodies with too few unique notes are discarded. ignore_polyphonic_notes: If True, melodies will be extracted from `quantized_sequence` tracks that contain polyphony (notes start at the same time). If False, tracks with polyphony will be ignored. pad_end: If True, the end of the melody will be padded with NO_EVENTs so that it will end at a bar boundary. filter_drums: If True, notes for which `is_drum` is True will be ignored. require_chords: If True, only return lead sheets that have at least one chord other than NO_CHORD. If False, lead sheets with only melody will also be returned. all_transpositions: If True, also transpose each lead sheet fragment into all 12 keys. Returns: A python list of LeadSheet instances. Raises: NonIntegerStepsPerBarException: If `quantized_sequence`'s bar length (derived from its time signature) is not an integer number of time steps. """ sequences_lib.assert_is_quantized_sequence(quantized_sequence) stats = dict([('empty_chord_progressions', statistics.Counter('empty_chord_progressions'))]) melodies, melody_stats = melodies_lib.extract_melodies( quantized_sequence, search_start_step=search_start_step, min_bars=min_bars, max_steps_truncate=max_steps_truncate, max_steps_discard=max_steps_discard, gap_bars=gap_bars, min_unique_pitches=min_unique_pitches, ignore_polyphonic_notes=ignore_polyphonic_notes, pad_end=pad_end, filter_drums=filter_drums) chord_progressions, chord_stats = chords_lib.extract_chords_for_melodies( quantized_sequence, melodies) lead_sheets = [] for melody, chords in zip(melodies, chord_progressions): # If `chords` is None, it's because a chord progression could not be # extracted for this particular melody. if chords is not None: if require_chords and all(chord == chords_lib.NO_CHORD for chord in chords): stats['empty_chord_progressions'].increment() else: lead_sheet = LeadSheet(melody, chords) if all_transpositions: for amount in range(-6, 6): transposed_lead_sheet = copy.deepcopy(lead_sheet) transposed_lead_sheet.transpose(amount) lead_sheets.append(transposed_lead_sheet) else: lead_sheets.append(lead_sheet) return lead_sheets, stats.values() + melody_stats + chord_stats