def testQuantizeNoteSequenceAbsolute(self): testing_lib.add_track_to_sequence( self.note_sequence, 0, [(12, 100, 0.01, 10.0), (11, 55, 0.22, 0.50), (40, 45, 2.50, 3.50), (55, 120, 4.0, 4.01), (52, 99, 4.75, 5.0)]) testing_lib.add_chords_to_sequence( self.note_sequence, [('B7', 0.22), ('Em9', 4.0)]) testing_lib.add_control_changes_to_sequence( self.note_sequence, 0, [(2.0, 64, 127), (4.0, 64, 0)]) expected_quantized_sequence = copy.deepcopy(self.note_sequence) expected_quantized_sequence.quantization_info.steps_per_second = 4 testing_lib.add_quantized_steps_to_sequence( expected_quantized_sequence, [(0, 40), (1, 2), (10, 14), (16, 17), (19, 20)]) testing_lib.add_quantized_chord_steps_to_sequence( expected_quantized_sequence, [1, 16]) testing_lib.add_quantized_control_steps_to_sequence( expected_quantized_sequence, [8, 16]) quantized_sequence = sequences_lib.quantize_note_sequence_absolute( self.note_sequence, steps_per_second=4) self.assertProtoEquals(expected_quantized_sequence, quantized_sequence)
def testApplySustainControlChangesWithRepeatedNotes(self): """Verify that sustain control handles repeated notes correctly. For example, a single pitch played before sustain: x-- x-- x-- After sustain: x---x---x-- Notes should be extended until either the end of the sustain control or the beginning of another note of the same pitch. """ sequence = copy.copy(self.note_sequence) testing_lib.add_control_changes_to_sequence( sequence, 0, [(1.0, 64, 127), (4.0, 64, 0)]) expected_sequence = copy.copy(sequence) testing_lib.add_track_to_sequence( sequence, 0, [(60, 100, 0.25, 1.50), (60, 100, 1.25, 1.50), (72, 100, 2.00, 3.50), (60, 100, 2.0, 3.00), (60, 100, 3.50, 4.50)]) testing_lib.add_track_to_sequence( expected_sequence, 0, [(60, 100, 0.25, 1.25), (60, 100, 1.25, 2.00), (72, 100, 2.00, 4.00), (60, 100, 2.0, 3.50), (60, 100, 3.50, 4.50)]) sus_sequence = sequences_lib.apply_sustain_control_changes(sequence) self.assertProtoEquals(expected_sequence, sus_sequence)
def testApplySustainControlChangesWithRepeatedNotes(self): """Verify that sustain control handles repeated notes correctly. For example, a single pitch played before sustain: x-- x-- x-- After sustain: x---x---x-- Notes should be extended until either the end of the sustain control or the beginning of another note of the same pitch. """ sequence = copy.copy(self.note_sequence) testing_lib.add_control_changes_to_sequence(sequence, 0, [(1.0, 64, 127), (4.0, 64, 0)]) expected_sequence = copy.copy(sequence) testing_lib.add_track_to_sequence(sequence, 0, [(60, 100, 0.25, 1.50), (60, 100, 1.25, 1.50), (72, 100, 2.00, 3.50), (60, 100, 2.0, 3.00), (60, 100, 3.50, 4.50)]) testing_lib.add_track_to_sequence(expected_sequence, 0, [(60, 100, 0.25, 1.25), (60, 100, 1.25, 2.00), (72, 100, 2.00, 4.00), (60, 100, 2.0, 3.50), (60, 100, 3.50, 4.50)]) sus_sequence = sequences_lib.apply_sustain_control_changes(sequence) self.assertProtoEquals(expected_sequence, sus_sequence)
def testApplySustainControlChangesWithRepeatedNotesBeforeSustain(self): """Repeated notes before sustain can overlap and should not be modified. Once a repeat happens within the sustain, any active notes should end before the next one starts. This is kind of an edge case because a note overlapping a note of the same pitch may not make sense, but apply_sustain_control_changes tries not to modify events that happen outside of a sustain. """ sequence = copy.copy(self.note_sequence) testing_lib.add_control_changes_to_sequence(sequence, 0, [(1.0, 64, 127), (4.0, 64, 0)]) expected_sequence = copy.copy(sequence) testing_lib.add_track_to_sequence(sequence, 0, [(60, 100, 0.25, 1.50), (60, 100, .50, 1.50), (60, 100, 1.25, 2.0)]) testing_lib.add_track_to_sequence(expected_sequence, 0, [(60, 100, 0.25, 1.25), (60, 100, 0.50, 1.25), (60, 100, 1.25, 4.00)]) sus_sequence = sequences_lib.apply_sustain_control_changes(sequence) self.assertProtoEquals(expected_sequence, sus_sequence)
def testSustainPipeline(self): note_sequence = common_testing_lib.parse_test_proto( music_pb2.NoteSequence, """ time_signatures: { numerator: 4 denominator: 4} tempos: { qpm: 60}""") testing_lib.add_track_to_sequence(note_sequence, 0, [(11, 55, 0.22, 0.50), (40, 45, 2.50, 3.50), (55, 120, 4.0, 4.01)]) testing_lib.add_control_changes_to_sequence(note_sequence, 0, [(0.0, 64, 127), (0.75, 64, 0), (2.0, 64, 127), (3.0, 64, 0), (3.75, 64, 127), (4.5, 64, 127), (4.8, 64, 0), (4.9, 64, 127), (6.0, 64, 0)]) expected_sequence = sequences_lib.apply_sustain_control_changes( note_sequence) unit = note_sequence_pipelines.SustainPipeline() self._unit_transform_test(unit, note_sequence, [expected_sequence])
def testApplySustainControlChanges(self): """Verify sustain controls extend notes until the end of the control.""" sequence = copy.copy(self.note_sequence) testing_lib.add_control_changes_to_sequence(sequence, 0, [(0.0, 64, 127), (0.75, 64, 0), (2.0, 64, 127), (3.0, 64, 0), (3.75, 64, 127), (4.5, 64, 127), (4.8, 64, 0), (4.9, 64, 127), (6.0, 64, 0)]) testing_lib.add_track_to_sequence(sequence, 1, [(12, 100, 0.01, 10.0), (52, 99, 4.75, 5.0)]) expected_sequence = copy.copy(sequence) testing_lib.add_track_to_sequence(sequence, 0, [(11, 55, 0.22, 0.50), (40, 45, 2.50, 3.50), (55, 120, 4.0, 4.01)]) testing_lib.add_track_to_sequence(expected_sequence, 0, [(11, 55, 0.22, 0.75), (40, 45, 2.50, 3.50), (55, 120, 4.0, 4.8)]) sus_sequence = sequences_lib.apply_sustain_control_changes(sequence) self.assertProtoEquals(expected_sequence, sus_sequence)
def testMixSequencesWithSustain(self): sample_rate = 10 sequence1 = music_pb2.NoteSequence() sequence1.notes.add(pitch=60, start_time=0.5, end_time=0.6, velocity=90) sequence1.notes.add(pitch=62, start_time=1.0, end_time=2.0, velocity=90) sequence1.total_time = 2.0 testing_lib.add_control_changes_to_sequence(sequence1, 0, [(0.0, 64, 127), (1.0, 64, 0)]) samples1 = np.linspace(0, 1, int(sample_rate * sequence1.total_time)) sequence2 = music_pb2.NoteSequence() sequence2.notes.add(pitch=64, start_time=0.5, end_time=0.6, velocity=90) sequence2.total_time = 1.0 testing_lib.add_control_changes_to_sequence(sequence2, 0, [(0.0, 64, 127), (0.9, 64, 0)]) samples2 = np.linspace(0, 1, int(sample_rate * sequence2.total_time)) mixed_samples, mixed_sequence = audio_label_data_utils.mix_sequences( [samples1, samples2], sample_rate, [sequence1, sequence2]) expected_sequence = music_pb2.NoteSequence() expected_sequence.ticks_per_quarter = constants.STANDARD_PPQ expected_sequence.notes.add(pitch=60, start_time=0.5, end_time=1.0, velocity=127) expected_sequence.notes.add(pitch=62, start_time=1.0, end_time=2.0, velocity=127) expected_sequence.notes.add(pitch=64, start_time=0.5, end_time=0.9, velocity=127) expected_sequence.notes.add(pitch=64, start_time=1.5, end_time=1.9, velocity=127) expected_sequence.total_time = 2.0 self.assertProtoEquals(expected_sequence, mixed_sequence) expected_samples = np.concatenate([samples2, samples2 ]) * .5 + samples1 * .5 np.testing.assert_array_equal(expected_samples, mixed_samples)
def testApplySustainControlChangesSimultaneousOnOff(self): """Test sustain on and off events happening at the same time. The off event should be processed last, so this should be a no-op. """ sequence = copy.copy(self.note_sequence) testing_lib.add_control_changes_to_sequence(sequence, 0, [(1.0, 64, 127), (1.0, 64, 0)]) testing_lib.add_track_to_sequence(sequence, 0, [(60, 100, 0.50, 1.50), (60, 100, 2.0, 3.0)]) sus_sequence = sequences_lib.apply_sustain_control_changes(sequence) self.assertProtoEquals(sequence, sus_sequence)
def testExtractSubsequence(self): sequence = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( sequence, 0, [(12, 100, 0.01, 10.0), (11, 55, 0.22, 0.50), (40, 45, 2.50, 3.50), (55, 120, 4.0, 4.01), (52, 99, 4.75, 5.0)]) testing_lib.add_chords_to_sequence( sequence, [('C', 1.5), ('G7', 3.0), ('F', 4.8)]) testing_lib.add_control_changes_to_sequence( sequence, 0, [(0.0, 64, 127), (2.0, 64, 0), (4.0, 64, 127), (5.0, 64, 0)]) testing_lib.add_control_changes_to_sequence( sequence, 1, [(2.0, 64, 127)]) expected_subsequence = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( expected_subsequence, 0, [(40, 45, 0.0, 1.0), (55, 120, 1.5, 1.51)]) testing_lib.add_chords_to_sequence( expected_subsequence, [('C', 0.0), ('G7', 0.5)]) testing_lib.add_control_changes_to_sequence( expected_subsequence, 0, [(0.0, 64, 0), (1.5, 64, 127)]) testing_lib.add_control_changes_to_sequence( expected_subsequence, 1, [(0.0, 64, 127)]) expected_subsequence.control_changes.sort(key=lambda cc: cc.time) expected_subsequence.total_time = 1.51 expected_subsequence.subsequence_info.start_time_offset = 2.5 expected_subsequence.subsequence_info.end_time_offset = 5.99 subsequence = sequences_lib.extract_subsequence(sequence, 2.5, 4.75) self.assertProtoEquals(expected_subsequence, subsequence)
def testApplySustainControlChangesSimultaneousOnOff(self): """Test sustain on and off events happening at the same time. The off event should be processed last, so this should be a no-op. """ sequence = copy.copy(self.note_sequence) testing_lib.add_control_changes_to_sequence( sequence, 0, [(1.0, 64, 127), (1.0, 64, 0)]) testing_lib.add_track_to_sequence( sequence, 0, [(60, 100, 0.50, 1.50), (60, 100, 2.0, 3.0)]) sus_sequence = sequences_lib.apply_sustain_control_changes(sequence) self.assertProtoEquals(sequence, sus_sequence)
def testApplySustainControlChangesExtendNotesToEnd(self): """Test sustain control extending the duration of the final note.""" sequence = copy.copy(self.note_sequence) testing_lib.add_control_changes_to_sequence(sequence, 0, [(1.0, 64, 127), (4.0, 64, 0)]) expected_sequence = copy.copy(sequence) testing_lib.add_track_to_sequence(sequence, 0, [(60, 100, 0.50, 1.50), (72, 100, 2.0, 3.0)]) testing_lib.add_track_to_sequence(expected_sequence, 0, [(60, 100, 0.50, 4.00), (72, 100, 2.0, 4.0)]) expected_sequence.total_time = 4.0 sus_sequence = sequences_lib.apply_sustain_control_changes(sequence) self.assertProtoEquals(expected_sequence, sus_sequence)
def testApplySustainControlChangesExtendNotesToEnd(self): """Test sustain control extending the duration of the final note.""" sequence = copy.copy(self.note_sequence) testing_lib.add_control_changes_to_sequence( sequence, 0, [(1.0, 64, 127), (4.0, 64, 0)]) expected_sequence = copy.copy(sequence) testing_lib.add_track_to_sequence( sequence, 0, [(60, 100, 0.50, 1.50), (72, 100, 2.0, 3.0)]) testing_lib.add_track_to_sequence( expected_sequence, 0, [(60, 100, 0.50, 4.00), (72, 100, 2.0, 4.0)]) expected_sequence.total_time = 4.0 sus_sequence = sequences_lib.apply_sustain_control_changes(sequence) self.assertProtoEquals(expected_sequence, sus_sequence)
def testApplySustainControlChangesWithIdenticalNotes(self): """In the case of identical notes, one should be dropped. This is an edge case because in most cases, the same pitch should not sound twice at the same time on one instrument. """ sequence = copy.copy(self.note_sequence) testing_lib.add_control_changes_to_sequence(sequence, 0, [(1.0, 64, 127), (4.0, 64, 0)]) expected_sequence = copy.copy(sequence) testing_lib.add_track_to_sequence(sequence, 0, [(60, 100, 2.00, 2.50), (60, 100, 2.00, 2.50)]) testing_lib.add_track_to_sequence(expected_sequence, 0, [(60, 100, 2.00, 4.00)]) sus_sequence = sequences_lib.apply_sustain_control_changes(sequence) self.assertProtoEquals(expected_sequence, sus_sequence)
def testApplySustainControlChangesExtraneousSustain(self): """Test applying extraneous sustain control at the end of the sequence.""" sequence = copy.copy(self.note_sequence) testing_lib.add_control_changes_to_sequence(sequence, 0, [(4.0, 64, 127), (5.0, 64, 0)]) expected_sequence = copy.copy(sequence) testing_lib.add_track_to_sequence(sequence, 0, [(60, 100, 0.50, 1.50), (72, 100, 2.0, 3.0)]) testing_lib.add_track_to_sequence(expected_sequence, 0, [(60, 100, 0.50, 1.50), (72, 100, 2.0, 3.0)]) # The total_time field only takes *notes* into account, and should not be # affected by a sustain-on event beyond the last note. expected_sequence.total_time = 3.0 sus_sequence = sequences_lib.apply_sustain_control_changes(sequence) self.assertProtoEquals(expected_sequence, sus_sequence)
def testApplySustainControlChangesExtraneousSustain(self): """Test applying extraneous sustain control at the end of the sequence.""" sequence = copy.copy(self.note_sequence) testing_lib.add_control_changes_to_sequence( sequence, 0, [(4.0, 64, 127), (5.0, 64, 0)]) expected_sequence = copy.copy(sequence) testing_lib.add_track_to_sequence( sequence, 0, [(60, 100, 0.50, 1.50), (72, 100, 2.0, 3.0)]) testing_lib.add_track_to_sequence( expected_sequence, 0, [(60, 100, 0.50, 1.50), (72, 100, 2.0, 3.0)]) # The total_time field only takes *notes* into account, and should not be # affected by a sustain-on event beyond the last note. expected_sequence.total_time = 3.0 sus_sequence = sequences_lib.apply_sustain_control_changes(sequence) self.assertProtoEquals(expected_sequence, sus_sequence)
def testApplySustainControlChangesWithIdenticalNotes(self): """In the case of identical notes, one should be dropped. This is an edge case because in most cases, the same pitch should not sound twice at the same time on one instrument. """ sequence = copy.copy(self.note_sequence) testing_lib.add_control_changes_to_sequence( sequence, 0, [(1.0, 64, 127), (4.0, 64, 0)]) expected_sequence = copy.copy(sequence) testing_lib.add_track_to_sequence( sequence, 0, [(60, 100, 2.00, 2.50), (60, 100, 2.00, 2.50)]) testing_lib.add_track_to_sequence( expected_sequence, 0, [(60, 100, 2.00, 4.00)]) sus_sequence = sequences_lib.apply_sustain_control_changes(sequence) self.assertProtoEquals(expected_sequence, sus_sequence)
def testApplySustainControlChanges(self): sequence = copy.copy(self.note_sequence) testing_lib.add_control_changes_to_sequence( sequence, 0, [(0.0, 64, 127), (0.75, 64, 0), (2.0, 64, 127), (3.0, 64, 0), (3.75, 64, 127), (4.5, 64, 127), (4.8, 64, 0), (4.9, 64, 127), (6.0, 64, 0)]) testing_lib.add_track_to_sequence( sequence, 1, [(12, 100, 0.01, 10.0), (52, 99, 4.75, 5.0)]) expected_sequence = copy.copy(sequence) testing_lib.add_track_to_sequence( sequence, 0, [(11, 55, 0.22, 0.50), (40, 45, 2.50, 3.50), (55, 120, 4.0, 4.01)]) testing_lib.add_track_to_sequence( expected_sequence, 0, [(11, 55, 0.22, 0.75), (40, 45, 2.50, 3.50), (55, 120, 4.0, 4.8)]) sus_sequence = sequences_lib.apply_sustain_control_changes(sequence) self.assertProtoEquals(expected_sequence, sus_sequence)
def testSustainPipeline(self): note_sequence = common_testing_lib.parse_test_proto( music_pb2.NoteSequence, """ time_signatures: { numerator: 4 denominator: 4} tempos: { qpm: 60}""") testing_lib.add_track_to_sequence( note_sequence, 0, [(11, 55, 0.22, 0.50), (40, 45, 2.50, 3.50), (55, 120, 4.0, 4.01)]) testing_lib.add_control_changes_to_sequence( note_sequence, 0, [(0.0, 64, 127), (0.75, 64, 0), (2.0, 64, 127), (3.0, 64, 0), (3.75, 64, 127), (4.5, 64, 127), (4.8, 64, 0), (4.9, 64, 127), (6.0, 64, 0)]) expected_sequence = sequences_lib.apply_sustain_control_changes( note_sequence) unit = note_sequence_pipelines.SustainPipeline() self._unit_transform_test(unit, note_sequence, [expected_sequence])
def testApplySustainControlChangesWithRepeatedNotesBeforeSustain(self): """Repeated notes before sustain can overlap and should not be modified. Once a repeat happens within the sustain, any active notes should end before the next one starts. This is kind of an edge case because a note overlapping a note of the same pitch may not make sense, but apply_sustain_control_changes tries not to modify events that happen outside of a sustain. """ sequence = copy.copy(self.note_sequence) testing_lib.add_control_changes_to_sequence( sequence, 0, [(1.0, 64, 127), (4.0, 64, 0)]) expected_sequence = copy.copy(sequence) testing_lib.add_track_to_sequence( sequence, 0, [(60, 100, 0.25, 1.50), (60, 100, .50, 1.50), (60, 100, 1.25, 2.0)]) testing_lib.add_track_to_sequence( expected_sequence, 0, [(60, 100, 0.25, 1.25), (60, 100, 0.50, 1.25), (60, 100, 1.25, 4.00)]) sus_sequence = sequences_lib.apply_sustain_control_changes(sequence) self.assertProtoEquals(expected_sequence, sus_sequence)
def testShiftSequenceTimes(self): sequence = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( sequence, 0, [(12, 100, 0.01, 10.0), (11, 55, 0.22, 0.50), (40, 45, 2.50, 3.50), (55, 120, 4.0, 4.01), (52, 99, 4.75, 5.0)]) testing_lib.add_chords_to_sequence( sequence, [('C', 1.5), ('G7', 3.0), ('F', 4.8)]) testing_lib.add_control_changes_to_sequence( sequence, 0, [(0.0, 64, 127), (2.0, 64, 0), (4.0, 64, 127), (5.0, 64, 0)]) testing_lib.add_control_changes_to_sequence( sequence, 1, [(2.0, 64, 127)]) testing_lib.add_pitch_bends_to_sequence( sequence, 1, 1, [(2.0, 100), (3.0, 0)]) expected_sequence = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( expected_sequence, 0, [(12, 100, 1.01, 11.0), (11, 55, 1.22, 1.50), (40, 45, 3.50, 4.50), (55, 120, 5.0, 5.01), (52, 99, 5.75, 6.0)]) testing_lib.add_chords_to_sequence( expected_sequence, [('C', 2.5), ('G7', 4.0), ('F', 5.8)]) testing_lib.add_control_changes_to_sequence( expected_sequence, 0, [(1.0, 64, 127), (3.0, 64, 0), (5.0, 64, 127), (6.0, 64, 0)]) testing_lib.add_control_changes_to_sequence( expected_sequence, 1, [(3.0, 64, 127)]) testing_lib.add_pitch_bends_to_sequence( expected_sequence, 1, 1, [(3.0, 100), (4.0, 0)]) expected_sequence.time_signatures[0].time = 1 expected_sequence.tempos[0].time = 1 shifted_sequence = sequences_lib.shift_sequence_times(sequence, 1.0) self.assertProtoEquals(expected_sequence, shifted_sequence)