Module note_seq.sequences_lib_test
Tests for sequences_lib.
Expand source code
# Copyright 2021 The Magenta Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Tests for sequences_lib."""
import copy
from absl.testing import absltest
from note_seq import constants
from note_seq import sequences_lib
from note_seq import testing_lib
from note_seq.protobuf import music_pb2
import numpy as np
CHORD_SYMBOL = music_pb2.NoteSequence.TextAnnotation.CHORD_SYMBOL
DEFAULT_FRAMES_PER_SECOND = 16000.0 / 512
MIDI_PITCHES = constants.MAX_MIDI_PITCH - constants.MIN_MIDI_PITCH + 1
class SequencesLibTest(testing_lib.ProtoTestCase):
def testTransposeNoteSequence(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)])
sequence.text_annotations.add(
time=1, annotation_type=CHORD_SYMBOL, text='N.C.')
sequence.text_annotations.add(
time=2, annotation_type=CHORD_SYMBOL, text='E7')
sequence.key_signatures.add(
time=0, key=music_pb2.NoteSequence.KeySignature.E,
mode=music_pb2.NoteSequence.KeySignature.MIXOLYDIAN)
expected_sequence = copy.copy(self.note_sequence)
testing_lib.add_track_to_sequence(
expected_sequence, 0,
[(13, 100, 0.01, 10.0), (12, 55, 0.22, 0.50), (41, 45, 2.50, 3.50),
(56, 120, 4.0, 4.01), (53, 99, 4.75, 5.0)])
expected_sequence.text_annotations.add(
time=1, annotation_type=CHORD_SYMBOL, text='N.C.')
expected_sequence.text_annotations.add(
time=2, annotation_type=CHORD_SYMBOL, text='F7')
expected_sequence.key_signatures.add(
time=0, key=music_pb2.NoteSequence.KeySignature.F,
mode=music_pb2.NoteSequence.KeySignature.MIXOLYDIAN)
transposed_sequence, delete_count = sequences_lib.transpose_note_sequence(
sequence, 1)
self.assertProtoEquals(expected_sequence, transposed_sequence)
self.assertEqual(delete_count, 0)
def testTransposeNoteSequenceOutOfRange(self):
sequence = copy.copy(self.note_sequence)
testing_lib.add_track_to_sequence(
sequence, 0,
[(35, 100, 0.01, 10.0), (36, 55, 0.22, 0.50), (37, 45, 2.50, 3.50),
(38, 120, 4.0, 4.01), (39, 99, 4.75, 5.0)])
expected_sequence_1 = copy.copy(self.note_sequence)
testing_lib.add_track_to_sequence(
expected_sequence_1, 0,
[(39, 100, 0.01, 10.0), (40, 55, 0.22, 0.50)])
expected_sequence_2 = copy.copy(self.note_sequence)
testing_lib.add_track_to_sequence(
expected_sequence_2, 0,
[(30, 120, 4.0, 4.01), (31, 99, 4.75, 5.0)])
sequence_copy = copy.copy(sequence)
transposed_sequence, delete_count = sequences_lib.transpose_note_sequence(
sequence_copy, 4, 30, 40)
self.assertProtoEquals(expected_sequence_1, transposed_sequence)
self.assertEqual(delete_count, 3)
sequence_copy = copy.copy(sequence)
transposed_sequence, delete_count = sequences_lib.transpose_note_sequence(
sequence_copy, -8, 30, 40)
self.assertProtoEquals(expected_sequence_2, transposed_sequence)
self.assertEqual(delete_count, 3)
def testClampTranspose(self):
clamped = sequences_lib._clamp_transpose( # pylint:disable=protected-access
5, 20, 60, 10, 70)
self.assertEqual(clamped, 5)
clamped = sequences_lib._clamp_transpose( # pylint:disable=protected-access
15, 20, 60, 10, 65)
self.assertEqual(clamped, 5)
clamped = sequences_lib._clamp_transpose( # pylint:disable=protected-access
-16, 20, 60, 10, 70)
self.assertEqual(clamped, -10)
def testAugmentNoteSequenceDeleteFalse(self):
sequence = copy.copy(self.note_sequence)
testing_lib.add_track_to_sequence(
sequence, 0, [(12, 100, 0.01, 10.0), (13, 55, 0.22, 0.50),
(40, 45, 2.50, 3.50), (55, 120, 4.0, 4.01),
(52, 99, 4.75, 5.0)])
augmented_sequence = sequences_lib.augment_note_sequence(
sequence,
min_stretch_factor=2,
max_stretch_factor=2,
min_transpose=-15,
max_transpose=-10,
min_allowed_pitch=10,
max_allowed_pitch=127,
delete_out_of_range_notes=False)
expected_sequence = copy.copy(self.note_sequence)
testing_lib.add_track_to_sequence(
expected_sequence, 0, [(10, 100, 0.02, 20.0), (11, 55, 0.44, 1.0),
(38, 45, 5., 7.), (53, 120, 8.0, 8.02),
(50, 99, 9.5, 10.0)])
expected_sequence.tempos[0].qpm = 30.
self.assertProtoEquals(augmented_sequence, expected_sequence)
def testAugmentNoteSequenceDeleteTrue(self):
sequence = copy.copy(self.note_sequence)
testing_lib.add_track_to_sequence(
sequence, 0, [(12, 100, 0.01, 10.0), (13, 55, 0.22, 0.50),
(40, 45, 2.50, 3.50), (55, 120, 4.0, 4.01),
(52, 99, 4.75, 5.0)])
augmented_sequence = sequences_lib.augment_note_sequence(
sequence,
min_stretch_factor=2,
max_stretch_factor=2,
min_transpose=-15,
max_transpose=-15,
min_allowed_pitch=10,
max_allowed_pitch=127,
delete_out_of_range_notes=True)
expected_sequence = copy.copy(self.note_sequence)
testing_lib.add_track_to_sequence(
expected_sequence, 0, [(25, 45, 5., 7.), (40, 120, 8.0, 8.02),
(37, 99, 9.5, 10.0)])
expected_sequence.tempos[0].qpm = 30.
self.assertProtoEquals(augmented_sequence, expected_sequence)
def testAugmentNoteSequenceNoStretch(self):
sequence = copy.copy(self.note_sequence)
testing_lib.add_track_to_sequence(
sequence, 0, [(12, 100, 0.01, 10.0), (13, 55, 0.22, 0.50),
(40, 45, 2.50, 3.50), (55, 120, 4.0, 4.01),
(52, 99, 4.75, 5.0)])
augmented_sequence = sequences_lib.augment_note_sequence(
sequence,
min_stretch_factor=1,
max_stretch_factor=1.,
min_transpose=-15,
max_transpose=-15,
min_allowed_pitch=10,
max_allowed_pitch=127,
delete_out_of_range_notes=True)
expected_sequence = copy.copy(self.note_sequence)
testing_lib.add_track_to_sequence(
expected_sequence, 0, [(25, 45, 2.5, 3.50), (40, 120, 4.0, 4.01),
(37, 99, 4.75, 5.0)])
self.assertProtoEquals(augmented_sequence, expected_sequence)
def testAugmentNoteSequenceNoTranspose(self):
sequence = copy.copy(self.note_sequence)
testing_lib.add_track_to_sequence(
sequence, 0, [(12, 100, 0.01, 10.0), (13, 55, 0.22, 0.50),
(40, 45, 2.50, 3.50), (55, 120, 4.0, 4.01),
(52, 99, 4.75, 5.0)])
augmented_sequence = sequences_lib.augment_note_sequence(
sequence,
min_stretch_factor=2,
max_stretch_factor=2.,
min_transpose=0,
max_transpose=0,
min_allowed_pitch=10,
max_allowed_pitch=127,
delete_out_of_range_notes=True)
expected_sequence = copy.copy(self.note_sequence)
testing_lib.add_track_to_sequence(
expected_sequence, 0, [(12, 100, 0.02, 20.0), (13, 55, 0.44, 1.0),
(40, 45, 5., 7.), (55, 120, 8.0, 8.02),
(52, 99, 9.5, 10.0)])
expected_sequence.tempos[0].qpm = 30.
self.assertProtoEquals(augmented_sequence, expected_sequence)
def testTrimNoteSequence(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)])
expected_subsequence = copy.copy(self.note_sequence)
testing_lib.add_track_to_sequence(
expected_subsequence, 0,
[(40, 45, 2.50, 3.50), (55, 120, 4.0, 4.01)])
expected_subsequence.total_time = 4.75
subsequence = sequences_lib.trim_note_sequence(sequence, 2.5, 4.75)
self.assertProtoEquals(expected_subsequence, subsequence)
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.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)
subsequence.control_changes.sort(
key=lambda cc: (cc.instrument, cc.time))
self.assertProtoEquals(expected_subsequence, subsequence)
def testExtractSubsequencePastEnd(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', 18.0)])
with self.assertRaises(ValueError):
sequences_lib.extract_subsequence(sequence, 15.0, 16.0)
def testExtractSubsequencePedalEvents(self):
sequence = copy.copy(self.note_sequence)
testing_lib.add_track_to_sequence(
sequence, 0, [(60, 80, 2.5, 5.0)])
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_control_changes_to_sequence(
sequence, 0,
[(0.0, 66, 0), (2.0, 66, 127), (4.0, 66, 0), (5.0, 66, 127)])
testing_lib.add_control_changes_to_sequence(
sequence, 0,
[(0.0, 67, 10), (2.0, 67, 20), (4.0, 67, 30), (5.0, 67, 40)])
expected_subsequence = copy.copy(self.note_sequence)
testing_lib.add_track_to_sequence(
expected_subsequence, 0, [(60, 80, 0, 2.25)])
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)])
testing_lib.add_control_changes_to_sequence(
expected_subsequence, 0, [(0.0, 66, 127), (1.5, 66, 0)])
testing_lib.add_control_changes_to_sequence(
expected_subsequence, 0, [(0.0, 67, 20), (1.5, 67, 30)])
expected_subsequence.control_changes.sort(
key=lambda cc: (cc.instrument, cc.control_number, cc.time))
expected_subsequence.total_time = 2.25
expected_subsequence.subsequence_info.start_time_offset = 2.5
expected_subsequence.subsequence_info.end_time_offset = .25
subsequence = sequences_lib.extract_subsequence(sequence, 2.5, 4.75)
subsequence.control_changes.sort(
key=lambda cc: (cc.instrument, cc.control_number, cc.time))
self.assertProtoEquals(expected_subsequence, subsequence)
def testSplitNoteSequenceWithHopSize(self):
# Tests splitting a NoteSequence at regular hop size, truncating notes.
sequence = testing_lib.parse_test_proto(
music_pb2.NoteSequence,
"""
time_signatures: {
numerator: 4
denominator: 4}
tempos: {
qpm: 60}""")
testing_lib.add_track_to_sequence(
sequence, 0,
[(12, 100, 0.01, 8.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.0), ('G7', 2.0), ('F', 4.0)])
expected_subsequence_1 = testing_lib.parse_test_proto(
music_pb2.NoteSequence,
"""
time_signatures: {
numerator: 4
denominator: 4}
tempos: {
qpm: 60}""")
testing_lib.add_track_to_sequence(
expected_subsequence_1, 0,
[(12, 100, 0.01, 3.0), (11, 55, 0.22, 0.50), (40, 45, 2.50, 3.0)])
testing_lib.add_chords_to_sequence(
expected_subsequence_1, [('C', 1.0), ('G7', 2.0)])
expected_subsequence_1.total_time = 3.0
expected_subsequence_1.subsequence_info.end_time_offset = 5.0
expected_subsequence_2 = testing_lib.parse_test_proto(
music_pb2.NoteSequence,
"""
time_signatures: {
numerator: 4
denominator: 4}
tempos: {
qpm: 60}""")
testing_lib.add_track_to_sequence(
expected_subsequence_2, 0,
[(55, 120, 1.0, 1.01), (52, 99, 1.75, 2.0)])
testing_lib.add_chords_to_sequence(
expected_subsequence_2, [('G7', 0.0), ('F', 1.0)])
expected_subsequence_2.total_time = 2.0
expected_subsequence_2.subsequence_info.start_time_offset = 3.0
expected_subsequence_2.subsequence_info.end_time_offset = 3.0
expected_subsequence_3 = testing_lib.parse_test_proto(
music_pb2.NoteSequence,
"""
time_signatures: {
numerator: 4
denominator: 4}
tempos: {
qpm: 60}""")
testing_lib.add_chords_to_sequence(
expected_subsequence_3, [('F', 0.0)])
expected_subsequence_3.total_time = 0.0
expected_subsequence_3.subsequence_info.start_time_offset = 6.0
expected_subsequence_3.subsequence_info.end_time_offset = 2.0
subsequences = sequences_lib.split_note_sequence(
sequence, hop_size_seconds=3.0)
self.assertLen(subsequences, 3)
self.assertProtoEquals(expected_subsequence_1, subsequences[0])
self.assertProtoEquals(expected_subsequence_2, subsequences[1])
self.assertProtoEquals(expected_subsequence_3, subsequences[2])
def testSplitNoteSequenceAtTimes(self):
# Tests splitting a NoteSequence at specified times, truncating notes.
sequence = testing_lib.parse_test_proto(
music_pb2.NoteSequence,
"""
time_signatures: {
numerator: 4
denominator: 4}
tempos: {
qpm: 60}""")
testing_lib.add_track_to_sequence(
sequence, 0,
[(12, 100, 0.01, 8.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.0), ('G7', 2.0), ('F', 4.0)])
expected_subsequence_1 = testing_lib.parse_test_proto(
music_pb2.NoteSequence,
"""
time_signatures: {
numerator: 4
denominator: 4}
tempos: {
qpm: 60}""")
testing_lib.add_track_to_sequence(
expected_subsequence_1, 0,
[(12, 100, 0.01, 3.0), (11, 55, 0.22, 0.50), (40, 45, 2.50, 3.0)])
testing_lib.add_chords_to_sequence(
expected_subsequence_1, [('C', 1.0), ('G7', 2.0)])
expected_subsequence_1.total_time = 3.0
expected_subsequence_1.subsequence_info.end_time_offset = 5.0
expected_subsequence_2 = testing_lib.parse_test_proto(
music_pb2.NoteSequence,
"""
time_signatures: {
numerator: 4
denominator: 4}
tempos: {
qpm: 60}""")
testing_lib.add_chords_to_sequence(
expected_subsequence_2, [('G7', 0.0)])
expected_subsequence_2.total_time = 0.0
expected_subsequence_2.subsequence_info.start_time_offset = 3.0
expected_subsequence_2.subsequence_info.end_time_offset = 5.0
expected_subsequence_3 = testing_lib.parse_test_proto(
music_pb2.NoteSequence,
"""
time_signatures: {
numerator: 4
denominator: 4}
tempos: {
qpm: 60}""")
testing_lib.add_track_to_sequence(
expected_subsequence_3, 0,
[(55, 120, 0.0, 0.01), (52, 99, 0.75, 1.0)])
testing_lib.add_chords_to_sequence(
expected_subsequence_3, [('F', 0.0)])
expected_subsequence_3.total_time = 1.0
expected_subsequence_3.subsequence_info.start_time_offset = 4.0
expected_subsequence_3.subsequence_info.end_time_offset = 3.0
subsequences = sequences_lib.split_note_sequence(
sequence, hop_size_seconds=[3.0, 4.0])
self.assertLen(subsequences, 3)
self.assertProtoEquals(expected_subsequence_1, subsequences[0])
self.assertProtoEquals(expected_subsequence_2, subsequences[1])
self.assertProtoEquals(expected_subsequence_3, subsequences[2])
def testSplitNoteSequenceSkipSplitsInsideNotes(self):
# Tests splitting a NoteSequence at regular hop size, skipping splits that
# would have occurred inside a note.
sequence = testing_lib.parse_test_proto(
music_pb2.NoteSequence,
"""
time_signatures: {
numerator: 4
denominator: 4}
tempos: {
qpm: 60}""")
testing_lib.add_track_to_sequence(
sequence, 0,
[(12, 100, 0.01, 3.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', 0.0), ('G7', 3.0), ('F', 4.5)])
expected_subsequence_1 = testing_lib.parse_test_proto(
music_pb2.NoteSequence,
"""
time_signatures: {
numerator: 4
denominator: 4}
tempos: {
qpm: 60}""")
testing_lib.add_track_to_sequence(
expected_subsequence_1, 0,
[(12, 100, 0.01, 3.0), (11, 55, 0.22, 0.50), (40, 45, 2.50, 3.50)])
testing_lib.add_chords_to_sequence(
expected_subsequence_1, [('C', 0.0), ('G7', 3.0)])
expected_subsequence_1.total_time = 3.50
expected_subsequence_1.subsequence_info.end_time_offset = 1.5
expected_subsequence_2 = testing_lib.parse_test_proto(
music_pb2.NoteSequence,
"""
time_signatures: {
numerator: 4
denominator: 4}
tempos: {
qpm: 60}""")
testing_lib.add_track_to_sequence(
expected_subsequence_2, 0,
[(55, 120, 0.0, 0.01), (52, 99, 0.75, 1.0)])
testing_lib.add_chords_to_sequence(
expected_subsequence_2, [('G7', 0.0), ('F', 0.5)])
expected_subsequence_2.total_time = 1.0
expected_subsequence_2.subsequence_info.start_time_offset = 4.0
subsequences = sequences_lib.split_note_sequence(
sequence, hop_size_seconds=2.0, skip_splits_inside_notes=True)
self.assertLen(subsequences, 2)
self.assertProtoEquals(expected_subsequence_1, subsequences[0])
self.assertProtoEquals(expected_subsequence_2, subsequences[1])
def testSplitNoteSequenceNoTimeChanges(self):
# Tests splitting a NoteSequence on time changes for a NoteSequence that has
# no time changes (time signature and tempo changes).
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)])
expected_subsequence = music_pb2.NoteSequence()
expected_subsequence.CopyFrom(sequence)
expected_subsequence.subsequence_info.start_time_offset = 0.0
expected_subsequence.subsequence_info.end_time_offset = 0.0
subsequences = sequences_lib.split_note_sequence_on_time_changes(sequence)
self.assertLen(subsequences, 1)
self.assertProtoEquals(expected_subsequence, subsequences[0])
def testSplitNoteSequenceDuplicateTimeChanges(self):
# Tests splitting a NoteSequence on time changes for a NoteSequence that has
# duplicate time changes.
sequence = testing_lib.parse_test_proto(
music_pb2.NoteSequence,
"""
time_signatures: {
numerator: 4
denominator: 4}
time_signatures: {
time: 2.0
numerator: 4
denominator: 4}
tempos: {
qpm: 60}""")
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)])
expected_subsequence = music_pb2.NoteSequence()
expected_subsequence.CopyFrom(sequence)
expected_subsequence.subsequence_info.start_time_offset = 0.0
expected_subsequence.subsequence_info.end_time_offset = 0.0
subsequences = sequences_lib.split_note_sequence_on_time_changes(sequence)
self.assertLen(subsequences, 1)
self.assertProtoEquals(expected_subsequence, subsequences[0])
def testSplitNoteSequenceCoincidentTimeChanges(self):
# Tests splitting a NoteSequence on time changes for a NoteSequence that has
# two time changes occurring simultaneously.
sequence = testing_lib.parse_test_proto(
music_pb2.NoteSequence,
"""
time_signatures: {
numerator: 4
denominator: 4}
time_signatures: {
time: 2.0
numerator: 3
denominator: 4}
tempos: {
qpm: 60}
tempos: {
time: 2.0
qpm: 80}""")
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)])
expected_subsequence_1 = testing_lib.parse_test_proto(
music_pb2.NoteSequence,
"""
time_signatures: {
numerator: 4
denominator: 4}
tempos: {
qpm: 60}""")
testing_lib.add_track_to_sequence(
expected_subsequence_1, 0,
[(12, 100, 0.01, 2.0), (11, 55, 0.22, 0.50)])
testing_lib.add_chords_to_sequence(
expected_subsequence_1, [('C', 1.5)])
expected_subsequence_1.total_time = 2.0
expected_subsequence_1.subsequence_info.end_time_offset = 8.0
expected_subsequence_2 = testing_lib.parse_test_proto(
music_pb2.NoteSequence,
"""
time_signatures: {
numerator: 3
denominator: 4}
tempos: {
qpm: 80}""")
testing_lib.add_track_to_sequence(
expected_subsequence_2, 0,
[(40, 45, 0.50, 1.50), (55, 120, 2.0, 2.01), (52, 99, 2.75, 3.0)])
testing_lib.add_chords_to_sequence(
expected_subsequence_2, [('C', 0.0), ('G7', 1.0), ('F', 2.8)])
expected_subsequence_2.total_time = 3.0
expected_subsequence_2.subsequence_info.start_time_offset = 2.0
expected_subsequence_2.subsequence_info.end_time_offset = 5.0
subsequences = sequences_lib.split_note_sequence_on_time_changes(sequence)
self.assertLen(subsequences, 2)
self.assertProtoEquals(expected_subsequence_1, subsequences[0])
self.assertProtoEquals(expected_subsequence_2, subsequences[1])
def testSplitNoteSequenceMultipleTimeChangesSkipSplitsInsideNotes(self):
# Tests splitting a NoteSequence on time changes skipping splits that occur
# inside notes.
sequence = testing_lib.parse_test_proto(
music_pb2.NoteSequence,
"""
time_signatures: {
numerator: 4
denominator: 4}
time_signatures: {
time: 2.0
numerator: 3
denominator: 4}
tempos: {
qpm: 60}
tempos: {
time: 4.25
qpm: 80}""")
testing_lib.add_track_to_sequence(
sequence, 0,
[(12, 100, 0.01, 3.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)])
expected_subsequence_1 = testing_lib.parse_test_proto(
music_pb2.NoteSequence,
"""
time_signatures: {
numerator: 4
denominator: 4}
time_signatures: {
time: 2.0
numerator: 3
denominator: 4}
tempos: {
qpm: 60}""")
testing_lib.add_track_to_sequence(
expected_subsequence_1, 0,
[(12, 100, 0.01, 3.0), (11, 55, 0.22, 0.50), (40, 45, 2.50, 3.50),
(55, 120, 4.0, 4.01)])
testing_lib.add_chords_to_sequence(
expected_subsequence_1, [('C', 1.5), ('G7', 3.0)])
expected_subsequence_1.total_time = 4.01
expected_subsequence_1.subsequence_info.end_time_offset = 0.99
expected_subsequence_2 = testing_lib.parse_test_proto(
music_pb2.NoteSequence,
"""
time_signatures: {
numerator: 3
denominator: 4}
tempos: {
qpm: 80}""")
testing_lib.add_track_to_sequence(
expected_subsequence_2, 0, [(52, 99, 0.5, 0.75)])
testing_lib.add_chords_to_sequence(expected_subsequence_2, [
('G7', 0.0), ('F', 0.55)])
expected_subsequence_2.total_time = 0.75
expected_subsequence_2.subsequence_info.start_time_offset = 4.25
subsequences = sequences_lib.split_note_sequence_on_time_changes(
sequence, skip_splits_inside_notes=True)
self.assertLen(subsequences, 2)
self.assertProtoEquals(expected_subsequence_1, subsequences[0])
self.assertProtoEquals(expected_subsequence_2, subsequences[1])
def testSplitNoteSequenceMultipleTimeChanges(self):
# Tests splitting a NoteSequence on time changes, truncating notes on splits
# that occur inside notes.
sequence = testing_lib.parse_test_proto(
music_pb2.NoteSequence,
"""
time_signatures: {
numerator: 4
denominator: 4}
time_signatures: {
time: 2.0
numerator: 3
denominator: 4}
tempos: {
qpm: 60}
tempos: {
time: 4.25
qpm: 80}""")
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)])
expected_subsequence_1 = testing_lib.parse_test_proto(
music_pb2.NoteSequence,
"""
time_signatures: {
numerator: 4
denominator: 4}
tempos: {
qpm: 60}""")
testing_lib.add_track_to_sequence(
expected_subsequence_1, 0,
[(12, 100, 0.01, 2.0), (11, 55, 0.22, 0.50)])
testing_lib.add_chords_to_sequence(
expected_subsequence_1, [('C', 1.5)])
expected_subsequence_1.total_time = 2.0
expected_subsequence_1.subsequence_info.end_time_offset = 8.0
expected_subsequence_2 = testing_lib.parse_test_proto(
music_pb2.NoteSequence,
"""
time_signatures: {
numerator: 3
denominator: 4}
tempos: {
qpm: 60}""")
testing_lib.add_track_to_sequence(
expected_subsequence_2, 0,
[(40, 45, 0.50, 1.50), (55, 120, 2.0, 2.01)])
testing_lib.add_chords_to_sequence(
expected_subsequence_2, [('C', 0.0), ('G7', 1.0)])
expected_subsequence_2.total_time = 2.01
expected_subsequence_2.subsequence_info.start_time_offset = 2.0
expected_subsequence_2.subsequence_info.end_time_offset = 5.99
expected_subsequence_3 = testing_lib.parse_test_proto(
music_pb2.NoteSequence,
"""
time_signatures: {
numerator: 3
denominator: 4}
tempos: {
qpm: 80}""")
testing_lib.add_track_to_sequence(
expected_subsequence_3, 0,
[(52, 99, 0.5, 0.75)])
testing_lib.add_chords_to_sequence(
expected_subsequence_3, [('G7', 0.0), ('F', 0.55)])
expected_subsequence_3.total_time = 0.75
expected_subsequence_3.subsequence_info.start_time_offset = 4.25
expected_subsequence_3.subsequence_info.end_time_offset = 5.0
subsequences = sequences_lib.split_note_sequence_on_time_changes(sequence)
self.assertLen(subsequences, 3)
self.assertProtoEquals(expected_subsequence_1, subsequences[0])
self.assertProtoEquals(expected_subsequence_2, subsequences[1])
self.assertProtoEquals(expected_subsequence_3, subsequences[2])
def testSplitNoteSequenceWithStatelessEvents(self):
# Tests splitting a NoteSequence at specified times with stateless events.
sequence = testing_lib.parse_test_proto(
music_pb2.NoteSequence,
"""
time_signatures: {
numerator: 4
denominator: 4}
tempos: {
qpm: 60}""")
testing_lib.add_track_to_sequence(
sequence, 0,
[(12, 100, 0.01, 8.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_beats_to_sequence(sequence, [1.0, 2.0, 4.0])
expected_subsequence_1 = testing_lib.parse_test_proto(
music_pb2.NoteSequence,
"""
time_signatures: {
numerator: 4
denominator: 4}
tempos: {
qpm: 60}""")
testing_lib.add_track_to_sequence(
expected_subsequence_1, 0,
[(12, 100, 0.01, 3.0), (11, 55, 0.22, 0.50), (40, 45, 2.50, 3.0)])
testing_lib.add_beats_to_sequence(expected_subsequence_1, [1.0, 2.0])
expected_subsequence_1.total_time = 3.0
expected_subsequence_1.subsequence_info.end_time_offset = 5.0
expected_subsequence_2 = testing_lib.parse_test_proto(
music_pb2.NoteSequence,
"""
time_signatures: {
numerator: 4
denominator: 4}
tempos: {
qpm: 60}""")
expected_subsequence_2.total_time = 0.0
expected_subsequence_2.subsequence_info.start_time_offset = 3.0
expected_subsequence_2.subsequence_info.end_time_offset = 5.0
expected_subsequence_3 = testing_lib.parse_test_proto(
music_pb2.NoteSequence,
"""
time_signatures: {
numerator: 4
denominator: 4}
tempos: {
qpm: 60}""")
testing_lib.add_track_to_sequence(
expected_subsequence_3, 0,
[(55, 120, 0.0, 0.01), (52, 99, 0.75, 1.0)])
testing_lib.add_beats_to_sequence(expected_subsequence_3, [0.0])
expected_subsequence_3.total_time = 1.0
expected_subsequence_3.subsequence_info.start_time_offset = 4.0
expected_subsequence_3.subsequence_info.end_time_offset = 3.0
subsequences = sequences_lib.split_note_sequence(
sequence, hop_size_seconds=[3.0, 4.0])
self.assertLen(subsequences, 3)
self.assertProtoEquals(expected_subsequence_1, subsequences[0])
self.assertProtoEquals(expected_subsequence_2, subsequences[1])
self.assertProtoEquals(expected_subsequence_3, subsequences[2])
def testSplitNoteSequenceOnSilence(self):
sequence = music_pb2.NoteSequence()
testing_lib.add_track_to_sequence(
sequence, 0,
[(12, 100, 0.01, 1.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)])
expected_subsequence_1 = music_pb2.NoteSequence()
testing_lib.add_track_to_sequence(
expected_subsequence_1, 0,
[(12, 100, 0.01, 1.0), (11, 55, 0.22, 0.50)])
expected_subsequence_1.total_time = 1.0
expected_subsequence_1.subsequence_info.end_time_offset = 4.0
expected_subsequence_2 = music_pb2.NoteSequence()
testing_lib.add_track_to_sequence(
expected_subsequence_2, 0,
[(40, 45, 0.0, 1.0), (55, 120, 1.50, 1.51)])
expected_subsequence_2.total_time = 1.51
expected_subsequence_2.subsequence_info.start_time_offset = 2.50
expected_subsequence_2.subsequence_info.end_time_offset = 0.99
expected_subsequence_3 = music_pb2.NoteSequence()
testing_lib.add_track_to_sequence(
expected_subsequence_3, 0,
[(52, 99, 0.0, 0.25)])
expected_subsequence_3.total_time = 0.25
expected_subsequence_3.subsequence_info.start_time_offset = 4.75
subsequences = sequences_lib.split_note_sequence_on_silence(
sequence, gap_seconds=0.5)
self.assertLen(subsequences, 3)
self.assertProtoEquals(expected_subsequence_1, subsequences[0])
self.assertProtoEquals(expected_subsequence_2, subsequences[1])
self.assertProtoEquals(expected_subsequence_3, subsequences[2])
def testSplitNoteSequenceOnSilenceInitialGap(self):
sequence = music_pb2.NoteSequence()
testing_lib.add_track_to_sequence(
sequence, 0,
[(12, 100, 1.5, 2.0), (11, 55, 1.5, 3.0), (40, 45, 2.5, 3.5)])
expected_subsequence_1 = music_pb2.NoteSequence()
expected_subsequence_1.total_time = 0.0
expected_subsequence_1.subsequence_info.end_time_offset = 3.5
expected_subsequence_2 = music_pb2.NoteSequence()
testing_lib.add_track_to_sequence(
expected_subsequence_2, 0,
[(12, 100, 0.0, 0.5), (11, 55, 0.0, 1.5), (40, 45, 1.0, 2.0)])
expected_subsequence_2.total_time = 2.0
expected_subsequence_2.subsequence_info.start_time_offset = 1.5
subsequences = sequences_lib.split_note_sequence_on_silence(
sequence, gap_seconds=1.0)
self.assertLen(subsequences, 2)
self.assertProtoEquals(expected_subsequence_1, subsequences[0])
self.assertProtoEquals(expected_subsequence_2, subsequences[1])
def testQuantizeNoteSequence(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_quarter = (
self.steps_per_quarter)
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(
self.note_sequence, steps_per_quarter=self.steps_per_quarter)
self.assertProtoEquals(expected_quantized_sequence, quantized_sequence)
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 testAssertIsQuantizedNoteSequence(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)])
relative_quantized_sequence = sequences_lib.quantize_note_sequence(
self.note_sequence, steps_per_quarter=self.steps_per_quarter)
absolute_quantized_sequence = sequences_lib.quantize_note_sequence_absolute(
self.note_sequence, steps_per_second=4)
sequences_lib.assert_is_quantized_sequence(relative_quantized_sequence)
sequences_lib.assert_is_quantized_sequence(absolute_quantized_sequence)
with self.assertRaises(sequences_lib.QuantizationStatusError): # pylint:disable=g-error-prone-assert-raises
sequences_lib.assert_is_quantized_sequence(self.note_sequence)
def testAssertIsRelativeQuantizedNoteSequence(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)])
relative_quantized_sequence = sequences_lib.quantize_note_sequence(
self.note_sequence, steps_per_quarter=self.steps_per_quarter)
absolute_quantized_sequence = sequences_lib.quantize_note_sequence_absolute(
self.note_sequence, steps_per_second=4)
sequences_lib.assert_is_relative_quantized_sequence(
relative_quantized_sequence)
with self.assertRaises(sequences_lib.QuantizationStatusError): # pylint:disable=g-error-prone-assert-raises
sequences_lib.assert_is_relative_quantized_sequence(
absolute_quantized_sequence)
with self.assertRaises(sequences_lib.QuantizationStatusError): # pylint:disable=g-error-prone-assert-raises
sequences_lib.assert_is_relative_quantized_sequence(self.note_sequence)
def testQuantizeNoteSequence_TimeSignatureChange(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)])
del self.note_sequence.time_signatures[:]
sequences_lib.quantize_note_sequence(
self.note_sequence, self.steps_per_quarter)
# Single time signature.
self.note_sequence.time_signatures.add(numerator=4, denominator=4, time=0)
sequences_lib.quantize_note_sequence(
self.note_sequence, self.steps_per_quarter)
# Multiple time signatures with no change.
self.note_sequence.time_signatures.add(numerator=4, denominator=4, time=1)
sequences_lib.quantize_note_sequence(
self.note_sequence, self.steps_per_quarter)
# Time signature change.
self.note_sequence.time_signatures.add(numerator=2, denominator=4, time=2)
with self.assertRaises(sequences_lib.MultipleTimeSignatureError):
sequences_lib.quantize_note_sequence(
self.note_sequence, self.steps_per_quarter)
def testQuantizeNoteSequence_ImplicitTimeSignatureChange(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)])
del self.note_sequence.time_signatures[:]
# No time signature.
sequences_lib.quantize_note_sequence(
self.note_sequence, self.steps_per_quarter)
# Implicit time signature change.
self.note_sequence.time_signatures.add(numerator=2, denominator=4, time=2)
with self.assertRaises(sequences_lib.MultipleTimeSignatureError):
sequences_lib.quantize_note_sequence(
self.note_sequence, self.steps_per_quarter)
def testQuantizeNoteSequence_NoImplicitTimeSignatureChangeOutOfOrder(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)])
del self.note_sequence.time_signatures[:]
# No time signature.
sequences_lib.quantize_note_sequence(
self.note_sequence, self.steps_per_quarter)
# No implicit time signature change, but time signatures are added out of
# order.
self.note_sequence.time_signatures.add(numerator=2, denominator=4, time=2)
self.note_sequence.time_signatures.add(numerator=2, denominator=4, time=0)
sequences_lib.quantize_note_sequence(
self.note_sequence, self.steps_per_quarter)
def testStepsPerQuarterToStepsPerSecond(self):
self.assertEqual(
4.0, sequences_lib.steps_per_quarter_to_steps_per_second(4, 60.0))
def testQuantizeToStep(self):
self.assertEqual(
32, sequences_lib.quantize_to_step(8.0001, 4))
self.assertEqual(
34, sequences_lib.quantize_to_step(8.4999, 4))
self.assertEqual(
33, sequences_lib.quantize_to_step(8.4999, 4, quantize_cutoff=1.0))
def testFromNoteSequence_TempoChange(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)])
del self.note_sequence.tempos[:]
# No tempos.
sequences_lib.quantize_note_sequence(
self.note_sequence, self.steps_per_quarter)
# Single tempo.
self.note_sequence.tempos.add(qpm=60, time=0)
sequences_lib.quantize_note_sequence(
self.note_sequence, self.steps_per_quarter)
# Multiple tempos with no change.
self.note_sequence.tempos.add(qpm=60, time=1)
sequences_lib.quantize_note_sequence(
self.note_sequence, self.steps_per_quarter)
# Tempo change.
self.note_sequence.tempos.add(qpm=120, time=2)
with self.assertRaises(sequences_lib.MultipleTempoError):
sequences_lib.quantize_note_sequence(
self.note_sequence, self.steps_per_quarter)
def testFromNoteSequence_ImplicitTempoChange(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)])
del self.note_sequence.tempos[:]
# No tempo.
sequences_lib.quantize_note_sequence(
self.note_sequence, self.steps_per_quarter)
# Implicit tempo change.
self.note_sequence.tempos.add(qpm=60, time=2)
with self.assertRaises(sequences_lib.MultipleTempoError):
sequences_lib.quantize_note_sequence(
self.note_sequence, self.steps_per_quarter)
def testFromNoteSequence_NoImplicitTempoChangeOutOfOrder(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)])
del self.note_sequence.tempos[:]
# No tempo.
sequences_lib.quantize_note_sequence(
self.note_sequence, self.steps_per_quarter)
# No implicit tempo change, but tempos are added out of order.
self.note_sequence.tempos.add(qpm=60, time=2)
self.note_sequence.tempos.add(qpm=60, time=0)
sequences_lib.quantize_note_sequence(
self.note_sequence, self.steps_per_quarter)
def testRounding(self):
testing_lib.add_track_to_sequence(
self.note_sequence, 1,
[(12, 100, 0.01, 0.24), (11, 100, 0.22, 0.55), (40, 100, 0.50, 0.75),
(41, 100, 0.689, 1.18), (44, 100, 1.19, 1.69), (55, 100, 4.0, 4.01)])
expected_quantized_sequence = copy.deepcopy(self.note_sequence)
expected_quantized_sequence.quantization_info.steps_per_quarter = (
self.steps_per_quarter)
testing_lib.add_quantized_steps_to_sequence(
expected_quantized_sequence,
[(0, 1), (1, 2), (2, 3), (3, 5), (5, 7), (16, 17)])
quantized_sequence = sequences_lib.quantize_note_sequence(
self.note_sequence, self.steps_per_quarter)
self.assertProtoEquals(expected_quantized_sequence, quantized_sequence)
def testMultiTrack(self):
testing_lib.add_track_to_sequence(
self.note_sequence, 0,
[(12, 100, 1.0, 4.0), (19, 100, 0.95, 3.0)])
testing_lib.add_track_to_sequence(
self.note_sequence, 3,
[(12, 100, 1.0, 4.0), (19, 100, 2.0, 5.0)])
testing_lib.add_track_to_sequence(
self.note_sequence, 7,
[(12, 100, 1.0, 5.0), (19, 100, 2.0, 4.0), (24, 100, 3.0, 3.5)])
expected_quantized_sequence = copy.deepcopy(self.note_sequence)
expected_quantized_sequence.quantization_info.steps_per_quarter = (
self.steps_per_quarter)
testing_lib.add_quantized_steps_to_sequence(
expected_quantized_sequence,
[(4, 16), (4, 12), (4, 16), (8, 20), (4, 20), (8, 16), (12, 14)])
quantized_sequence = sequences_lib.quantize_note_sequence(
self.note_sequence, self.steps_per_quarter)
self.assertProtoEquals(expected_quantized_sequence, quantized_sequence)
def testStepsPerBar(self):
qns = sequences_lib.quantize_note_sequence(
self.note_sequence, self.steps_per_quarter)
self.assertEqual(16, sequences_lib.steps_per_bar_in_quantized_sequence(qns))
self.note_sequence.time_signatures[0].numerator = 6
self.note_sequence.time_signatures[0].denominator = 8
qns = sequences_lib.quantize_note_sequence(
self.note_sequence, self.steps_per_quarter)
self.assertEqual(12.0,
sequences_lib.steps_per_bar_in_quantized_sequence(qns))
def testStretchNoteSequence(self):
expected_stretched_sequence = copy.deepcopy(self.note_sequence)
expected_stretched_sequence.tempos[0].qpm = 40
testing_lib.add_track_to_sequence(
self.note_sequence, 0,
[(12, 100, 0.0, 10.0), (11, 55, 0.2, 0.5), (40, 45, 2.5, 3.5)])
testing_lib.add_track_to_sequence(
expected_stretched_sequence, 0,
[(12, 100, 0.0, 15.0), (11, 55, 0.3, 0.75), (40, 45, 3.75, 5.25)])
testing_lib.add_chords_to_sequence(
self.note_sequence, [('B7', 0.5), ('Em9', 2.0)])
testing_lib.add_chords_to_sequence(
expected_stretched_sequence, [('B7', 0.75), ('Em9', 3.0)])
prestretched_sequence = copy.deepcopy(self.note_sequence)
stretched_sequence = sequences_lib.stretch_note_sequence(
self.note_sequence, stretch_factor=1.5, in_place=False)
self.assertProtoEquals(expected_stretched_sequence, stretched_sequence)
# Make sure the proto was not modified
self.assertProtoEquals(prestretched_sequence, self.note_sequence)
sequences_lib.stretch_note_sequence(
self.note_sequence, stretch_factor=1.5, in_place=True)
self.assertProtoEquals(stretched_sequence, self.note_sequence)
def testAdjustNoteSequenceTimes(self):
sequence = music_pb2.NoteSequence()
sequence.notes.add(pitch=60, start_time=1.0, end_time=5.0)
sequence.notes.add(pitch=61, start_time=6.0, end_time=7.0)
sequence.control_changes.add(control_number=1, time=2.0)
sequence.pitch_bends.add(bend=5, time=2.0)
sequence.total_time = 7.0
adjusted_ns, skipped_notes = sequences_lib.adjust_notesequence_times(
sequence, lambda t: t - 1)
expected_sequence = music_pb2.NoteSequence()
expected_sequence.notes.add(pitch=60, start_time=0.0, end_time=4.0)
expected_sequence.notes.add(pitch=61, start_time=5.0, end_time=6.0)
expected_sequence.control_changes.add(control_number=1, time=1.0)
expected_sequence.pitch_bends.add(bend=5, time=1.0)
expected_sequence.total_time = 6.0
self.assertEqual(expected_sequence, adjusted_ns)
self.assertEqual(0, skipped_notes)
def testAdjustNoteSequenceTimesWithSkippedNotes(self):
sequence = music_pb2.NoteSequence()
sequence.notes.add(pitch=60, start_time=1.0, end_time=5.0)
sequence.notes.add(pitch=61, start_time=6.0, end_time=7.0)
sequence.notes.add(pitch=62, start_time=7.0, end_time=8.0)
sequence.total_time = 8.0
def time_func(time):
if time > 5:
return 5
else:
return time
adjusted_ns, skipped_notes = sequences_lib.adjust_notesequence_times(
sequence, time_func)
expected_sequence = music_pb2.NoteSequence()
expected_sequence.notes.add(pitch=60, start_time=1.0, end_time=5.0)
expected_sequence.total_time = 5.0
self.assertEqual(expected_sequence, adjusted_ns)
self.assertEqual(2, skipped_notes)
def testAdjustNoteSequenceTimesWithNotesBeforeTimeZero(self):
sequence = music_pb2.NoteSequence()
sequence.notes.add(pitch=60, start_time=1.0, end_time=5.0)
sequence.notes.add(pitch=61, start_time=6.0, end_time=7.0)
sequence.notes.add(pitch=62, start_time=7.0, end_time=8.0)
sequence.total_time = 8.0
def time_func(time):
return time - 5
with self.assertRaises(sequences_lib.InvalidTimeAdjustmentError):
sequences_lib.adjust_notesequence_times(sequence, time_func)
def testAdjustNoteSequenceTimesWithZeroDurations(self):
sequence = music_pb2.NoteSequence()
sequence.notes.add(pitch=60, start_time=1.0, end_time=2.0)
sequence.notes.add(pitch=61, start_time=3.0, end_time=4.0)
sequence.notes.add(pitch=62, start_time=5.0, end_time=6.0)
sequence.total_time = 8.0
def time_func(time):
if time % 2 == 0:
return time - 1
else:
return time
adjusted_ns, skipped_notes = sequences_lib.adjust_notesequence_times(
sequence, time_func)
expected_sequence = music_pb2.NoteSequence()
self.assertEqual(expected_sequence, adjusted_ns)
self.assertEqual(3, skipped_notes)
adjusted_ns, skipped_notes = sequences_lib.adjust_notesequence_times(
sequence, time_func, minimum_duration=.1)
expected_sequence = music_pb2.NoteSequence()
expected_sequence.notes.add(pitch=60, start_time=1.0, end_time=1.1)
expected_sequence.notes.add(pitch=61, start_time=3.0, end_time=3.1)
expected_sequence.notes.add(pitch=62, start_time=5.0, end_time=5.1)
expected_sequence.total_time = 5.1
self.assertEqual(expected_sequence, adjusted_ns)
self.assertEqual(0, skipped_notes)
def testAdjustNoteSequenceTimesEndBeforeStart(self):
sequence = music_pb2.NoteSequence()
sequence.notes.add(pitch=60, start_time=1.0, end_time=2.0)
sequence.notes.add(pitch=61, start_time=3.0, end_time=4.0)
sequence.notes.add(pitch=62, start_time=5.0, end_time=6.0)
sequence.total_time = 8.0
def time_func(time):
if time % 2 == 0:
return time - 2
else:
return time
with self.assertRaises(sequences_lib.InvalidTimeAdjustmentError):
sequences_lib.adjust_notesequence_times(sequence, time_func)
def testRectifyBeats(self):
sequence = music_pb2.NoteSequence()
testing_lib.add_track_to_sequence(
sequence, 0,
[(60, 100, 0.25, 0.5), (62, 100, 0.5, 0.75), (64, 100, 0.75, 2.5),
(65, 100, 1.0, 1.5), (67, 100, 1.5, 2.0)])
testing_lib.add_beats_to_sequence(sequence, [0.5, 1.0, 2.0])
rectified_sequence, alignment = sequences_lib.rectify_beats(
sequence, 120)
expected_sequence = music_pb2.NoteSequence()
expected_sequence.tempos.add(qpm=120)
testing_lib.add_track_to_sequence(
expected_sequence, 0,
[(60, 100, 0.25, 0.5), (62, 100, 0.5, 0.75), (64, 100, 0.75, 2.0),
(65, 100, 1.0, 1.25), (67, 100, 1.25, 1.5)])
testing_lib.add_beats_to_sequence(expected_sequence, [0.5, 1.0, 1.5])
self.assertEqual(expected_sequence, rectified_sequence)
expected_alignment = [
[0.0, 0.5, 1.0, 2.0, 2.5],
[0.0, 0.5, 1.0, 1.5, 2.0]
]
self.assertEqual(expected_alignment, alignment.T.tolist())
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 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 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 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 testApplySustainControlChangesWithDrumNotes(self):
"""Drum notes should not be modified when applying sustain changes."""
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)])
testing_lib.add_track_to_sequence(
sequence, 0,
[(38, 100, 2.00, 2.50)], is_drum=True)
testing_lib.add_track_to_sequence(
expected_sequence, 0,
[(60, 100, 2.00, 4.00)])
testing_lib.add_track_to_sequence(
expected_sequence, 0,
[(38, 100, 2.0, 2.5)], is_drum=True)
sus_sequence = sequences_lib.apply_sustain_control_changes(sequence)
self.assertProtoEquals(expected_sequence, sus_sequence)
def testApplySustainControlChangesProcessSustainBeforeNotes(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)])
expected_sequence = copy.copy(sequence)
testing_lib.add_track_to_sequence(
sequence, 0,
[(11, 55, 0.22, 0.75), (40, 45, 2.50, 3.50)])
testing_lib.add_track_to_sequence(
expected_sequence, 0,
[(11, 55, 0.22, 0.75), (40, 45, 2.50, 3.50)])
sus_sequence = sequences_lib.apply_sustain_control_changes(sequence)
self.assertProtoEquals(expected_sequence, sus_sequence)
def testInferDenseChordsForSequence(self):
# Test non-quantized sequence.
sequence = copy.copy(self.note_sequence)
testing_lib.add_track_to_sequence(
sequence, 0,
[(60, 100, 1.0, 3.0), (64, 100, 1.0, 2.0), (67, 100, 1.0, 2.0),
(65, 100, 2.0, 3.0), (69, 100, 2.0, 3.0),
(62, 100, 3.0, 5.0), (65, 100, 3.0, 4.0), (69, 100, 3.0, 4.0)])
expected_sequence = copy.copy(sequence)
testing_lib.add_chords_to_sequence(
expected_sequence, [('C', 1.0), ('F/C', 2.0), ('Dm', 3.0)])
sequences_lib.infer_dense_chords_for_sequence(sequence)
self.assertProtoEquals(expected_sequence, sequence)
# Test quantized sequence.
sequence = copy.copy(self.note_sequence)
sequence.quantization_info.steps_per_quarter = 1
testing_lib.add_track_to_sequence(
sequence, 0,
[(60, 100, 1.1, 3.0), (64, 100, 1.0, 1.9), (67, 100, 1.0, 2.0),
(65, 100, 2.0, 3.2), (69, 100, 2.1, 3.1),
(62, 100, 2.9, 4.8), (65, 100, 3.0, 4.0), (69, 100, 3.0, 4.1)])
testing_lib.add_quantized_steps_to_sequence(
sequence,
[(1, 3), (1, 2), (1, 2), (2, 3), (2, 3), (3, 5), (3, 4), (3, 4)])
expected_sequence = copy.copy(sequence)
testing_lib.add_chords_to_sequence(
expected_sequence, [('C', 1.0), ('F/C', 2.0), ('Dm', 3.0)])
testing_lib.add_quantized_chord_steps_to_sequence(
expected_sequence, [1, 2, 3])
sequences_lib.infer_dense_chords_for_sequence(sequence)
self.assertProtoEquals(expected_sequence, 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)
def testConcatenateSequences(self):
sequence1 = copy.copy(self.note_sequence)
testing_lib.add_track_to_sequence(
sequence1, 0,
[(60, 100, 0.0, 1.0), (72, 100, 0.5, 1.5)])
sequence2 = copy.copy(self.note_sequence)
testing_lib.add_track_to_sequence(
sequence2, 0,
[(59, 100, 0.0, 1.0), (71, 100, 0.5, 1.5)])
expected_sequence = copy.copy(self.note_sequence)
testing_lib.add_track_to_sequence(
expected_sequence, 0,
[(60, 100, 0.0, 1.0), (72, 100, 0.5, 1.5),
(59, 100, 1.5, 2.5), (71, 100, 2.0, 3.0)])
cat_seq = sequences_lib.concatenate_sequences([sequence1, sequence2])
self.assertProtoEquals(expected_sequence, cat_seq)
def testConcatenateSequencesWithSpecifiedDurations(self):
sequence1 = copy.copy(self.note_sequence)
testing_lib.add_track_to_sequence(
sequence1, 0, [(60, 100, 0.0, 1.0), (72, 100, 0.5, 1.5)])
sequence2 = copy.copy(self.note_sequence)
testing_lib.add_track_to_sequence(
sequence2, 0,
[(59, 100, 0.0, 1.0)])
sequence3 = copy.copy(self.note_sequence)
testing_lib.add_track_to_sequence(
sequence3, 0,
[(72, 100, 0.0, 1.0), (73, 100, 0.5, 1.5)])
expected_sequence = copy.copy(self.note_sequence)
testing_lib.add_track_to_sequence(
expected_sequence, 0,
[(60, 100, 0.0, 1.0), (72, 100, 0.5, 1.5),
(59, 100, 2.0, 3.0),
(72, 100, 3.5, 4.5), (73, 100, 4.0, 5.0)])
cat_seq = sequences_lib.concatenate_sequences(
[sequence1, sequence2, sequence3],
sequence_durations=[2, 1.5, 2])
self.assertProtoEquals(expected_sequence, cat_seq)
def testRepeatSequenceToDuration(self):
sequence = copy.copy(self.note_sequence)
testing_lib.add_track_to_sequence(
sequence, 0,
[(60, 100, 0.0, 1.0), (72, 100, 0.5, 1.5)])
expected_sequence = copy.copy(self.note_sequence)
testing_lib.add_track_to_sequence(
expected_sequence, 0,
[(60, 100, 0.0, 1.0), (72, 100, 0.5, 1.5),
(60, 100, 1.5, 2.5), (72, 100, 2.0, 3.0)])
repeated_seq = sequences_lib.repeat_sequence_to_duration(
sequence, duration=3)
self.assertProtoEquals(expected_sequence, repeated_seq)
def testRepeatSequenceToDurationProvidedDuration(self):
sequence = copy.copy(self.note_sequence)
testing_lib.add_track_to_sequence(
sequence, 0,
[(60, 100, 0.0, 1.0), (72, 100, 0.5, 1.5)])
expected_sequence = copy.copy(self.note_sequence)
testing_lib.add_track_to_sequence(
expected_sequence, 0,
[(60, 100, 0.0, 1.0), (72, 100, 0.5, 1.5),
(60, 100, 2.0, 3.0), (72, 100, 2.5, 3.0)])
repeated_seq = sequences_lib.repeat_sequence_to_duration(
sequence, duration=3, sequence_duration=2)
self.assertProtoEquals(expected_sequence, repeated_seq)
def testRemoveRedundantData(self):
sequence = copy.copy(self.note_sequence)
redundant_tempo = sequence.tempos.add()
redundant_tempo.CopyFrom(sequence.tempos[0])
redundant_tempo.time = 5.0
sequence.sequence_metadata.composers.append('Foo')
sequence.sequence_metadata.composers.append('Bar')
sequence.sequence_metadata.composers.append('Foo')
sequence.sequence_metadata.composers.append('Bar')
sequence.sequence_metadata.genre.append('Classical')
sequence.sequence_metadata.genre.append('Classical')
fixed_sequence = sequences_lib.remove_redundant_data(sequence)
expected_sequence = copy.copy(self.note_sequence)
expected_sequence.sequence_metadata.composers.append('Foo')
expected_sequence.sequence_metadata.composers.append('Bar')
expected_sequence.sequence_metadata.genre.append('Classical')
self.assertProtoEquals(expected_sequence, fixed_sequence)
def testRemoveRedundantDataOutOfOrder(self):
sequence = copy.copy(self.note_sequence)
meaningful_tempo = sequence.tempos.add()
meaningful_tempo.time = 5.0
meaningful_tempo.qpm = 50
redundant_tempo = sequence.tempos.add()
redundant_tempo.CopyFrom(sequence.tempos[0])
expected_sequence = copy.copy(self.note_sequence)
expected_meaningful_tempo = expected_sequence.tempos.add()
expected_meaningful_tempo.time = 5.0
expected_meaningful_tempo.qpm = 50
fixed_sequence = sequences_lib.remove_redundant_data(sequence)
self.assertProtoEquals(expected_sequence, fixed_sequence)
def testExpandSectionGroups(self):
sequence = copy.copy(self.note_sequence)
testing_lib.add_track_to_sequence(
sequence, 0,
[(60, 100, 0.0, 1.0), (72, 100, 1.0, 2.0),
(59, 100, 2.0, 3.0), (71, 100, 3.0, 4.0)])
sequence.section_annotations.add(time=0, section_id=0)
sequence.section_annotations.add(time=1, section_id=1)
sequence.section_annotations.add(time=2, section_id=2)
sequence.section_annotations.add(time=3, section_id=3)
# A((BC)2D)2
sg = sequence.section_groups.add()
sg.sections.add(section_id=0)
sg.num_times = 1
sg = sequence.section_groups.add()
sg.sections.add(section_group=music_pb2.NoteSequence.SectionGroup(
sections=[music_pb2.NoteSequence.Section(section_id=1),
music_pb2.NoteSequence.Section(section_id=2)],
num_times=2))
sg.sections.add(section_id=3)
sg.num_times = 2
expanded = sequences_lib.expand_section_groups(sequence)
expected = copy.copy(self.note_sequence)
testing_lib.add_track_to_sequence(
expected, 0,
[(60, 100, 0.0, 1.0),
(72, 100, 1.0, 2.0),
(59, 100, 2.0, 3.0),
(72, 100, 3.0, 4.0),
(59, 100, 4.0, 5.0),
(71, 100, 5.0, 6.0),
(72, 100, 6.0, 7.0),
(59, 100, 7.0, 8.0),
(72, 100, 8.0, 9.0),
(59, 100, 9.0, 10.0),
(71, 100, 10.0, 11.0)])
expected.section_annotations.add(time=0, section_id=0)
expected.section_annotations.add(time=1, section_id=1)
expected.section_annotations.add(time=2, section_id=2)
expected.section_annotations.add(time=3, section_id=1)
expected.section_annotations.add(time=4, section_id=2)
expected.section_annotations.add(time=5, section_id=3)
expected.section_annotations.add(time=6, section_id=1)
expected.section_annotations.add(time=7, section_id=2)
expected.section_annotations.add(time=8, section_id=1)
expected.section_annotations.add(time=9, section_id=2)
expected.section_annotations.add(time=10, section_id=3)
self.assertProtoEquals(expected, expanded)
def testExpandWithoutSectionGroups(self):
sequence = copy.copy(self.note_sequence)
testing_lib.add_track_to_sequence(
sequence, 0,
[(60, 100, 0.0, 1.0), (72, 100, 1.0, 2.0),
(59, 100, 2.0, 3.0), (71, 100, 3.0, 4.0)])
sequence.section_annotations.add(time=0, section_id=0)
sequence.section_annotations.add(time=1, section_id=1)
sequence.section_annotations.add(time=2, section_id=2)
sequence.section_annotations.add(time=3, section_id=3)
expanded = sequences_lib.expand_section_groups(sequence)
self.assertEqual(sequence, expanded)
def testSequenceToPianoroll(self):
sequence = music_pb2.NoteSequence(total_time=1.21)
testing_lib.add_track_to_sequence(sequence, 0, [(1, 100, 0.11, 1.01),
(2, 55, 0.22, 0.50),
(3, 100, 0.3, 0.8),
(2, 45, 1.0, 1.21)])
pianoroll_tuple = sequences_lib.sequence_to_pianoroll(
sequence, frames_per_second=10, min_pitch=1, max_pitch=2)
output = pianoroll_tuple.active
offset = pianoroll_tuple.offsets
expected_pianoroll = [[0, 0],
[1, 0],
[1, 1],
[1, 1],
[1, 1],
[1, 0],
[1, 0],
[1, 0],
[1, 0],
[1, 0],
[1, 1],
[0, 1],
[0, 1]]
expected_offsets = [[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 1],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[1, 0],
[0, 0],
[0, 1]]
np.testing.assert_allclose(expected_pianoroll, output)
np.testing.assert_allclose(expected_offsets, offset)
def testSequenceToPianorollWithBlankFrameBeforeOffset(self):
sequence = music_pb2.NoteSequence(total_time=1.5)
testing_lib.add_track_to_sequence(sequence, 0, [(1, 100, 0.00, 1.00),
(2, 100, 0.20, 0.50),
(1, 100, 1.20, 1.50),
(2, 100, 0.50, 1.50)])
expected_pianoroll = [
[1, 0],
[1, 0],
[1, 1],
[1, 1],
[1, 1],
[1, 1],
[1, 1],
[1, 1],
[1, 1],
[1, 1],
[0, 1],
[0, 1],
[1, 1],
[1, 1],
[1, 1],
[0, 0],
]
output = sequences_lib.sequence_to_pianoroll(
sequence, frames_per_second=10, min_pitch=1, max_pitch=2).active
np.testing.assert_allclose(expected_pianoroll, output)
expected_pianoroll_with_blank_frame = [
[1, 0],
[1, 0],
[1, 1],
[1, 1],
[1, 0],
[1, 1],
[1, 1],
[1, 1],
[1, 1],
[1, 1],
[0, 1],
[0, 1],
[1, 1],
[1, 1],
[1, 1],
[0, 0],
]
output_with_blank_frame = sequences_lib.sequence_to_pianoroll(
sequence,
frames_per_second=10,
min_pitch=1,
max_pitch=2,
add_blank_frame_before_onset=True).active
np.testing.assert_allclose(expected_pianoroll_with_blank_frame,
output_with_blank_frame)
def testSequenceToPianorollWithBlankFrameBeforeOffsetOutOfOrder(self):
sequence = music_pb2.NoteSequence(total_time=.5)
testing_lib.add_track_to_sequence(sequence, 0, [(1, 100, 0.20, 0.50),
(1, 100, 0.00, 0.20)])
expected_pianoroll = [
[1],
[0],
[1],
[1],
[1],
[0],
]
output = sequences_lib.sequence_to_pianoroll(
sequence,
frames_per_second=10,
min_pitch=1,
max_pitch=1,
add_blank_frame_before_onset=True).active
np.testing.assert_allclose(expected_pianoroll, output)
def testSequenceToPianorollWeightedRoll(self):
sequence = music_pb2.NoteSequence(total_time=2.0)
testing_lib.add_track_to_sequence(sequence, 0, [(1, 100, 0.00, 1.00),
(2, 100, 0.20, 0.50),
(3, 100, 1.20, 1.50),
(4, 100, 0.40, 2.00),
(6, 100, 0.10, 0.60)])
onset_upweight = 5.0
expected_roll_weights = [
[onset_upweight, onset_upweight, 1, onset_upweight],
[onset_upweight, onset_upweight, onset_upweight, onset_upweight],
[1, 1, onset_upweight, onset_upweight / 1],
[1, 1, onset_upweight, onset_upweight / 2],
[1, 1, 1, 1],
]
expected_onsets = [
[1, 1, 0, 1],
[1, 1, 1, 1],
[0, 0, 1, 0],
[0, 0, 1, 0],
[0, 0, 0, 0],
]
roll = sequences_lib.sequence_to_pianoroll(
sequence,
frames_per_second=2,
min_pitch=1,
max_pitch=4,
onset_upweight=onset_upweight)
np.testing.assert_allclose(expected_roll_weights, roll.weights)
np.testing.assert_allclose(expected_onsets, roll.onsets)
def testSequenceToPianorollOnsets(self):
sequence = music_pb2.NoteSequence()
sequence.notes.add(pitch=60, start_time=2.0, end_time=5.0)
sequence.notes.add(pitch=61, start_time=6.0, end_time=7.0)
sequence.notes.add(pitch=62, start_time=7.0, end_time=8.0)
sequence.total_time = 8.0
onsets = sequences_lib.sequence_to_pianoroll(
sequence,
100,
60,
62,
onset_mode='length_ms',
onset_length_ms=100.0,
onset_delay_ms=10.0,
min_frame_occupancy_for_label=.999).onsets
expected_roll = np.zeros([801, 3])
expected_roll[201:211, 0] = 1.
expected_roll[601:611, 1] = 1.
expected_roll[701:711, 2] = 1.
np.testing.assert_equal(expected_roll, onsets)
def testSequenceToPianorollFrameOccupancy(self):
sequence = music_pb2.NoteSequence()
sequence.notes.add(pitch=60, start_time=1.0, end_time=1.7)
sequence.notes.add(pitch=61, start_time=6.2, end_time=6.55)
sequence.notes.add(pitch=62, start_time=3.4, end_time=4.3)
sequence.total_time = 6.55
active = sequences_lib.sequence_to_pianoroll(
sequence, 2, 60, 62, min_frame_occupancy_for_label=0.5).active
expected_roll = np.zeros([14, 3])
expected_roll[2:3, 0] = 1.
expected_roll[12:13, 1] = 1.
expected_roll[7:9, 2] = 1.
np.testing.assert_equal(expected_roll, active)
def testSequenceToPianorollOnsetVelocities(self):
sequence = music_pb2.NoteSequence()
sequence.notes.add(pitch=60, start_time=0.0, end_time=2.0, velocity=16)
sequence.notes.add(pitch=61, start_time=0.0, end_time=2.0, velocity=32)
sequence.notes.add(pitch=62, start_time=0.0, end_time=2.0, velocity=64)
sequence.total_time = 2.0
roll = sequences_lib.sequence_to_pianoroll(
sequence, 1, 60, 62, max_velocity=64, onset_window=0)
onset_velocities = roll.onset_velocities
self.assertEqual(onset_velocities[0, 0], 0.25)
self.assertEqual(onset_velocities[0, 1], 0.5)
self.assertEqual(onset_velocities[0, 2], 1.)
self.assertEqual(np.all(onset_velocities[1:] == 0), True)
def testSequenceToPianorollActiveVelocities(self):
sequence = music_pb2.NoteSequence()
sequence.notes.add(pitch=60, start_time=0.0, end_time=2.0, velocity=16)
sequence.notes.add(pitch=61, start_time=0.0, end_time=2.0, velocity=32)
sequence.notes.add(pitch=62, start_time=0.0, end_time=2.0, velocity=64)
sequence.total_time = 2.0
roll = sequences_lib.sequence_to_pianoroll(
sequence, 1, 60, 62, max_velocity=64)
active_velocities = roll.active_velocities
self.assertEqual(np.all(active_velocities[0:2, 0] == 0.25), True)
self.assertEqual(np.all(active_velocities[0:2, 1] == 0.5), True)
self.assertEqual(np.all(active_velocities[0:2, 2] == 1.), True)
self.assertEqual(np.all(active_velocities[2:] == 0), True)
def testPianorollToNoteSequence(self):
# 100 frames of notes.
frames = np.zeros((100, MIDI_PITCHES), np.bool)
# Activate key 39 for the middle 50 frames.
frames[25:75, 39] = True
sequence = sequences_lib.pianoroll_to_note_sequence(
frames, frames_per_second=DEFAULT_FRAMES_PER_SECOND, min_duration_ms=0)
self.assertLen(sequence.notes, 1)
self.assertEqual(39, sequence.notes[0].pitch)
self.assertEqual(25 / DEFAULT_FRAMES_PER_SECOND,
sequence.notes[0].start_time)
self.assertEqual(75 / DEFAULT_FRAMES_PER_SECOND, sequence.notes[0].end_time)
def testPianorollToNoteSequenceAllNotes(self):
# Test all 128 notes
frames = np.eye(MIDI_PITCHES, dtype=np.bool) # diagonal identity matrix
sequence = sequences_lib.pianoroll_to_note_sequence(
frames, frames_per_second=DEFAULT_FRAMES_PER_SECOND, min_duration_ms=0)
self.assertLen(sequence.notes, MIDI_PITCHES)
for i in range(MIDI_PITCHES):
self.assertEqual(i, sequence.notes[i].pitch)
self.assertAlmostEqual(i / DEFAULT_FRAMES_PER_SECOND,
sequence.notes[i].start_time)
self.assertAlmostEqual((i+1) / DEFAULT_FRAMES_PER_SECOND,
sequence.notes[i].end_time)
def testPianorollToNoteSequenceWithOnsets(self):
# 100 frames of notes and onsets.
frames = np.zeros((100, MIDI_PITCHES), np.bool)
onsets = np.zeros((100, MIDI_PITCHES), np.bool)
# Activate key 39 for the middle 50 frames and last 10 frames.
frames[25:75, 39] = True
frames[90:100, 39] = True
# Add an onset for the first occurrence.
onsets[25, 39] = True
# Add an onset for a note that doesn't have an active frame.
onsets[80, 49] = True
sequence = sequences_lib.pianoroll_to_note_sequence(
frames,
frames_per_second=DEFAULT_FRAMES_PER_SECOND,
min_duration_ms=0,
onset_predictions=onsets)
self.assertLen(sequence.notes, 2)
self.assertEqual(39, sequence.notes[0].pitch)
self.assertEqual(25 / DEFAULT_FRAMES_PER_SECOND,
sequence.notes[0].start_time)
self.assertEqual(75 / DEFAULT_FRAMES_PER_SECOND, sequence.notes[0].end_time)
self.assertEqual(49, sequence.notes[1].pitch)
self.assertEqual(80 / DEFAULT_FRAMES_PER_SECOND,
sequence.notes[1].start_time)
self.assertEqual(81 / DEFAULT_FRAMES_PER_SECOND, sequence.notes[1].end_time)
def testPianorollToNoteSequenceWithOnsetsAndVelocity(self):
# 100 frames of notes and onsets.
frames = np.zeros((100, MIDI_PITCHES), np.bool)
onsets = np.zeros((100, MIDI_PITCHES), np.bool)
velocity_values = np.zeros((100, MIDI_PITCHES), np.float32)
# Activate key 39 for the middle 50 frames and last 10 frames.
frames[25:75, 39] = True
frames[90:100, 39] = True
# Add an onset for the first occurrence with a valid velocity.
onsets[25, 39] = True
velocity_values[25, 39] = 0.5
# Add an onset for the second occurrence with a NaN velocity.
onsets[90, 39] = True
velocity_values[90, 39] = float('nan')
sequence = sequences_lib.pianoroll_to_note_sequence(
frames,
frames_per_second=DEFAULT_FRAMES_PER_SECOND,
min_duration_ms=0,
onset_predictions=onsets,
velocity_values=velocity_values)
self.assertLen(sequence.notes, 2)
self.assertEqual(39, sequence.notes[0].pitch)
self.assertEqual(50, sequence.notes[0].velocity)
self.assertEqual(39, sequence.notes[1].pitch)
self.assertEqual(0, sequence.notes[1].velocity)
def testPianorollToNoteSequenceWithOnsetsAndFullScaleVelocity(self):
# 100 frames of notes and onsets.
frames = np.zeros((100, MIDI_PITCHES), np.bool)
onsets = np.zeros((100, MIDI_PITCHES), np.bool)
velocity_values = np.zeros((100, MIDI_PITCHES), np.float32)
# Activate key 39 for the middle 50 frames and last 10 frames.
frames[25:75, 39] = True
frames[90:100, 39] = True
onsets[25, 39] = True
velocity_values[25, 39] = 0.5
onsets[90, 39] = True
velocity_values[90, 39] = 1.0
sequence = sequences_lib.pianoroll_to_note_sequence(
frames,
frames_per_second=DEFAULT_FRAMES_PER_SECOND,
min_duration_ms=0,
onset_predictions=onsets,
velocity_values=velocity_values,
velocity_scale=127,
velocity_bias=0)
self.assertLen(sequence.notes, 2)
self.assertEqual(39, sequence.notes[0].pitch)
self.assertEqual(63, sequence.notes[0].velocity)
self.assertEqual(39, sequence.notes[1].pitch)
self.assertEqual(127, sequence.notes[1].velocity)
def testPianorollToNoteSequenceWithOnsetsDefaultVelocity(self):
# 100 frames of notes and onsets.
frames = np.zeros((100, MIDI_PITCHES), np.bool)
onsets = np.zeros((100, MIDI_PITCHES), np.bool)
# Activate key 39 for the middle 50 frames and last 10 frames.
frames[25:75, 39] = True
frames[90:100, 39] = True
onsets[25, 39] = True
onsets[90, 39] = True
sequence = sequences_lib.pianoroll_to_note_sequence(
frames,
frames_per_second=DEFAULT_FRAMES_PER_SECOND,
min_duration_ms=0,
onset_predictions=onsets,
velocity=100)
self.assertLen(sequence.notes, 2)
self.assertEqual(39, sequence.notes[0].pitch)
self.assertEqual(100, sequence.notes[0].velocity)
self.assertEqual(39, sequence.notes[1].pitch)
self.assertEqual(100, sequence.notes[1].velocity)
def testPianorollToNoteSequenceWithOnsetsOverlappingFrames(self):
# 100 frames of notes and onsets.
frames = np.zeros((100, MIDI_PITCHES), np.bool)
onsets = np.zeros((100, MIDI_PITCHES), np.bool)
# Activate key 39 for the middle 50 frames.
frames[25:75, 39] = True
# Add multiple onsets within those frames.
onsets[25, 39] = True
onsets[30, 39] = True
# If an onset lasts for multiple frames, it should create only 1 note.
onsets[35, 39] = True
onsets[36, 39] = True
sequence = sequences_lib.pianoroll_to_note_sequence(
frames,
frames_per_second=DEFAULT_FRAMES_PER_SECOND,
min_duration_ms=0,
onset_predictions=onsets)
self.assertLen(sequence.notes, 3)
self.assertEqual(39, sequence.notes[0].pitch)
self.assertEqual(25 / DEFAULT_FRAMES_PER_SECOND,
sequence.notes[0].start_time)
self.assertEqual(30 / DEFAULT_FRAMES_PER_SECOND, sequence.notes[0].end_time)
self.assertEqual(39, sequence.notes[1].pitch)
self.assertEqual(30 / DEFAULT_FRAMES_PER_SECOND,
sequence.notes[1].start_time)
self.assertEqual(35 / DEFAULT_FRAMES_PER_SECOND, sequence.notes[1].end_time)
self.assertEqual(39, sequence.notes[2].pitch)
self.assertEqual(35 / DEFAULT_FRAMES_PER_SECOND,
sequence.notes[2].start_time)
self.assertEqual(75 / DEFAULT_FRAMES_PER_SECOND, sequence.notes[2].end_time)
def testPianorollOnsetsToNoteSequence(self):
onsets = np.zeros((10, 2), np.bool)
velocity_values = np.zeros_like(onsets, np.float32)
onsets[0:2, 0] = True
velocity_values[0:2, 0] = .5
onsets[1:2, 1] = True
velocity_values[1:2, 1] = 1
sequence = sequences_lib.pianoroll_onsets_to_note_sequence(
onsets, frames_per_second=10, note_duration_seconds=0.05,
min_midi_pitch=60, velocity_values=velocity_values)
self.assertLen(sequence.notes, 3)
self.assertEqual(60, sequence.notes[0].pitch)
self.assertEqual(0, sequence.notes[0].start_time)
self.assertAlmostEqual(0.05, sequence.notes[0].end_time)
self.assertEqual(50, sequence.notes[0].velocity)
self.assertEqual(60, sequence.notes[1].pitch)
self.assertEqual(0.1, sequence.notes[1].start_time)
self.assertAlmostEqual(0.15, sequence.notes[1].end_time)
self.assertEqual(50, sequence.notes[1].velocity)
self.assertEqual(61, sequence.notes[2].pitch)
self.assertEqual(0.1, sequence.notes[2].start_time)
self.assertAlmostEqual(0.15, sequence.notes[2].end_time)
self.assertEqual(90, sequence.notes[2].velocity)
def testPianorollOnsetsToNoteSequenceFullVelocityScale(self):
onsets = np.zeros((10, 2), np.bool)
velocity_values = np.zeros_like(onsets, np.float32)
onsets[0:2, 0] = True
velocity_values[0:2, 0] = .5
onsets[1:2, 1] = True
velocity_values[1:2, 1] = 1
sequence = sequences_lib.pianoroll_onsets_to_note_sequence(
onsets, frames_per_second=10, note_duration_seconds=0.05,
min_midi_pitch=60, velocity_values=velocity_values,
velocity_scale=127, velocity_bias=0)
self.assertLen(sequence.notes, 3)
self.assertEqual(60, sequence.notes[0].pitch)
self.assertEqual(0, sequence.notes[0].start_time)
self.assertAlmostEqual(0.05, sequence.notes[0].end_time)
self.assertEqual(63, sequence.notes[0].velocity)
self.assertEqual(60, sequence.notes[1].pitch)
self.assertEqual(0.1, sequence.notes[1].start_time)
self.assertAlmostEqual(0.15, sequence.notes[1].end_time)
self.assertEqual(63, sequence.notes[1].velocity)
self.assertEqual(61, sequence.notes[2].pitch)
self.assertEqual(0.1, sequence.notes[2].start_time)
self.assertAlmostEqual(0.15, sequence.notes[2].end_time)
self.assertEqual(127, sequence.notes[2].velocity)
def testSequenceToPianorollControlChanges(self):
sequence = music_pb2.NoteSequence(total_time=2.0)
cc = music_pb2.NoteSequence.ControlChange
sequence.control_changes.extend([
cc(time=0.7, control_number=3, control_value=16),
cc(time=0.0, control_number=4, control_value=32),
cc(time=0.5, control_number=4, control_value=32),
cc(time=1.6, control_number=3, control_value=64),
])
expected_cc_roll = np.zeros((5, 128), dtype=np.int32)
expected_cc_roll[0:2, 4] = 33
expected_cc_roll[1, 3] = 17
expected_cc_roll[3, 3] = 65
cc_roll = sequences_lib.sequence_to_pianoroll(
sequence, frames_per_second=2, min_pitch=1, max_pitch=4).control_changes
np.testing.assert_allclose(expected_cc_roll, cc_roll)
def testSequenceToPianorollOverlappingNotes(self):
sequence = music_pb2.NoteSequence()
sequence.notes.add(pitch=60, start_time=1.0, end_time=2.0)
sequence.notes.add(pitch=60, start_time=1.2, end_time=2.0)
sequence.notes.add(pitch=60, start_time=1.0, end_time=2.5)
sequence.total_time = 2.5
rolls = sequences_lib.sequence_to_pianoroll(
sequence, frames_per_second=10, min_pitch=60, max_pitch=60,
onset_mode='length_ms', onset_length_ms=10)
expected_onsets = np.zeros([26, 1])
expected_onsets[10, 0] = 1
expected_onsets[12, 0] = 1
np.testing.assert_equal(expected_onsets, rolls.onsets)
expected_offsets = np.zeros([26, 1])
expected_offsets[20, 0] = 1
expected_offsets[25, 0] = 1
np.testing.assert_equal(expected_offsets, rolls.offsets)
expected_active = np.zeros([26, 1])
expected_active[10:25, 0] = 1
np.testing.assert_equal(expected_active, rolls.active)
def testSequenceToPianorollShortNotes(self):
sequence = music_pb2.NoteSequence()
sequence.notes.add(pitch=60, start_time=1.0, end_time=1.0001)
sequence.notes.add(pitch=60, start_time=1.2, end_time=1.2001)
sequence.total_time = 2.5
rolls = sequences_lib.sequence_to_pianoroll(
sequence, frames_per_second=10, min_pitch=60, max_pitch=60,
onset_mode='length_ms', onset_length_ms=0)
expected_onsets = np.zeros([26, 1])
expected_onsets[10, 0] = 1
expected_onsets[12, 0] = 1
np.testing.assert_equal(expected_onsets, rolls.onsets)
expected_offsets = np.zeros([26, 1])
expected_offsets[10, 0] = 1
expected_offsets[12, 0] = 1
np.testing.assert_equal(expected_offsets, rolls.offsets)
expected_active = np.zeros([26, 1])
expected_active[10:11, 0] = 1
expected_active[12:13, 0] = 1
np.testing.assert_equal(expected_active, rolls.active)
def testSequenceToValuedIntervals(self):
sequence = music_pb2.NoteSequence()
sequence.notes.add(pitch=69, start_time=1.0, end_time=2.0, velocity=80)
# Should be dropped because it is 0 duration.
sequence.notes.add(pitch=60, start_time=3.0, end_time=3.0, velocity=90)
intervals, pitches, velocities = sequences_lib.sequence_to_valued_intervals(
sequence)
np.testing.assert_array_equal([[1., 2.]], intervals)
np.testing.assert_array_equal([440.0], pitches)
np.testing.assert_array_equal([80], velocities)
if __name__ == '__main__':
absltest.main()
Classes
class SequencesLibTest (*args, **kwargs)
-
Adds assertProtoEquals from tf.test.TestCase.
Create an instance of the class that will use the named test method when executed. Raises a ValueError if the instance does not have a method with the specified name.
Expand source code
class SequencesLibTest(testing_lib.ProtoTestCase): def testTransposeNoteSequence(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)]) sequence.text_annotations.add( time=1, annotation_type=CHORD_SYMBOL, text='N.C.') sequence.text_annotations.add( time=2, annotation_type=CHORD_SYMBOL, text='E7') sequence.key_signatures.add( time=0, key=music_pb2.NoteSequence.KeySignature.E, mode=music_pb2.NoteSequence.KeySignature.MIXOLYDIAN) expected_sequence = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( expected_sequence, 0, [(13, 100, 0.01, 10.0), (12, 55, 0.22, 0.50), (41, 45, 2.50, 3.50), (56, 120, 4.0, 4.01), (53, 99, 4.75, 5.0)]) expected_sequence.text_annotations.add( time=1, annotation_type=CHORD_SYMBOL, text='N.C.') expected_sequence.text_annotations.add( time=2, annotation_type=CHORD_SYMBOL, text='F7') expected_sequence.key_signatures.add( time=0, key=music_pb2.NoteSequence.KeySignature.F, mode=music_pb2.NoteSequence.KeySignature.MIXOLYDIAN) transposed_sequence, delete_count = sequences_lib.transpose_note_sequence( sequence, 1) self.assertProtoEquals(expected_sequence, transposed_sequence) self.assertEqual(delete_count, 0) def testTransposeNoteSequenceOutOfRange(self): sequence = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( sequence, 0, [(35, 100, 0.01, 10.0), (36, 55, 0.22, 0.50), (37, 45, 2.50, 3.50), (38, 120, 4.0, 4.01), (39, 99, 4.75, 5.0)]) expected_sequence_1 = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( expected_sequence_1, 0, [(39, 100, 0.01, 10.0), (40, 55, 0.22, 0.50)]) expected_sequence_2 = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( expected_sequence_2, 0, [(30, 120, 4.0, 4.01), (31, 99, 4.75, 5.0)]) sequence_copy = copy.copy(sequence) transposed_sequence, delete_count = sequences_lib.transpose_note_sequence( sequence_copy, 4, 30, 40) self.assertProtoEquals(expected_sequence_1, transposed_sequence) self.assertEqual(delete_count, 3) sequence_copy = copy.copy(sequence) transposed_sequence, delete_count = sequences_lib.transpose_note_sequence( sequence_copy, -8, 30, 40) self.assertProtoEquals(expected_sequence_2, transposed_sequence) self.assertEqual(delete_count, 3) def testClampTranspose(self): clamped = sequences_lib._clamp_transpose( # pylint:disable=protected-access 5, 20, 60, 10, 70) self.assertEqual(clamped, 5) clamped = sequences_lib._clamp_transpose( # pylint:disable=protected-access 15, 20, 60, 10, 65) self.assertEqual(clamped, 5) clamped = sequences_lib._clamp_transpose( # pylint:disable=protected-access -16, 20, 60, 10, 70) self.assertEqual(clamped, -10) def testAugmentNoteSequenceDeleteFalse(self): sequence = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( sequence, 0, [(12, 100, 0.01, 10.0), (13, 55, 0.22, 0.50), (40, 45, 2.50, 3.50), (55, 120, 4.0, 4.01), (52, 99, 4.75, 5.0)]) augmented_sequence = sequences_lib.augment_note_sequence( sequence, min_stretch_factor=2, max_stretch_factor=2, min_transpose=-15, max_transpose=-10, min_allowed_pitch=10, max_allowed_pitch=127, delete_out_of_range_notes=False) expected_sequence = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( expected_sequence, 0, [(10, 100, 0.02, 20.0), (11, 55, 0.44, 1.0), (38, 45, 5., 7.), (53, 120, 8.0, 8.02), (50, 99, 9.5, 10.0)]) expected_sequence.tempos[0].qpm = 30. self.assertProtoEquals(augmented_sequence, expected_sequence) def testAugmentNoteSequenceDeleteTrue(self): sequence = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( sequence, 0, [(12, 100, 0.01, 10.0), (13, 55, 0.22, 0.50), (40, 45, 2.50, 3.50), (55, 120, 4.0, 4.01), (52, 99, 4.75, 5.0)]) augmented_sequence = sequences_lib.augment_note_sequence( sequence, min_stretch_factor=2, max_stretch_factor=2, min_transpose=-15, max_transpose=-15, min_allowed_pitch=10, max_allowed_pitch=127, delete_out_of_range_notes=True) expected_sequence = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( expected_sequence, 0, [(25, 45, 5., 7.), (40, 120, 8.0, 8.02), (37, 99, 9.5, 10.0)]) expected_sequence.tempos[0].qpm = 30. self.assertProtoEquals(augmented_sequence, expected_sequence) def testAugmentNoteSequenceNoStretch(self): sequence = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( sequence, 0, [(12, 100, 0.01, 10.0), (13, 55, 0.22, 0.50), (40, 45, 2.50, 3.50), (55, 120, 4.0, 4.01), (52, 99, 4.75, 5.0)]) augmented_sequence = sequences_lib.augment_note_sequence( sequence, min_stretch_factor=1, max_stretch_factor=1., min_transpose=-15, max_transpose=-15, min_allowed_pitch=10, max_allowed_pitch=127, delete_out_of_range_notes=True) expected_sequence = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( expected_sequence, 0, [(25, 45, 2.5, 3.50), (40, 120, 4.0, 4.01), (37, 99, 4.75, 5.0)]) self.assertProtoEquals(augmented_sequence, expected_sequence) def testAugmentNoteSequenceNoTranspose(self): sequence = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( sequence, 0, [(12, 100, 0.01, 10.0), (13, 55, 0.22, 0.50), (40, 45, 2.50, 3.50), (55, 120, 4.0, 4.01), (52, 99, 4.75, 5.0)]) augmented_sequence = sequences_lib.augment_note_sequence( sequence, min_stretch_factor=2, max_stretch_factor=2., min_transpose=0, max_transpose=0, min_allowed_pitch=10, max_allowed_pitch=127, delete_out_of_range_notes=True) expected_sequence = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( expected_sequence, 0, [(12, 100, 0.02, 20.0), (13, 55, 0.44, 1.0), (40, 45, 5., 7.), (55, 120, 8.0, 8.02), (52, 99, 9.5, 10.0)]) expected_sequence.tempos[0].qpm = 30. self.assertProtoEquals(augmented_sequence, expected_sequence) def testTrimNoteSequence(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)]) expected_subsequence = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( expected_subsequence, 0, [(40, 45, 2.50, 3.50), (55, 120, 4.0, 4.01)]) expected_subsequence.total_time = 4.75 subsequence = sequences_lib.trim_note_sequence(sequence, 2.5, 4.75) self.assertProtoEquals(expected_subsequence, subsequence) 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.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) subsequence.control_changes.sort( key=lambda cc: (cc.instrument, cc.time)) self.assertProtoEquals(expected_subsequence, subsequence) def testExtractSubsequencePastEnd(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', 18.0)]) with self.assertRaises(ValueError): sequences_lib.extract_subsequence(sequence, 15.0, 16.0) def testExtractSubsequencePedalEvents(self): sequence = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( sequence, 0, [(60, 80, 2.5, 5.0)]) 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_control_changes_to_sequence( sequence, 0, [(0.0, 66, 0), (2.0, 66, 127), (4.0, 66, 0), (5.0, 66, 127)]) testing_lib.add_control_changes_to_sequence( sequence, 0, [(0.0, 67, 10), (2.0, 67, 20), (4.0, 67, 30), (5.0, 67, 40)]) expected_subsequence = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( expected_subsequence, 0, [(60, 80, 0, 2.25)]) 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)]) testing_lib.add_control_changes_to_sequence( expected_subsequence, 0, [(0.0, 66, 127), (1.5, 66, 0)]) testing_lib.add_control_changes_to_sequence( expected_subsequence, 0, [(0.0, 67, 20), (1.5, 67, 30)]) expected_subsequence.control_changes.sort( key=lambda cc: (cc.instrument, cc.control_number, cc.time)) expected_subsequence.total_time = 2.25 expected_subsequence.subsequence_info.start_time_offset = 2.5 expected_subsequence.subsequence_info.end_time_offset = .25 subsequence = sequences_lib.extract_subsequence(sequence, 2.5, 4.75) subsequence.control_changes.sort( key=lambda cc: (cc.instrument, cc.control_number, cc.time)) self.assertProtoEquals(expected_subsequence, subsequence) def testSplitNoteSequenceWithHopSize(self): # Tests splitting a NoteSequence at regular hop size, truncating notes. sequence = testing_lib.parse_test_proto( music_pb2.NoteSequence, """ time_signatures: { numerator: 4 denominator: 4} tempos: { qpm: 60}""") testing_lib.add_track_to_sequence( sequence, 0, [(12, 100, 0.01, 8.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.0), ('G7', 2.0), ('F', 4.0)]) expected_subsequence_1 = testing_lib.parse_test_proto( music_pb2.NoteSequence, """ time_signatures: { numerator: 4 denominator: 4} tempos: { qpm: 60}""") testing_lib.add_track_to_sequence( expected_subsequence_1, 0, [(12, 100, 0.01, 3.0), (11, 55, 0.22, 0.50), (40, 45, 2.50, 3.0)]) testing_lib.add_chords_to_sequence( expected_subsequence_1, [('C', 1.0), ('G7', 2.0)]) expected_subsequence_1.total_time = 3.0 expected_subsequence_1.subsequence_info.end_time_offset = 5.0 expected_subsequence_2 = testing_lib.parse_test_proto( music_pb2.NoteSequence, """ time_signatures: { numerator: 4 denominator: 4} tempos: { qpm: 60}""") testing_lib.add_track_to_sequence( expected_subsequence_2, 0, [(55, 120, 1.0, 1.01), (52, 99, 1.75, 2.0)]) testing_lib.add_chords_to_sequence( expected_subsequence_2, [('G7', 0.0), ('F', 1.0)]) expected_subsequence_2.total_time = 2.0 expected_subsequence_2.subsequence_info.start_time_offset = 3.0 expected_subsequence_2.subsequence_info.end_time_offset = 3.0 expected_subsequence_3 = testing_lib.parse_test_proto( music_pb2.NoteSequence, """ time_signatures: { numerator: 4 denominator: 4} tempos: { qpm: 60}""") testing_lib.add_chords_to_sequence( expected_subsequence_3, [('F', 0.0)]) expected_subsequence_3.total_time = 0.0 expected_subsequence_3.subsequence_info.start_time_offset = 6.0 expected_subsequence_3.subsequence_info.end_time_offset = 2.0 subsequences = sequences_lib.split_note_sequence( sequence, hop_size_seconds=3.0) self.assertLen(subsequences, 3) self.assertProtoEquals(expected_subsequence_1, subsequences[0]) self.assertProtoEquals(expected_subsequence_2, subsequences[1]) self.assertProtoEquals(expected_subsequence_3, subsequences[2]) def testSplitNoteSequenceAtTimes(self): # Tests splitting a NoteSequence at specified times, truncating notes. sequence = testing_lib.parse_test_proto( music_pb2.NoteSequence, """ time_signatures: { numerator: 4 denominator: 4} tempos: { qpm: 60}""") testing_lib.add_track_to_sequence( sequence, 0, [(12, 100, 0.01, 8.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.0), ('G7', 2.0), ('F', 4.0)]) expected_subsequence_1 = testing_lib.parse_test_proto( music_pb2.NoteSequence, """ time_signatures: { numerator: 4 denominator: 4} tempos: { qpm: 60}""") testing_lib.add_track_to_sequence( expected_subsequence_1, 0, [(12, 100, 0.01, 3.0), (11, 55, 0.22, 0.50), (40, 45, 2.50, 3.0)]) testing_lib.add_chords_to_sequence( expected_subsequence_1, [('C', 1.0), ('G7', 2.0)]) expected_subsequence_1.total_time = 3.0 expected_subsequence_1.subsequence_info.end_time_offset = 5.0 expected_subsequence_2 = testing_lib.parse_test_proto( music_pb2.NoteSequence, """ time_signatures: { numerator: 4 denominator: 4} tempos: { qpm: 60}""") testing_lib.add_chords_to_sequence( expected_subsequence_2, [('G7', 0.0)]) expected_subsequence_2.total_time = 0.0 expected_subsequence_2.subsequence_info.start_time_offset = 3.0 expected_subsequence_2.subsequence_info.end_time_offset = 5.0 expected_subsequence_3 = testing_lib.parse_test_proto( music_pb2.NoteSequence, """ time_signatures: { numerator: 4 denominator: 4} tempos: { qpm: 60}""") testing_lib.add_track_to_sequence( expected_subsequence_3, 0, [(55, 120, 0.0, 0.01), (52, 99, 0.75, 1.0)]) testing_lib.add_chords_to_sequence( expected_subsequence_3, [('F', 0.0)]) expected_subsequence_3.total_time = 1.0 expected_subsequence_3.subsequence_info.start_time_offset = 4.0 expected_subsequence_3.subsequence_info.end_time_offset = 3.0 subsequences = sequences_lib.split_note_sequence( sequence, hop_size_seconds=[3.0, 4.0]) self.assertLen(subsequences, 3) self.assertProtoEquals(expected_subsequence_1, subsequences[0]) self.assertProtoEquals(expected_subsequence_2, subsequences[1]) self.assertProtoEquals(expected_subsequence_3, subsequences[2]) def testSplitNoteSequenceSkipSplitsInsideNotes(self): # Tests splitting a NoteSequence at regular hop size, skipping splits that # would have occurred inside a note. sequence = testing_lib.parse_test_proto( music_pb2.NoteSequence, """ time_signatures: { numerator: 4 denominator: 4} tempos: { qpm: 60}""") testing_lib.add_track_to_sequence( sequence, 0, [(12, 100, 0.01, 3.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', 0.0), ('G7', 3.0), ('F', 4.5)]) expected_subsequence_1 = testing_lib.parse_test_proto( music_pb2.NoteSequence, """ time_signatures: { numerator: 4 denominator: 4} tempos: { qpm: 60}""") testing_lib.add_track_to_sequence( expected_subsequence_1, 0, [(12, 100, 0.01, 3.0), (11, 55, 0.22, 0.50), (40, 45, 2.50, 3.50)]) testing_lib.add_chords_to_sequence( expected_subsequence_1, [('C', 0.0), ('G7', 3.0)]) expected_subsequence_1.total_time = 3.50 expected_subsequence_1.subsequence_info.end_time_offset = 1.5 expected_subsequence_2 = testing_lib.parse_test_proto( music_pb2.NoteSequence, """ time_signatures: { numerator: 4 denominator: 4} tempos: { qpm: 60}""") testing_lib.add_track_to_sequence( expected_subsequence_2, 0, [(55, 120, 0.0, 0.01), (52, 99, 0.75, 1.0)]) testing_lib.add_chords_to_sequence( expected_subsequence_2, [('G7', 0.0), ('F', 0.5)]) expected_subsequence_2.total_time = 1.0 expected_subsequence_2.subsequence_info.start_time_offset = 4.0 subsequences = sequences_lib.split_note_sequence( sequence, hop_size_seconds=2.0, skip_splits_inside_notes=True) self.assertLen(subsequences, 2) self.assertProtoEquals(expected_subsequence_1, subsequences[0]) self.assertProtoEquals(expected_subsequence_2, subsequences[1]) def testSplitNoteSequenceNoTimeChanges(self): # Tests splitting a NoteSequence on time changes for a NoteSequence that has # no time changes (time signature and tempo changes). 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)]) expected_subsequence = music_pb2.NoteSequence() expected_subsequence.CopyFrom(sequence) expected_subsequence.subsequence_info.start_time_offset = 0.0 expected_subsequence.subsequence_info.end_time_offset = 0.0 subsequences = sequences_lib.split_note_sequence_on_time_changes(sequence) self.assertLen(subsequences, 1) self.assertProtoEquals(expected_subsequence, subsequences[0]) def testSplitNoteSequenceDuplicateTimeChanges(self): # Tests splitting a NoteSequence on time changes for a NoteSequence that has # duplicate time changes. sequence = testing_lib.parse_test_proto( music_pb2.NoteSequence, """ time_signatures: { numerator: 4 denominator: 4} time_signatures: { time: 2.0 numerator: 4 denominator: 4} tempos: { qpm: 60}""") 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)]) expected_subsequence = music_pb2.NoteSequence() expected_subsequence.CopyFrom(sequence) expected_subsequence.subsequence_info.start_time_offset = 0.0 expected_subsequence.subsequence_info.end_time_offset = 0.0 subsequences = sequences_lib.split_note_sequence_on_time_changes(sequence) self.assertLen(subsequences, 1) self.assertProtoEquals(expected_subsequence, subsequences[0]) def testSplitNoteSequenceCoincidentTimeChanges(self): # Tests splitting a NoteSequence on time changes for a NoteSequence that has # two time changes occurring simultaneously. sequence = testing_lib.parse_test_proto( music_pb2.NoteSequence, """ time_signatures: { numerator: 4 denominator: 4} time_signatures: { time: 2.0 numerator: 3 denominator: 4} tempos: { qpm: 60} tempos: { time: 2.0 qpm: 80}""") 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)]) expected_subsequence_1 = testing_lib.parse_test_proto( music_pb2.NoteSequence, """ time_signatures: { numerator: 4 denominator: 4} tempos: { qpm: 60}""") testing_lib.add_track_to_sequence( expected_subsequence_1, 0, [(12, 100, 0.01, 2.0), (11, 55, 0.22, 0.50)]) testing_lib.add_chords_to_sequence( expected_subsequence_1, [('C', 1.5)]) expected_subsequence_1.total_time = 2.0 expected_subsequence_1.subsequence_info.end_time_offset = 8.0 expected_subsequence_2 = testing_lib.parse_test_proto( music_pb2.NoteSequence, """ time_signatures: { numerator: 3 denominator: 4} tempos: { qpm: 80}""") testing_lib.add_track_to_sequence( expected_subsequence_2, 0, [(40, 45, 0.50, 1.50), (55, 120, 2.0, 2.01), (52, 99, 2.75, 3.0)]) testing_lib.add_chords_to_sequence( expected_subsequence_2, [('C', 0.0), ('G7', 1.0), ('F', 2.8)]) expected_subsequence_2.total_time = 3.0 expected_subsequence_2.subsequence_info.start_time_offset = 2.0 expected_subsequence_2.subsequence_info.end_time_offset = 5.0 subsequences = sequences_lib.split_note_sequence_on_time_changes(sequence) self.assertLen(subsequences, 2) self.assertProtoEquals(expected_subsequence_1, subsequences[0]) self.assertProtoEquals(expected_subsequence_2, subsequences[1]) def testSplitNoteSequenceMultipleTimeChangesSkipSplitsInsideNotes(self): # Tests splitting a NoteSequence on time changes skipping splits that occur # inside notes. sequence = testing_lib.parse_test_proto( music_pb2.NoteSequence, """ time_signatures: { numerator: 4 denominator: 4} time_signatures: { time: 2.0 numerator: 3 denominator: 4} tempos: { qpm: 60} tempos: { time: 4.25 qpm: 80}""") testing_lib.add_track_to_sequence( sequence, 0, [(12, 100, 0.01, 3.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)]) expected_subsequence_1 = testing_lib.parse_test_proto( music_pb2.NoteSequence, """ time_signatures: { numerator: 4 denominator: 4} time_signatures: { time: 2.0 numerator: 3 denominator: 4} tempos: { qpm: 60}""") testing_lib.add_track_to_sequence( expected_subsequence_1, 0, [(12, 100, 0.01, 3.0), (11, 55, 0.22, 0.50), (40, 45, 2.50, 3.50), (55, 120, 4.0, 4.01)]) testing_lib.add_chords_to_sequence( expected_subsequence_1, [('C', 1.5), ('G7', 3.0)]) expected_subsequence_1.total_time = 4.01 expected_subsequence_1.subsequence_info.end_time_offset = 0.99 expected_subsequence_2 = testing_lib.parse_test_proto( music_pb2.NoteSequence, """ time_signatures: { numerator: 3 denominator: 4} tempos: { qpm: 80}""") testing_lib.add_track_to_sequence( expected_subsequence_2, 0, [(52, 99, 0.5, 0.75)]) testing_lib.add_chords_to_sequence(expected_subsequence_2, [ ('G7', 0.0), ('F', 0.55)]) expected_subsequence_2.total_time = 0.75 expected_subsequence_2.subsequence_info.start_time_offset = 4.25 subsequences = sequences_lib.split_note_sequence_on_time_changes( sequence, skip_splits_inside_notes=True) self.assertLen(subsequences, 2) self.assertProtoEquals(expected_subsequence_1, subsequences[0]) self.assertProtoEquals(expected_subsequence_2, subsequences[1]) def testSplitNoteSequenceMultipleTimeChanges(self): # Tests splitting a NoteSequence on time changes, truncating notes on splits # that occur inside notes. sequence = testing_lib.parse_test_proto( music_pb2.NoteSequence, """ time_signatures: { numerator: 4 denominator: 4} time_signatures: { time: 2.0 numerator: 3 denominator: 4} tempos: { qpm: 60} tempos: { time: 4.25 qpm: 80}""") 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)]) expected_subsequence_1 = testing_lib.parse_test_proto( music_pb2.NoteSequence, """ time_signatures: { numerator: 4 denominator: 4} tempos: { qpm: 60}""") testing_lib.add_track_to_sequence( expected_subsequence_1, 0, [(12, 100, 0.01, 2.0), (11, 55, 0.22, 0.50)]) testing_lib.add_chords_to_sequence( expected_subsequence_1, [('C', 1.5)]) expected_subsequence_1.total_time = 2.0 expected_subsequence_1.subsequence_info.end_time_offset = 8.0 expected_subsequence_2 = testing_lib.parse_test_proto( music_pb2.NoteSequence, """ time_signatures: { numerator: 3 denominator: 4} tempos: { qpm: 60}""") testing_lib.add_track_to_sequence( expected_subsequence_2, 0, [(40, 45, 0.50, 1.50), (55, 120, 2.0, 2.01)]) testing_lib.add_chords_to_sequence( expected_subsequence_2, [('C', 0.0), ('G7', 1.0)]) expected_subsequence_2.total_time = 2.01 expected_subsequence_2.subsequence_info.start_time_offset = 2.0 expected_subsequence_2.subsequence_info.end_time_offset = 5.99 expected_subsequence_3 = testing_lib.parse_test_proto( music_pb2.NoteSequence, """ time_signatures: { numerator: 3 denominator: 4} tempos: { qpm: 80}""") testing_lib.add_track_to_sequence( expected_subsequence_3, 0, [(52, 99, 0.5, 0.75)]) testing_lib.add_chords_to_sequence( expected_subsequence_3, [('G7', 0.0), ('F', 0.55)]) expected_subsequence_3.total_time = 0.75 expected_subsequence_3.subsequence_info.start_time_offset = 4.25 expected_subsequence_3.subsequence_info.end_time_offset = 5.0 subsequences = sequences_lib.split_note_sequence_on_time_changes(sequence) self.assertLen(subsequences, 3) self.assertProtoEquals(expected_subsequence_1, subsequences[0]) self.assertProtoEquals(expected_subsequence_2, subsequences[1]) self.assertProtoEquals(expected_subsequence_3, subsequences[2]) def testSplitNoteSequenceWithStatelessEvents(self): # Tests splitting a NoteSequence at specified times with stateless events. sequence = testing_lib.parse_test_proto( music_pb2.NoteSequence, """ time_signatures: { numerator: 4 denominator: 4} tempos: { qpm: 60}""") testing_lib.add_track_to_sequence( sequence, 0, [(12, 100, 0.01, 8.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_beats_to_sequence(sequence, [1.0, 2.0, 4.0]) expected_subsequence_1 = testing_lib.parse_test_proto( music_pb2.NoteSequence, """ time_signatures: { numerator: 4 denominator: 4} tempos: { qpm: 60}""") testing_lib.add_track_to_sequence( expected_subsequence_1, 0, [(12, 100, 0.01, 3.0), (11, 55, 0.22, 0.50), (40, 45, 2.50, 3.0)]) testing_lib.add_beats_to_sequence(expected_subsequence_1, [1.0, 2.0]) expected_subsequence_1.total_time = 3.0 expected_subsequence_1.subsequence_info.end_time_offset = 5.0 expected_subsequence_2 = testing_lib.parse_test_proto( music_pb2.NoteSequence, """ time_signatures: { numerator: 4 denominator: 4} tempos: { qpm: 60}""") expected_subsequence_2.total_time = 0.0 expected_subsequence_2.subsequence_info.start_time_offset = 3.0 expected_subsequence_2.subsequence_info.end_time_offset = 5.0 expected_subsequence_3 = testing_lib.parse_test_proto( music_pb2.NoteSequence, """ time_signatures: { numerator: 4 denominator: 4} tempos: { qpm: 60}""") testing_lib.add_track_to_sequence( expected_subsequence_3, 0, [(55, 120, 0.0, 0.01), (52, 99, 0.75, 1.0)]) testing_lib.add_beats_to_sequence(expected_subsequence_3, [0.0]) expected_subsequence_3.total_time = 1.0 expected_subsequence_3.subsequence_info.start_time_offset = 4.0 expected_subsequence_3.subsequence_info.end_time_offset = 3.0 subsequences = sequences_lib.split_note_sequence( sequence, hop_size_seconds=[3.0, 4.0]) self.assertLen(subsequences, 3) self.assertProtoEquals(expected_subsequence_1, subsequences[0]) self.assertProtoEquals(expected_subsequence_2, subsequences[1]) self.assertProtoEquals(expected_subsequence_3, subsequences[2]) def testSplitNoteSequenceOnSilence(self): sequence = music_pb2.NoteSequence() testing_lib.add_track_to_sequence( sequence, 0, [(12, 100, 0.01, 1.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)]) expected_subsequence_1 = music_pb2.NoteSequence() testing_lib.add_track_to_sequence( expected_subsequence_1, 0, [(12, 100, 0.01, 1.0), (11, 55, 0.22, 0.50)]) expected_subsequence_1.total_time = 1.0 expected_subsequence_1.subsequence_info.end_time_offset = 4.0 expected_subsequence_2 = music_pb2.NoteSequence() testing_lib.add_track_to_sequence( expected_subsequence_2, 0, [(40, 45, 0.0, 1.0), (55, 120, 1.50, 1.51)]) expected_subsequence_2.total_time = 1.51 expected_subsequence_2.subsequence_info.start_time_offset = 2.50 expected_subsequence_2.subsequence_info.end_time_offset = 0.99 expected_subsequence_3 = music_pb2.NoteSequence() testing_lib.add_track_to_sequence( expected_subsequence_3, 0, [(52, 99, 0.0, 0.25)]) expected_subsequence_3.total_time = 0.25 expected_subsequence_3.subsequence_info.start_time_offset = 4.75 subsequences = sequences_lib.split_note_sequence_on_silence( sequence, gap_seconds=0.5) self.assertLen(subsequences, 3) self.assertProtoEquals(expected_subsequence_1, subsequences[0]) self.assertProtoEquals(expected_subsequence_2, subsequences[1]) self.assertProtoEquals(expected_subsequence_3, subsequences[2]) def testSplitNoteSequenceOnSilenceInitialGap(self): sequence = music_pb2.NoteSequence() testing_lib.add_track_to_sequence( sequence, 0, [(12, 100, 1.5, 2.0), (11, 55, 1.5, 3.0), (40, 45, 2.5, 3.5)]) expected_subsequence_1 = music_pb2.NoteSequence() expected_subsequence_1.total_time = 0.0 expected_subsequence_1.subsequence_info.end_time_offset = 3.5 expected_subsequence_2 = music_pb2.NoteSequence() testing_lib.add_track_to_sequence( expected_subsequence_2, 0, [(12, 100, 0.0, 0.5), (11, 55, 0.0, 1.5), (40, 45, 1.0, 2.0)]) expected_subsequence_2.total_time = 2.0 expected_subsequence_2.subsequence_info.start_time_offset = 1.5 subsequences = sequences_lib.split_note_sequence_on_silence( sequence, gap_seconds=1.0) self.assertLen(subsequences, 2) self.assertProtoEquals(expected_subsequence_1, subsequences[0]) self.assertProtoEquals(expected_subsequence_2, subsequences[1]) def testQuantizeNoteSequence(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_quarter = ( self.steps_per_quarter) 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( self.note_sequence, steps_per_quarter=self.steps_per_quarter) self.assertProtoEquals(expected_quantized_sequence, quantized_sequence) 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 testAssertIsQuantizedNoteSequence(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)]) relative_quantized_sequence = sequences_lib.quantize_note_sequence( self.note_sequence, steps_per_quarter=self.steps_per_quarter) absolute_quantized_sequence = sequences_lib.quantize_note_sequence_absolute( self.note_sequence, steps_per_second=4) sequences_lib.assert_is_quantized_sequence(relative_quantized_sequence) sequences_lib.assert_is_quantized_sequence(absolute_quantized_sequence) with self.assertRaises(sequences_lib.QuantizationStatusError): # pylint:disable=g-error-prone-assert-raises sequences_lib.assert_is_quantized_sequence(self.note_sequence) def testAssertIsRelativeQuantizedNoteSequence(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)]) relative_quantized_sequence = sequences_lib.quantize_note_sequence( self.note_sequence, steps_per_quarter=self.steps_per_quarter) absolute_quantized_sequence = sequences_lib.quantize_note_sequence_absolute( self.note_sequence, steps_per_second=4) sequences_lib.assert_is_relative_quantized_sequence( relative_quantized_sequence) with self.assertRaises(sequences_lib.QuantizationStatusError): # pylint:disable=g-error-prone-assert-raises sequences_lib.assert_is_relative_quantized_sequence( absolute_quantized_sequence) with self.assertRaises(sequences_lib.QuantizationStatusError): # pylint:disable=g-error-prone-assert-raises sequences_lib.assert_is_relative_quantized_sequence(self.note_sequence) def testQuantizeNoteSequence_TimeSignatureChange(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)]) del self.note_sequence.time_signatures[:] sequences_lib.quantize_note_sequence( self.note_sequence, self.steps_per_quarter) # Single time signature. self.note_sequence.time_signatures.add(numerator=4, denominator=4, time=0) sequences_lib.quantize_note_sequence( self.note_sequence, self.steps_per_quarter) # Multiple time signatures with no change. self.note_sequence.time_signatures.add(numerator=4, denominator=4, time=1) sequences_lib.quantize_note_sequence( self.note_sequence, self.steps_per_quarter) # Time signature change. self.note_sequence.time_signatures.add(numerator=2, denominator=4, time=2) with self.assertRaises(sequences_lib.MultipleTimeSignatureError): sequences_lib.quantize_note_sequence( self.note_sequence, self.steps_per_quarter) def testQuantizeNoteSequence_ImplicitTimeSignatureChange(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)]) del self.note_sequence.time_signatures[:] # No time signature. sequences_lib.quantize_note_sequence( self.note_sequence, self.steps_per_quarter) # Implicit time signature change. self.note_sequence.time_signatures.add(numerator=2, denominator=4, time=2) with self.assertRaises(sequences_lib.MultipleTimeSignatureError): sequences_lib.quantize_note_sequence( self.note_sequence, self.steps_per_quarter) def testQuantizeNoteSequence_NoImplicitTimeSignatureChangeOutOfOrder(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)]) del self.note_sequence.time_signatures[:] # No time signature. sequences_lib.quantize_note_sequence( self.note_sequence, self.steps_per_quarter) # No implicit time signature change, but time signatures are added out of # order. self.note_sequence.time_signatures.add(numerator=2, denominator=4, time=2) self.note_sequence.time_signatures.add(numerator=2, denominator=4, time=0) sequences_lib.quantize_note_sequence( self.note_sequence, self.steps_per_quarter) def testStepsPerQuarterToStepsPerSecond(self): self.assertEqual( 4.0, sequences_lib.steps_per_quarter_to_steps_per_second(4, 60.0)) def testQuantizeToStep(self): self.assertEqual( 32, sequences_lib.quantize_to_step(8.0001, 4)) self.assertEqual( 34, sequences_lib.quantize_to_step(8.4999, 4)) self.assertEqual( 33, sequences_lib.quantize_to_step(8.4999, 4, quantize_cutoff=1.0)) def testFromNoteSequence_TempoChange(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)]) del self.note_sequence.tempos[:] # No tempos. sequences_lib.quantize_note_sequence( self.note_sequence, self.steps_per_quarter) # Single tempo. self.note_sequence.tempos.add(qpm=60, time=0) sequences_lib.quantize_note_sequence( self.note_sequence, self.steps_per_quarter) # Multiple tempos with no change. self.note_sequence.tempos.add(qpm=60, time=1) sequences_lib.quantize_note_sequence( self.note_sequence, self.steps_per_quarter) # Tempo change. self.note_sequence.tempos.add(qpm=120, time=2) with self.assertRaises(sequences_lib.MultipleTempoError): sequences_lib.quantize_note_sequence( self.note_sequence, self.steps_per_quarter) def testFromNoteSequence_ImplicitTempoChange(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)]) del self.note_sequence.tempos[:] # No tempo. sequences_lib.quantize_note_sequence( self.note_sequence, self.steps_per_quarter) # Implicit tempo change. self.note_sequence.tempos.add(qpm=60, time=2) with self.assertRaises(sequences_lib.MultipleTempoError): sequences_lib.quantize_note_sequence( self.note_sequence, self.steps_per_quarter) def testFromNoteSequence_NoImplicitTempoChangeOutOfOrder(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)]) del self.note_sequence.tempos[:] # No tempo. sequences_lib.quantize_note_sequence( self.note_sequence, self.steps_per_quarter) # No implicit tempo change, but tempos are added out of order. self.note_sequence.tempos.add(qpm=60, time=2) self.note_sequence.tempos.add(qpm=60, time=0) sequences_lib.quantize_note_sequence( self.note_sequence, self.steps_per_quarter) def testRounding(self): testing_lib.add_track_to_sequence( self.note_sequence, 1, [(12, 100, 0.01, 0.24), (11, 100, 0.22, 0.55), (40, 100, 0.50, 0.75), (41, 100, 0.689, 1.18), (44, 100, 1.19, 1.69), (55, 100, 4.0, 4.01)]) expected_quantized_sequence = copy.deepcopy(self.note_sequence) expected_quantized_sequence.quantization_info.steps_per_quarter = ( self.steps_per_quarter) testing_lib.add_quantized_steps_to_sequence( expected_quantized_sequence, [(0, 1), (1, 2), (2, 3), (3, 5), (5, 7), (16, 17)]) quantized_sequence = sequences_lib.quantize_note_sequence( self.note_sequence, self.steps_per_quarter) self.assertProtoEquals(expected_quantized_sequence, quantized_sequence) def testMultiTrack(self): testing_lib.add_track_to_sequence( self.note_sequence, 0, [(12, 100, 1.0, 4.0), (19, 100, 0.95, 3.0)]) testing_lib.add_track_to_sequence( self.note_sequence, 3, [(12, 100, 1.0, 4.0), (19, 100, 2.0, 5.0)]) testing_lib.add_track_to_sequence( self.note_sequence, 7, [(12, 100, 1.0, 5.0), (19, 100, 2.0, 4.0), (24, 100, 3.0, 3.5)]) expected_quantized_sequence = copy.deepcopy(self.note_sequence) expected_quantized_sequence.quantization_info.steps_per_quarter = ( self.steps_per_quarter) testing_lib.add_quantized_steps_to_sequence( expected_quantized_sequence, [(4, 16), (4, 12), (4, 16), (8, 20), (4, 20), (8, 16), (12, 14)]) quantized_sequence = sequences_lib.quantize_note_sequence( self.note_sequence, self.steps_per_quarter) self.assertProtoEquals(expected_quantized_sequence, quantized_sequence) def testStepsPerBar(self): qns = sequences_lib.quantize_note_sequence( self.note_sequence, self.steps_per_quarter) self.assertEqual(16, sequences_lib.steps_per_bar_in_quantized_sequence(qns)) self.note_sequence.time_signatures[0].numerator = 6 self.note_sequence.time_signatures[0].denominator = 8 qns = sequences_lib.quantize_note_sequence( self.note_sequence, self.steps_per_quarter) self.assertEqual(12.0, sequences_lib.steps_per_bar_in_quantized_sequence(qns)) def testStretchNoteSequence(self): expected_stretched_sequence = copy.deepcopy(self.note_sequence) expected_stretched_sequence.tempos[0].qpm = 40 testing_lib.add_track_to_sequence( self.note_sequence, 0, [(12, 100, 0.0, 10.0), (11, 55, 0.2, 0.5), (40, 45, 2.5, 3.5)]) testing_lib.add_track_to_sequence( expected_stretched_sequence, 0, [(12, 100, 0.0, 15.0), (11, 55, 0.3, 0.75), (40, 45, 3.75, 5.25)]) testing_lib.add_chords_to_sequence( self.note_sequence, [('B7', 0.5), ('Em9', 2.0)]) testing_lib.add_chords_to_sequence( expected_stretched_sequence, [('B7', 0.75), ('Em9', 3.0)]) prestretched_sequence = copy.deepcopy(self.note_sequence) stretched_sequence = sequences_lib.stretch_note_sequence( self.note_sequence, stretch_factor=1.5, in_place=False) self.assertProtoEquals(expected_stretched_sequence, stretched_sequence) # Make sure the proto was not modified self.assertProtoEquals(prestretched_sequence, self.note_sequence) sequences_lib.stretch_note_sequence( self.note_sequence, stretch_factor=1.5, in_place=True) self.assertProtoEquals(stretched_sequence, self.note_sequence) def testAdjustNoteSequenceTimes(self): sequence = music_pb2.NoteSequence() sequence.notes.add(pitch=60, start_time=1.0, end_time=5.0) sequence.notes.add(pitch=61, start_time=6.0, end_time=7.0) sequence.control_changes.add(control_number=1, time=2.0) sequence.pitch_bends.add(bend=5, time=2.0) sequence.total_time = 7.0 adjusted_ns, skipped_notes = sequences_lib.adjust_notesequence_times( sequence, lambda t: t - 1) expected_sequence = music_pb2.NoteSequence() expected_sequence.notes.add(pitch=60, start_time=0.0, end_time=4.0) expected_sequence.notes.add(pitch=61, start_time=5.0, end_time=6.0) expected_sequence.control_changes.add(control_number=1, time=1.0) expected_sequence.pitch_bends.add(bend=5, time=1.0) expected_sequence.total_time = 6.0 self.assertEqual(expected_sequence, adjusted_ns) self.assertEqual(0, skipped_notes) def testAdjustNoteSequenceTimesWithSkippedNotes(self): sequence = music_pb2.NoteSequence() sequence.notes.add(pitch=60, start_time=1.0, end_time=5.0) sequence.notes.add(pitch=61, start_time=6.0, end_time=7.0) sequence.notes.add(pitch=62, start_time=7.0, end_time=8.0) sequence.total_time = 8.0 def time_func(time): if time > 5: return 5 else: return time adjusted_ns, skipped_notes = sequences_lib.adjust_notesequence_times( sequence, time_func) expected_sequence = music_pb2.NoteSequence() expected_sequence.notes.add(pitch=60, start_time=1.0, end_time=5.0) expected_sequence.total_time = 5.0 self.assertEqual(expected_sequence, adjusted_ns) self.assertEqual(2, skipped_notes) def testAdjustNoteSequenceTimesWithNotesBeforeTimeZero(self): sequence = music_pb2.NoteSequence() sequence.notes.add(pitch=60, start_time=1.0, end_time=5.0) sequence.notes.add(pitch=61, start_time=6.0, end_time=7.0) sequence.notes.add(pitch=62, start_time=7.0, end_time=8.0) sequence.total_time = 8.0 def time_func(time): return time - 5 with self.assertRaises(sequences_lib.InvalidTimeAdjustmentError): sequences_lib.adjust_notesequence_times(sequence, time_func) def testAdjustNoteSequenceTimesWithZeroDurations(self): sequence = music_pb2.NoteSequence() sequence.notes.add(pitch=60, start_time=1.0, end_time=2.0) sequence.notes.add(pitch=61, start_time=3.0, end_time=4.0) sequence.notes.add(pitch=62, start_time=5.0, end_time=6.0) sequence.total_time = 8.0 def time_func(time): if time % 2 == 0: return time - 1 else: return time adjusted_ns, skipped_notes = sequences_lib.adjust_notesequence_times( sequence, time_func) expected_sequence = music_pb2.NoteSequence() self.assertEqual(expected_sequence, adjusted_ns) self.assertEqual(3, skipped_notes) adjusted_ns, skipped_notes = sequences_lib.adjust_notesequence_times( sequence, time_func, minimum_duration=.1) expected_sequence = music_pb2.NoteSequence() expected_sequence.notes.add(pitch=60, start_time=1.0, end_time=1.1) expected_sequence.notes.add(pitch=61, start_time=3.0, end_time=3.1) expected_sequence.notes.add(pitch=62, start_time=5.0, end_time=5.1) expected_sequence.total_time = 5.1 self.assertEqual(expected_sequence, adjusted_ns) self.assertEqual(0, skipped_notes) def testAdjustNoteSequenceTimesEndBeforeStart(self): sequence = music_pb2.NoteSequence() sequence.notes.add(pitch=60, start_time=1.0, end_time=2.0) sequence.notes.add(pitch=61, start_time=3.0, end_time=4.0) sequence.notes.add(pitch=62, start_time=5.0, end_time=6.0) sequence.total_time = 8.0 def time_func(time): if time % 2 == 0: return time - 2 else: return time with self.assertRaises(sequences_lib.InvalidTimeAdjustmentError): sequences_lib.adjust_notesequence_times(sequence, time_func) def testRectifyBeats(self): sequence = music_pb2.NoteSequence() testing_lib.add_track_to_sequence( sequence, 0, [(60, 100, 0.25, 0.5), (62, 100, 0.5, 0.75), (64, 100, 0.75, 2.5), (65, 100, 1.0, 1.5), (67, 100, 1.5, 2.0)]) testing_lib.add_beats_to_sequence(sequence, [0.5, 1.0, 2.0]) rectified_sequence, alignment = sequences_lib.rectify_beats( sequence, 120) expected_sequence = music_pb2.NoteSequence() expected_sequence.tempos.add(qpm=120) testing_lib.add_track_to_sequence( expected_sequence, 0, [(60, 100, 0.25, 0.5), (62, 100, 0.5, 0.75), (64, 100, 0.75, 2.0), (65, 100, 1.0, 1.25), (67, 100, 1.25, 1.5)]) testing_lib.add_beats_to_sequence(expected_sequence, [0.5, 1.0, 1.5]) self.assertEqual(expected_sequence, rectified_sequence) expected_alignment = [ [0.0, 0.5, 1.0, 2.0, 2.5], [0.0, 0.5, 1.0, 1.5, 2.0] ] self.assertEqual(expected_alignment, alignment.T.tolist()) 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 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 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 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 testApplySustainControlChangesWithDrumNotes(self): """Drum notes should not be modified when applying sustain changes.""" 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)]) testing_lib.add_track_to_sequence( sequence, 0, [(38, 100, 2.00, 2.50)], is_drum=True) testing_lib.add_track_to_sequence( expected_sequence, 0, [(60, 100, 2.00, 4.00)]) testing_lib.add_track_to_sequence( expected_sequence, 0, [(38, 100, 2.0, 2.5)], is_drum=True) sus_sequence = sequences_lib.apply_sustain_control_changes(sequence) self.assertProtoEquals(expected_sequence, sus_sequence) def testApplySustainControlChangesProcessSustainBeforeNotes(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)]) expected_sequence = copy.copy(sequence) testing_lib.add_track_to_sequence( sequence, 0, [(11, 55, 0.22, 0.75), (40, 45, 2.50, 3.50)]) testing_lib.add_track_to_sequence( expected_sequence, 0, [(11, 55, 0.22, 0.75), (40, 45, 2.50, 3.50)]) sus_sequence = sequences_lib.apply_sustain_control_changes(sequence) self.assertProtoEquals(expected_sequence, sus_sequence) def testInferDenseChordsForSequence(self): # Test non-quantized sequence. sequence = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( sequence, 0, [(60, 100, 1.0, 3.0), (64, 100, 1.0, 2.0), (67, 100, 1.0, 2.0), (65, 100, 2.0, 3.0), (69, 100, 2.0, 3.0), (62, 100, 3.0, 5.0), (65, 100, 3.0, 4.0), (69, 100, 3.0, 4.0)]) expected_sequence = copy.copy(sequence) testing_lib.add_chords_to_sequence( expected_sequence, [('C', 1.0), ('F/C', 2.0), ('Dm', 3.0)]) sequences_lib.infer_dense_chords_for_sequence(sequence) self.assertProtoEquals(expected_sequence, sequence) # Test quantized sequence. sequence = copy.copy(self.note_sequence) sequence.quantization_info.steps_per_quarter = 1 testing_lib.add_track_to_sequence( sequence, 0, [(60, 100, 1.1, 3.0), (64, 100, 1.0, 1.9), (67, 100, 1.0, 2.0), (65, 100, 2.0, 3.2), (69, 100, 2.1, 3.1), (62, 100, 2.9, 4.8), (65, 100, 3.0, 4.0), (69, 100, 3.0, 4.1)]) testing_lib.add_quantized_steps_to_sequence( sequence, [(1, 3), (1, 2), (1, 2), (2, 3), (2, 3), (3, 5), (3, 4), (3, 4)]) expected_sequence = copy.copy(sequence) testing_lib.add_chords_to_sequence( expected_sequence, [('C', 1.0), ('F/C', 2.0), ('Dm', 3.0)]) testing_lib.add_quantized_chord_steps_to_sequence( expected_sequence, [1, 2, 3]) sequences_lib.infer_dense_chords_for_sequence(sequence) self.assertProtoEquals(expected_sequence, 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) def testConcatenateSequences(self): sequence1 = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( sequence1, 0, [(60, 100, 0.0, 1.0), (72, 100, 0.5, 1.5)]) sequence2 = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( sequence2, 0, [(59, 100, 0.0, 1.0), (71, 100, 0.5, 1.5)]) expected_sequence = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( expected_sequence, 0, [(60, 100, 0.0, 1.0), (72, 100, 0.5, 1.5), (59, 100, 1.5, 2.5), (71, 100, 2.0, 3.0)]) cat_seq = sequences_lib.concatenate_sequences([sequence1, sequence2]) self.assertProtoEquals(expected_sequence, cat_seq) def testConcatenateSequencesWithSpecifiedDurations(self): sequence1 = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( sequence1, 0, [(60, 100, 0.0, 1.0), (72, 100, 0.5, 1.5)]) sequence2 = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( sequence2, 0, [(59, 100, 0.0, 1.0)]) sequence3 = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( sequence3, 0, [(72, 100, 0.0, 1.0), (73, 100, 0.5, 1.5)]) expected_sequence = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( expected_sequence, 0, [(60, 100, 0.0, 1.0), (72, 100, 0.5, 1.5), (59, 100, 2.0, 3.0), (72, 100, 3.5, 4.5), (73, 100, 4.0, 5.0)]) cat_seq = sequences_lib.concatenate_sequences( [sequence1, sequence2, sequence3], sequence_durations=[2, 1.5, 2]) self.assertProtoEquals(expected_sequence, cat_seq) def testRepeatSequenceToDuration(self): sequence = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( sequence, 0, [(60, 100, 0.0, 1.0), (72, 100, 0.5, 1.5)]) expected_sequence = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( expected_sequence, 0, [(60, 100, 0.0, 1.0), (72, 100, 0.5, 1.5), (60, 100, 1.5, 2.5), (72, 100, 2.0, 3.0)]) repeated_seq = sequences_lib.repeat_sequence_to_duration( sequence, duration=3) self.assertProtoEquals(expected_sequence, repeated_seq) def testRepeatSequenceToDurationProvidedDuration(self): sequence = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( sequence, 0, [(60, 100, 0.0, 1.0), (72, 100, 0.5, 1.5)]) expected_sequence = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( expected_sequence, 0, [(60, 100, 0.0, 1.0), (72, 100, 0.5, 1.5), (60, 100, 2.0, 3.0), (72, 100, 2.5, 3.0)]) repeated_seq = sequences_lib.repeat_sequence_to_duration( sequence, duration=3, sequence_duration=2) self.assertProtoEquals(expected_sequence, repeated_seq) def testRemoveRedundantData(self): sequence = copy.copy(self.note_sequence) redundant_tempo = sequence.tempos.add() redundant_tempo.CopyFrom(sequence.tempos[0]) redundant_tempo.time = 5.0 sequence.sequence_metadata.composers.append('Foo') sequence.sequence_metadata.composers.append('Bar') sequence.sequence_metadata.composers.append('Foo') sequence.sequence_metadata.composers.append('Bar') sequence.sequence_metadata.genre.append('Classical') sequence.sequence_metadata.genre.append('Classical') fixed_sequence = sequences_lib.remove_redundant_data(sequence) expected_sequence = copy.copy(self.note_sequence) expected_sequence.sequence_metadata.composers.append('Foo') expected_sequence.sequence_metadata.composers.append('Bar') expected_sequence.sequence_metadata.genre.append('Classical') self.assertProtoEquals(expected_sequence, fixed_sequence) def testRemoveRedundantDataOutOfOrder(self): sequence = copy.copy(self.note_sequence) meaningful_tempo = sequence.tempos.add() meaningful_tempo.time = 5.0 meaningful_tempo.qpm = 50 redundant_tempo = sequence.tempos.add() redundant_tempo.CopyFrom(sequence.tempos[0]) expected_sequence = copy.copy(self.note_sequence) expected_meaningful_tempo = expected_sequence.tempos.add() expected_meaningful_tempo.time = 5.0 expected_meaningful_tempo.qpm = 50 fixed_sequence = sequences_lib.remove_redundant_data(sequence) self.assertProtoEquals(expected_sequence, fixed_sequence) def testExpandSectionGroups(self): sequence = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( sequence, 0, [(60, 100, 0.0, 1.0), (72, 100, 1.0, 2.0), (59, 100, 2.0, 3.0), (71, 100, 3.0, 4.0)]) sequence.section_annotations.add(time=0, section_id=0) sequence.section_annotations.add(time=1, section_id=1) sequence.section_annotations.add(time=2, section_id=2) sequence.section_annotations.add(time=3, section_id=3) # A((BC)2D)2 sg = sequence.section_groups.add() sg.sections.add(section_id=0) sg.num_times = 1 sg = sequence.section_groups.add() sg.sections.add(section_group=music_pb2.NoteSequence.SectionGroup( sections=[music_pb2.NoteSequence.Section(section_id=1), music_pb2.NoteSequence.Section(section_id=2)], num_times=2)) sg.sections.add(section_id=3) sg.num_times = 2 expanded = sequences_lib.expand_section_groups(sequence) expected = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( expected, 0, [(60, 100, 0.0, 1.0), (72, 100, 1.0, 2.0), (59, 100, 2.0, 3.0), (72, 100, 3.0, 4.0), (59, 100, 4.0, 5.0), (71, 100, 5.0, 6.0), (72, 100, 6.0, 7.0), (59, 100, 7.0, 8.0), (72, 100, 8.0, 9.0), (59, 100, 9.0, 10.0), (71, 100, 10.0, 11.0)]) expected.section_annotations.add(time=0, section_id=0) expected.section_annotations.add(time=1, section_id=1) expected.section_annotations.add(time=2, section_id=2) expected.section_annotations.add(time=3, section_id=1) expected.section_annotations.add(time=4, section_id=2) expected.section_annotations.add(time=5, section_id=3) expected.section_annotations.add(time=6, section_id=1) expected.section_annotations.add(time=7, section_id=2) expected.section_annotations.add(time=8, section_id=1) expected.section_annotations.add(time=9, section_id=2) expected.section_annotations.add(time=10, section_id=3) self.assertProtoEquals(expected, expanded) def testExpandWithoutSectionGroups(self): sequence = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( sequence, 0, [(60, 100, 0.0, 1.0), (72, 100, 1.0, 2.0), (59, 100, 2.0, 3.0), (71, 100, 3.0, 4.0)]) sequence.section_annotations.add(time=0, section_id=0) sequence.section_annotations.add(time=1, section_id=1) sequence.section_annotations.add(time=2, section_id=2) sequence.section_annotations.add(time=3, section_id=3) expanded = sequences_lib.expand_section_groups(sequence) self.assertEqual(sequence, expanded) def testSequenceToPianoroll(self): sequence = music_pb2.NoteSequence(total_time=1.21) testing_lib.add_track_to_sequence(sequence, 0, [(1, 100, 0.11, 1.01), (2, 55, 0.22, 0.50), (3, 100, 0.3, 0.8), (2, 45, 1.0, 1.21)]) pianoroll_tuple = sequences_lib.sequence_to_pianoroll( sequence, frames_per_second=10, min_pitch=1, max_pitch=2) output = pianoroll_tuple.active offset = pianoroll_tuple.offsets expected_pianoroll = [[0, 0], [1, 0], [1, 1], [1, 1], [1, 1], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 1], [0, 1], [0, 1]] expected_offsets = [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 1], [0, 0], [0, 0], [0, 0], [0, 0], [1, 0], [0, 0], [0, 1]] np.testing.assert_allclose(expected_pianoroll, output) np.testing.assert_allclose(expected_offsets, offset) def testSequenceToPianorollWithBlankFrameBeforeOffset(self): sequence = music_pb2.NoteSequence(total_time=1.5) testing_lib.add_track_to_sequence(sequence, 0, [(1, 100, 0.00, 1.00), (2, 100, 0.20, 0.50), (1, 100, 1.20, 1.50), (2, 100, 0.50, 1.50)]) expected_pianoroll = [ [1, 0], [1, 0], [1, 1], [1, 1], [1, 1], [1, 1], [1, 1], [1, 1], [1, 1], [1, 1], [0, 1], [0, 1], [1, 1], [1, 1], [1, 1], [0, 0], ] output = sequences_lib.sequence_to_pianoroll( sequence, frames_per_second=10, min_pitch=1, max_pitch=2).active np.testing.assert_allclose(expected_pianoroll, output) expected_pianoroll_with_blank_frame = [ [1, 0], [1, 0], [1, 1], [1, 1], [1, 0], [1, 1], [1, 1], [1, 1], [1, 1], [1, 1], [0, 1], [0, 1], [1, 1], [1, 1], [1, 1], [0, 0], ] output_with_blank_frame = sequences_lib.sequence_to_pianoroll( sequence, frames_per_second=10, min_pitch=1, max_pitch=2, add_blank_frame_before_onset=True).active np.testing.assert_allclose(expected_pianoroll_with_blank_frame, output_with_blank_frame) def testSequenceToPianorollWithBlankFrameBeforeOffsetOutOfOrder(self): sequence = music_pb2.NoteSequence(total_time=.5) testing_lib.add_track_to_sequence(sequence, 0, [(1, 100, 0.20, 0.50), (1, 100, 0.00, 0.20)]) expected_pianoroll = [ [1], [0], [1], [1], [1], [0], ] output = sequences_lib.sequence_to_pianoroll( sequence, frames_per_second=10, min_pitch=1, max_pitch=1, add_blank_frame_before_onset=True).active np.testing.assert_allclose(expected_pianoroll, output) def testSequenceToPianorollWeightedRoll(self): sequence = music_pb2.NoteSequence(total_time=2.0) testing_lib.add_track_to_sequence(sequence, 0, [(1, 100, 0.00, 1.00), (2, 100, 0.20, 0.50), (3, 100, 1.20, 1.50), (4, 100, 0.40, 2.00), (6, 100, 0.10, 0.60)]) onset_upweight = 5.0 expected_roll_weights = [ [onset_upweight, onset_upweight, 1, onset_upweight], [onset_upweight, onset_upweight, onset_upweight, onset_upweight], [1, 1, onset_upweight, onset_upweight / 1], [1, 1, onset_upweight, onset_upweight / 2], [1, 1, 1, 1], ] expected_onsets = [ [1, 1, 0, 1], [1, 1, 1, 1], [0, 0, 1, 0], [0, 0, 1, 0], [0, 0, 0, 0], ] roll = sequences_lib.sequence_to_pianoroll( sequence, frames_per_second=2, min_pitch=1, max_pitch=4, onset_upweight=onset_upweight) np.testing.assert_allclose(expected_roll_weights, roll.weights) np.testing.assert_allclose(expected_onsets, roll.onsets) def testSequenceToPianorollOnsets(self): sequence = music_pb2.NoteSequence() sequence.notes.add(pitch=60, start_time=2.0, end_time=5.0) sequence.notes.add(pitch=61, start_time=6.0, end_time=7.0) sequence.notes.add(pitch=62, start_time=7.0, end_time=8.0) sequence.total_time = 8.0 onsets = sequences_lib.sequence_to_pianoroll( sequence, 100, 60, 62, onset_mode='length_ms', onset_length_ms=100.0, onset_delay_ms=10.0, min_frame_occupancy_for_label=.999).onsets expected_roll = np.zeros([801, 3]) expected_roll[201:211, 0] = 1. expected_roll[601:611, 1] = 1. expected_roll[701:711, 2] = 1. np.testing.assert_equal(expected_roll, onsets) def testSequenceToPianorollFrameOccupancy(self): sequence = music_pb2.NoteSequence() sequence.notes.add(pitch=60, start_time=1.0, end_time=1.7) sequence.notes.add(pitch=61, start_time=6.2, end_time=6.55) sequence.notes.add(pitch=62, start_time=3.4, end_time=4.3) sequence.total_time = 6.55 active = sequences_lib.sequence_to_pianoroll( sequence, 2, 60, 62, min_frame_occupancy_for_label=0.5).active expected_roll = np.zeros([14, 3]) expected_roll[2:3, 0] = 1. expected_roll[12:13, 1] = 1. expected_roll[7:9, 2] = 1. np.testing.assert_equal(expected_roll, active) def testSequenceToPianorollOnsetVelocities(self): sequence = music_pb2.NoteSequence() sequence.notes.add(pitch=60, start_time=0.0, end_time=2.0, velocity=16) sequence.notes.add(pitch=61, start_time=0.0, end_time=2.0, velocity=32) sequence.notes.add(pitch=62, start_time=0.0, end_time=2.0, velocity=64) sequence.total_time = 2.0 roll = sequences_lib.sequence_to_pianoroll( sequence, 1, 60, 62, max_velocity=64, onset_window=0) onset_velocities = roll.onset_velocities self.assertEqual(onset_velocities[0, 0], 0.25) self.assertEqual(onset_velocities[0, 1], 0.5) self.assertEqual(onset_velocities[0, 2], 1.) self.assertEqual(np.all(onset_velocities[1:] == 0), True) def testSequenceToPianorollActiveVelocities(self): sequence = music_pb2.NoteSequence() sequence.notes.add(pitch=60, start_time=0.0, end_time=2.0, velocity=16) sequence.notes.add(pitch=61, start_time=0.0, end_time=2.0, velocity=32) sequence.notes.add(pitch=62, start_time=0.0, end_time=2.0, velocity=64) sequence.total_time = 2.0 roll = sequences_lib.sequence_to_pianoroll( sequence, 1, 60, 62, max_velocity=64) active_velocities = roll.active_velocities self.assertEqual(np.all(active_velocities[0:2, 0] == 0.25), True) self.assertEqual(np.all(active_velocities[0:2, 1] == 0.5), True) self.assertEqual(np.all(active_velocities[0:2, 2] == 1.), True) self.assertEqual(np.all(active_velocities[2:] == 0), True) def testPianorollToNoteSequence(self): # 100 frames of notes. frames = np.zeros((100, MIDI_PITCHES), np.bool) # Activate key 39 for the middle 50 frames. frames[25:75, 39] = True sequence = sequences_lib.pianoroll_to_note_sequence( frames, frames_per_second=DEFAULT_FRAMES_PER_SECOND, min_duration_ms=0) self.assertLen(sequence.notes, 1) self.assertEqual(39, sequence.notes[0].pitch) self.assertEqual(25 / DEFAULT_FRAMES_PER_SECOND, sequence.notes[0].start_time) self.assertEqual(75 / DEFAULT_FRAMES_PER_SECOND, sequence.notes[0].end_time) def testPianorollToNoteSequenceAllNotes(self): # Test all 128 notes frames = np.eye(MIDI_PITCHES, dtype=np.bool) # diagonal identity matrix sequence = sequences_lib.pianoroll_to_note_sequence( frames, frames_per_second=DEFAULT_FRAMES_PER_SECOND, min_duration_ms=0) self.assertLen(sequence.notes, MIDI_PITCHES) for i in range(MIDI_PITCHES): self.assertEqual(i, sequence.notes[i].pitch) self.assertAlmostEqual(i / DEFAULT_FRAMES_PER_SECOND, sequence.notes[i].start_time) self.assertAlmostEqual((i+1) / DEFAULT_FRAMES_PER_SECOND, sequence.notes[i].end_time) def testPianorollToNoteSequenceWithOnsets(self): # 100 frames of notes and onsets. frames = np.zeros((100, MIDI_PITCHES), np.bool) onsets = np.zeros((100, MIDI_PITCHES), np.bool) # Activate key 39 for the middle 50 frames and last 10 frames. frames[25:75, 39] = True frames[90:100, 39] = True # Add an onset for the first occurrence. onsets[25, 39] = True # Add an onset for a note that doesn't have an active frame. onsets[80, 49] = True sequence = sequences_lib.pianoroll_to_note_sequence( frames, frames_per_second=DEFAULT_FRAMES_PER_SECOND, min_duration_ms=0, onset_predictions=onsets) self.assertLen(sequence.notes, 2) self.assertEqual(39, sequence.notes[0].pitch) self.assertEqual(25 / DEFAULT_FRAMES_PER_SECOND, sequence.notes[0].start_time) self.assertEqual(75 / DEFAULT_FRAMES_PER_SECOND, sequence.notes[0].end_time) self.assertEqual(49, sequence.notes[1].pitch) self.assertEqual(80 / DEFAULT_FRAMES_PER_SECOND, sequence.notes[1].start_time) self.assertEqual(81 / DEFAULT_FRAMES_PER_SECOND, sequence.notes[1].end_time) def testPianorollToNoteSequenceWithOnsetsAndVelocity(self): # 100 frames of notes and onsets. frames = np.zeros((100, MIDI_PITCHES), np.bool) onsets = np.zeros((100, MIDI_PITCHES), np.bool) velocity_values = np.zeros((100, MIDI_PITCHES), np.float32) # Activate key 39 for the middle 50 frames and last 10 frames. frames[25:75, 39] = True frames[90:100, 39] = True # Add an onset for the first occurrence with a valid velocity. onsets[25, 39] = True velocity_values[25, 39] = 0.5 # Add an onset for the second occurrence with a NaN velocity. onsets[90, 39] = True velocity_values[90, 39] = float('nan') sequence = sequences_lib.pianoroll_to_note_sequence( frames, frames_per_second=DEFAULT_FRAMES_PER_SECOND, min_duration_ms=0, onset_predictions=onsets, velocity_values=velocity_values) self.assertLen(sequence.notes, 2) self.assertEqual(39, sequence.notes[0].pitch) self.assertEqual(50, sequence.notes[0].velocity) self.assertEqual(39, sequence.notes[1].pitch) self.assertEqual(0, sequence.notes[1].velocity) def testPianorollToNoteSequenceWithOnsetsAndFullScaleVelocity(self): # 100 frames of notes and onsets. frames = np.zeros((100, MIDI_PITCHES), np.bool) onsets = np.zeros((100, MIDI_PITCHES), np.bool) velocity_values = np.zeros((100, MIDI_PITCHES), np.float32) # Activate key 39 for the middle 50 frames and last 10 frames. frames[25:75, 39] = True frames[90:100, 39] = True onsets[25, 39] = True velocity_values[25, 39] = 0.5 onsets[90, 39] = True velocity_values[90, 39] = 1.0 sequence = sequences_lib.pianoroll_to_note_sequence( frames, frames_per_second=DEFAULT_FRAMES_PER_SECOND, min_duration_ms=0, onset_predictions=onsets, velocity_values=velocity_values, velocity_scale=127, velocity_bias=0) self.assertLen(sequence.notes, 2) self.assertEqual(39, sequence.notes[0].pitch) self.assertEqual(63, sequence.notes[0].velocity) self.assertEqual(39, sequence.notes[1].pitch) self.assertEqual(127, sequence.notes[1].velocity) def testPianorollToNoteSequenceWithOnsetsDefaultVelocity(self): # 100 frames of notes and onsets. frames = np.zeros((100, MIDI_PITCHES), np.bool) onsets = np.zeros((100, MIDI_PITCHES), np.bool) # Activate key 39 for the middle 50 frames and last 10 frames. frames[25:75, 39] = True frames[90:100, 39] = True onsets[25, 39] = True onsets[90, 39] = True sequence = sequences_lib.pianoroll_to_note_sequence( frames, frames_per_second=DEFAULT_FRAMES_PER_SECOND, min_duration_ms=0, onset_predictions=onsets, velocity=100) self.assertLen(sequence.notes, 2) self.assertEqual(39, sequence.notes[0].pitch) self.assertEqual(100, sequence.notes[0].velocity) self.assertEqual(39, sequence.notes[1].pitch) self.assertEqual(100, sequence.notes[1].velocity) def testPianorollToNoteSequenceWithOnsetsOverlappingFrames(self): # 100 frames of notes and onsets. frames = np.zeros((100, MIDI_PITCHES), np.bool) onsets = np.zeros((100, MIDI_PITCHES), np.bool) # Activate key 39 for the middle 50 frames. frames[25:75, 39] = True # Add multiple onsets within those frames. onsets[25, 39] = True onsets[30, 39] = True # If an onset lasts for multiple frames, it should create only 1 note. onsets[35, 39] = True onsets[36, 39] = True sequence = sequences_lib.pianoroll_to_note_sequence( frames, frames_per_second=DEFAULT_FRAMES_PER_SECOND, min_duration_ms=0, onset_predictions=onsets) self.assertLen(sequence.notes, 3) self.assertEqual(39, sequence.notes[0].pitch) self.assertEqual(25 / DEFAULT_FRAMES_PER_SECOND, sequence.notes[0].start_time) self.assertEqual(30 / DEFAULT_FRAMES_PER_SECOND, sequence.notes[0].end_time) self.assertEqual(39, sequence.notes[1].pitch) self.assertEqual(30 / DEFAULT_FRAMES_PER_SECOND, sequence.notes[1].start_time) self.assertEqual(35 / DEFAULT_FRAMES_PER_SECOND, sequence.notes[1].end_time) self.assertEqual(39, sequence.notes[2].pitch) self.assertEqual(35 / DEFAULT_FRAMES_PER_SECOND, sequence.notes[2].start_time) self.assertEqual(75 / DEFAULT_FRAMES_PER_SECOND, sequence.notes[2].end_time) def testPianorollOnsetsToNoteSequence(self): onsets = np.zeros((10, 2), np.bool) velocity_values = np.zeros_like(onsets, np.float32) onsets[0:2, 0] = True velocity_values[0:2, 0] = .5 onsets[1:2, 1] = True velocity_values[1:2, 1] = 1 sequence = sequences_lib.pianoroll_onsets_to_note_sequence( onsets, frames_per_second=10, note_duration_seconds=0.05, min_midi_pitch=60, velocity_values=velocity_values) self.assertLen(sequence.notes, 3) self.assertEqual(60, sequence.notes[0].pitch) self.assertEqual(0, sequence.notes[0].start_time) self.assertAlmostEqual(0.05, sequence.notes[0].end_time) self.assertEqual(50, sequence.notes[0].velocity) self.assertEqual(60, sequence.notes[1].pitch) self.assertEqual(0.1, sequence.notes[1].start_time) self.assertAlmostEqual(0.15, sequence.notes[1].end_time) self.assertEqual(50, sequence.notes[1].velocity) self.assertEqual(61, sequence.notes[2].pitch) self.assertEqual(0.1, sequence.notes[2].start_time) self.assertAlmostEqual(0.15, sequence.notes[2].end_time) self.assertEqual(90, sequence.notes[2].velocity) def testPianorollOnsetsToNoteSequenceFullVelocityScale(self): onsets = np.zeros((10, 2), np.bool) velocity_values = np.zeros_like(onsets, np.float32) onsets[0:2, 0] = True velocity_values[0:2, 0] = .5 onsets[1:2, 1] = True velocity_values[1:2, 1] = 1 sequence = sequences_lib.pianoroll_onsets_to_note_sequence( onsets, frames_per_second=10, note_duration_seconds=0.05, min_midi_pitch=60, velocity_values=velocity_values, velocity_scale=127, velocity_bias=0) self.assertLen(sequence.notes, 3) self.assertEqual(60, sequence.notes[0].pitch) self.assertEqual(0, sequence.notes[0].start_time) self.assertAlmostEqual(0.05, sequence.notes[0].end_time) self.assertEqual(63, sequence.notes[0].velocity) self.assertEqual(60, sequence.notes[1].pitch) self.assertEqual(0.1, sequence.notes[1].start_time) self.assertAlmostEqual(0.15, sequence.notes[1].end_time) self.assertEqual(63, sequence.notes[1].velocity) self.assertEqual(61, sequence.notes[2].pitch) self.assertEqual(0.1, sequence.notes[2].start_time) self.assertAlmostEqual(0.15, sequence.notes[2].end_time) self.assertEqual(127, sequence.notes[2].velocity) def testSequenceToPianorollControlChanges(self): sequence = music_pb2.NoteSequence(total_time=2.0) cc = music_pb2.NoteSequence.ControlChange sequence.control_changes.extend([ cc(time=0.7, control_number=3, control_value=16), cc(time=0.0, control_number=4, control_value=32), cc(time=0.5, control_number=4, control_value=32), cc(time=1.6, control_number=3, control_value=64), ]) expected_cc_roll = np.zeros((5, 128), dtype=np.int32) expected_cc_roll[0:2, 4] = 33 expected_cc_roll[1, 3] = 17 expected_cc_roll[3, 3] = 65 cc_roll = sequences_lib.sequence_to_pianoroll( sequence, frames_per_second=2, min_pitch=1, max_pitch=4).control_changes np.testing.assert_allclose(expected_cc_roll, cc_roll) def testSequenceToPianorollOverlappingNotes(self): sequence = music_pb2.NoteSequence() sequence.notes.add(pitch=60, start_time=1.0, end_time=2.0) sequence.notes.add(pitch=60, start_time=1.2, end_time=2.0) sequence.notes.add(pitch=60, start_time=1.0, end_time=2.5) sequence.total_time = 2.5 rolls = sequences_lib.sequence_to_pianoroll( sequence, frames_per_second=10, min_pitch=60, max_pitch=60, onset_mode='length_ms', onset_length_ms=10) expected_onsets = np.zeros([26, 1]) expected_onsets[10, 0] = 1 expected_onsets[12, 0] = 1 np.testing.assert_equal(expected_onsets, rolls.onsets) expected_offsets = np.zeros([26, 1]) expected_offsets[20, 0] = 1 expected_offsets[25, 0] = 1 np.testing.assert_equal(expected_offsets, rolls.offsets) expected_active = np.zeros([26, 1]) expected_active[10:25, 0] = 1 np.testing.assert_equal(expected_active, rolls.active) def testSequenceToPianorollShortNotes(self): sequence = music_pb2.NoteSequence() sequence.notes.add(pitch=60, start_time=1.0, end_time=1.0001) sequence.notes.add(pitch=60, start_time=1.2, end_time=1.2001) sequence.total_time = 2.5 rolls = sequences_lib.sequence_to_pianoroll( sequence, frames_per_second=10, min_pitch=60, max_pitch=60, onset_mode='length_ms', onset_length_ms=0) expected_onsets = np.zeros([26, 1]) expected_onsets[10, 0] = 1 expected_onsets[12, 0] = 1 np.testing.assert_equal(expected_onsets, rolls.onsets) expected_offsets = np.zeros([26, 1]) expected_offsets[10, 0] = 1 expected_offsets[12, 0] = 1 np.testing.assert_equal(expected_offsets, rolls.offsets) expected_active = np.zeros([26, 1]) expected_active[10:11, 0] = 1 expected_active[12:13, 0] = 1 np.testing.assert_equal(expected_active, rolls.active) def testSequenceToValuedIntervals(self): sequence = music_pb2.NoteSequence() sequence.notes.add(pitch=69, start_time=1.0, end_time=2.0, velocity=80) # Should be dropped because it is 0 duration. sequence.notes.add(pitch=60, start_time=3.0, end_time=3.0, velocity=90) intervals, pitches, velocities = sequences_lib.sequence_to_valued_intervals( sequence) np.testing.assert_array_equal([[1., 2.]], intervals) np.testing.assert_array_equal([440.0], pitches) np.testing.assert_array_equal([80], velocities)
Ancestors
- ProtoTestCase
- absl.testing.absltest.TestCase
- absl.third_party.unittest3_backport.case.TestCase
- unittest.case.TestCase
Methods
def testAdjustNoteSequenceTimes(self)
-
Expand source code
def testAdjustNoteSequenceTimes(self): sequence = music_pb2.NoteSequence() sequence.notes.add(pitch=60, start_time=1.0, end_time=5.0) sequence.notes.add(pitch=61, start_time=6.0, end_time=7.0) sequence.control_changes.add(control_number=1, time=2.0) sequence.pitch_bends.add(bend=5, time=2.0) sequence.total_time = 7.0 adjusted_ns, skipped_notes = sequences_lib.adjust_notesequence_times( sequence, lambda t: t - 1) expected_sequence = music_pb2.NoteSequence() expected_sequence.notes.add(pitch=60, start_time=0.0, end_time=4.0) expected_sequence.notes.add(pitch=61, start_time=5.0, end_time=6.0) expected_sequence.control_changes.add(control_number=1, time=1.0) expected_sequence.pitch_bends.add(bend=5, time=1.0) expected_sequence.total_time = 6.0 self.assertEqual(expected_sequence, adjusted_ns) self.assertEqual(0, skipped_notes)
def testAdjustNoteSequenceTimesEndBeforeStart(self)
-
Expand source code
def testAdjustNoteSequenceTimesEndBeforeStart(self): sequence = music_pb2.NoteSequence() sequence.notes.add(pitch=60, start_time=1.0, end_time=2.0) sequence.notes.add(pitch=61, start_time=3.0, end_time=4.0) sequence.notes.add(pitch=62, start_time=5.0, end_time=6.0) sequence.total_time = 8.0 def time_func(time): if time % 2 == 0: return time - 2 else: return time with self.assertRaises(sequences_lib.InvalidTimeAdjustmentError): sequences_lib.adjust_notesequence_times(sequence, time_func)
def testAdjustNoteSequenceTimesWithNotesBeforeTimeZero(self)
-
Expand source code
def testAdjustNoteSequenceTimesWithNotesBeforeTimeZero(self): sequence = music_pb2.NoteSequence() sequence.notes.add(pitch=60, start_time=1.0, end_time=5.0) sequence.notes.add(pitch=61, start_time=6.0, end_time=7.0) sequence.notes.add(pitch=62, start_time=7.0, end_time=8.0) sequence.total_time = 8.0 def time_func(time): return time - 5 with self.assertRaises(sequences_lib.InvalidTimeAdjustmentError): sequences_lib.adjust_notesequence_times(sequence, time_func)
def testAdjustNoteSequenceTimesWithSkippedNotes(self)
-
Expand source code
def testAdjustNoteSequenceTimesWithSkippedNotes(self): sequence = music_pb2.NoteSequence() sequence.notes.add(pitch=60, start_time=1.0, end_time=5.0) sequence.notes.add(pitch=61, start_time=6.0, end_time=7.0) sequence.notes.add(pitch=62, start_time=7.0, end_time=8.0) sequence.total_time = 8.0 def time_func(time): if time > 5: return 5 else: return time adjusted_ns, skipped_notes = sequences_lib.adjust_notesequence_times( sequence, time_func) expected_sequence = music_pb2.NoteSequence() expected_sequence.notes.add(pitch=60, start_time=1.0, end_time=5.0) expected_sequence.total_time = 5.0 self.assertEqual(expected_sequence, adjusted_ns) self.assertEqual(2, skipped_notes)
def testAdjustNoteSequenceTimesWithZeroDurations(self)
-
Expand source code
def testAdjustNoteSequenceTimesWithZeroDurations(self): sequence = music_pb2.NoteSequence() sequence.notes.add(pitch=60, start_time=1.0, end_time=2.0) sequence.notes.add(pitch=61, start_time=3.0, end_time=4.0) sequence.notes.add(pitch=62, start_time=5.0, end_time=6.0) sequence.total_time = 8.0 def time_func(time): if time % 2 == 0: return time - 1 else: return time adjusted_ns, skipped_notes = sequences_lib.adjust_notesequence_times( sequence, time_func) expected_sequence = music_pb2.NoteSequence() self.assertEqual(expected_sequence, adjusted_ns) self.assertEqual(3, skipped_notes) adjusted_ns, skipped_notes = sequences_lib.adjust_notesequence_times( sequence, time_func, minimum_duration=.1) expected_sequence = music_pb2.NoteSequence() expected_sequence.notes.add(pitch=60, start_time=1.0, end_time=1.1) expected_sequence.notes.add(pitch=61, start_time=3.0, end_time=3.1) expected_sequence.notes.add(pitch=62, start_time=5.0, end_time=5.1) expected_sequence.total_time = 5.1 self.assertEqual(expected_sequence, adjusted_ns) self.assertEqual(0, skipped_notes)
def testApplySustainControlChanges(self)
-
Verify sustain controls extend notes until the end of the control.
Expand source code
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 testApplySustainControlChangesExtendNotesToEnd(self)
-
Test sustain control extending the duration of the final note.
Expand source code
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 testApplySustainControlChangesExtraneousSustain(self)
-
Test applying extraneous sustain control at the end of the sequence.
Expand source code
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 testApplySustainControlChangesProcessSustainBeforeNotes(self)
-
Verify sustain controls extend notes until the end of the control.
Expand source code
def testApplySustainControlChangesProcessSustainBeforeNotes(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)]) expected_sequence = copy.copy(sequence) testing_lib.add_track_to_sequence( sequence, 0, [(11, 55, 0.22, 0.75), (40, 45, 2.50, 3.50)]) testing_lib.add_track_to_sequence( expected_sequence, 0, [(11, 55, 0.22, 0.75), (40, 45, 2.50, 3.50)]) sus_sequence = sequences_lib.apply_sustain_control_changes(sequence) self.assertProtoEquals(expected_sequence, sus_sequence)
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.
Expand source code
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 testApplySustainControlChangesWithDrumNotes(self)
-
Drum notes should not be modified when applying sustain changes.
Expand source code
def testApplySustainControlChangesWithDrumNotes(self): """Drum notes should not be modified when applying sustain changes.""" 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)]) testing_lib.add_track_to_sequence( sequence, 0, [(38, 100, 2.00, 2.50)], is_drum=True) testing_lib.add_track_to_sequence( expected_sequence, 0, [(60, 100, 2.00, 4.00)]) testing_lib.add_track_to_sequence( expected_sequence, 0, [(38, 100, 2.0, 2.5)], is_drum=True) 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.
Expand source code
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 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.
Expand source code
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.
Expand source code
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 testAssertIsQuantizedNoteSequence(self)
-
Expand source code
def testAssertIsQuantizedNoteSequence(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)]) relative_quantized_sequence = sequences_lib.quantize_note_sequence( self.note_sequence, steps_per_quarter=self.steps_per_quarter) absolute_quantized_sequence = sequences_lib.quantize_note_sequence_absolute( self.note_sequence, steps_per_second=4) sequences_lib.assert_is_quantized_sequence(relative_quantized_sequence) sequences_lib.assert_is_quantized_sequence(absolute_quantized_sequence) with self.assertRaises(sequences_lib.QuantizationStatusError): # pylint:disable=g-error-prone-assert-raises sequences_lib.assert_is_quantized_sequence(self.note_sequence)
def testAssertIsRelativeQuantizedNoteSequence(self)
-
Expand source code
def testAssertIsRelativeQuantizedNoteSequence(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)]) relative_quantized_sequence = sequences_lib.quantize_note_sequence( self.note_sequence, steps_per_quarter=self.steps_per_quarter) absolute_quantized_sequence = sequences_lib.quantize_note_sequence_absolute( self.note_sequence, steps_per_second=4) sequences_lib.assert_is_relative_quantized_sequence( relative_quantized_sequence) with self.assertRaises(sequences_lib.QuantizationStatusError): # pylint:disable=g-error-prone-assert-raises sequences_lib.assert_is_relative_quantized_sequence( absolute_quantized_sequence) with self.assertRaises(sequences_lib.QuantizationStatusError): # pylint:disable=g-error-prone-assert-raises sequences_lib.assert_is_relative_quantized_sequence(self.note_sequence)
def testAugmentNoteSequenceDeleteFalse(self)
-
Expand source code
def testAugmentNoteSequenceDeleteFalse(self): sequence = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( sequence, 0, [(12, 100, 0.01, 10.0), (13, 55, 0.22, 0.50), (40, 45, 2.50, 3.50), (55, 120, 4.0, 4.01), (52, 99, 4.75, 5.0)]) augmented_sequence = sequences_lib.augment_note_sequence( sequence, min_stretch_factor=2, max_stretch_factor=2, min_transpose=-15, max_transpose=-10, min_allowed_pitch=10, max_allowed_pitch=127, delete_out_of_range_notes=False) expected_sequence = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( expected_sequence, 0, [(10, 100, 0.02, 20.0), (11, 55, 0.44, 1.0), (38, 45, 5., 7.), (53, 120, 8.0, 8.02), (50, 99, 9.5, 10.0)]) expected_sequence.tempos[0].qpm = 30. self.assertProtoEquals(augmented_sequence, expected_sequence)
def testAugmentNoteSequenceDeleteTrue(self)
-
Expand source code
def testAugmentNoteSequenceDeleteTrue(self): sequence = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( sequence, 0, [(12, 100, 0.01, 10.0), (13, 55, 0.22, 0.50), (40, 45, 2.50, 3.50), (55, 120, 4.0, 4.01), (52, 99, 4.75, 5.0)]) augmented_sequence = sequences_lib.augment_note_sequence( sequence, min_stretch_factor=2, max_stretch_factor=2, min_transpose=-15, max_transpose=-15, min_allowed_pitch=10, max_allowed_pitch=127, delete_out_of_range_notes=True) expected_sequence = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( expected_sequence, 0, [(25, 45, 5., 7.), (40, 120, 8.0, 8.02), (37, 99, 9.5, 10.0)]) expected_sequence.tempos[0].qpm = 30. self.assertProtoEquals(augmented_sequence, expected_sequence)
def testAugmentNoteSequenceNoStretch(self)
-
Expand source code
def testAugmentNoteSequenceNoStretch(self): sequence = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( sequence, 0, [(12, 100, 0.01, 10.0), (13, 55, 0.22, 0.50), (40, 45, 2.50, 3.50), (55, 120, 4.0, 4.01), (52, 99, 4.75, 5.0)]) augmented_sequence = sequences_lib.augment_note_sequence( sequence, min_stretch_factor=1, max_stretch_factor=1., min_transpose=-15, max_transpose=-15, min_allowed_pitch=10, max_allowed_pitch=127, delete_out_of_range_notes=True) expected_sequence = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( expected_sequence, 0, [(25, 45, 2.5, 3.50), (40, 120, 4.0, 4.01), (37, 99, 4.75, 5.0)]) self.assertProtoEquals(augmented_sequence, expected_sequence)
def testAugmentNoteSequenceNoTranspose(self)
-
Expand source code
def testAugmentNoteSequenceNoTranspose(self): sequence = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( sequence, 0, [(12, 100, 0.01, 10.0), (13, 55, 0.22, 0.50), (40, 45, 2.50, 3.50), (55, 120, 4.0, 4.01), (52, 99, 4.75, 5.0)]) augmented_sequence = sequences_lib.augment_note_sequence( sequence, min_stretch_factor=2, max_stretch_factor=2., min_transpose=0, max_transpose=0, min_allowed_pitch=10, max_allowed_pitch=127, delete_out_of_range_notes=True) expected_sequence = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( expected_sequence, 0, [(12, 100, 0.02, 20.0), (13, 55, 0.44, 1.0), (40, 45, 5., 7.), (55, 120, 8.0, 8.02), (52, 99, 9.5, 10.0)]) expected_sequence.tempos[0].qpm = 30. self.assertProtoEquals(augmented_sequence, expected_sequence)
def testClampTranspose(self)
-
Expand source code
def testClampTranspose(self): clamped = sequences_lib._clamp_transpose( # pylint:disable=protected-access 5, 20, 60, 10, 70) self.assertEqual(clamped, 5) clamped = sequences_lib._clamp_transpose( # pylint:disable=protected-access 15, 20, 60, 10, 65) self.assertEqual(clamped, 5) clamped = sequences_lib._clamp_transpose( # pylint:disable=protected-access -16, 20, 60, 10, 70) self.assertEqual(clamped, -10)
def testConcatenateSequences(self)
-
Expand source code
def testConcatenateSequences(self): sequence1 = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( sequence1, 0, [(60, 100, 0.0, 1.0), (72, 100, 0.5, 1.5)]) sequence2 = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( sequence2, 0, [(59, 100, 0.0, 1.0), (71, 100, 0.5, 1.5)]) expected_sequence = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( expected_sequence, 0, [(60, 100, 0.0, 1.0), (72, 100, 0.5, 1.5), (59, 100, 1.5, 2.5), (71, 100, 2.0, 3.0)]) cat_seq = sequences_lib.concatenate_sequences([sequence1, sequence2]) self.assertProtoEquals(expected_sequence, cat_seq)
def testConcatenateSequencesWithSpecifiedDurations(self)
-
Expand source code
def testConcatenateSequencesWithSpecifiedDurations(self): sequence1 = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( sequence1, 0, [(60, 100, 0.0, 1.0), (72, 100, 0.5, 1.5)]) sequence2 = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( sequence2, 0, [(59, 100, 0.0, 1.0)]) sequence3 = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( sequence3, 0, [(72, 100, 0.0, 1.0), (73, 100, 0.5, 1.5)]) expected_sequence = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( expected_sequence, 0, [(60, 100, 0.0, 1.0), (72, 100, 0.5, 1.5), (59, 100, 2.0, 3.0), (72, 100, 3.5, 4.5), (73, 100, 4.0, 5.0)]) cat_seq = sequences_lib.concatenate_sequences( [sequence1, sequence2, sequence3], sequence_durations=[2, 1.5, 2]) self.assertProtoEquals(expected_sequence, cat_seq)
def testExpandSectionGroups(self)
-
Expand source code
def testExpandSectionGroups(self): sequence = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( sequence, 0, [(60, 100, 0.0, 1.0), (72, 100, 1.0, 2.0), (59, 100, 2.0, 3.0), (71, 100, 3.0, 4.0)]) sequence.section_annotations.add(time=0, section_id=0) sequence.section_annotations.add(time=1, section_id=1) sequence.section_annotations.add(time=2, section_id=2) sequence.section_annotations.add(time=3, section_id=3) # A((BC)2D)2 sg = sequence.section_groups.add() sg.sections.add(section_id=0) sg.num_times = 1 sg = sequence.section_groups.add() sg.sections.add(section_group=music_pb2.NoteSequence.SectionGroup( sections=[music_pb2.NoteSequence.Section(section_id=1), music_pb2.NoteSequence.Section(section_id=2)], num_times=2)) sg.sections.add(section_id=3) sg.num_times = 2 expanded = sequences_lib.expand_section_groups(sequence) expected = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( expected, 0, [(60, 100, 0.0, 1.0), (72, 100, 1.0, 2.0), (59, 100, 2.0, 3.0), (72, 100, 3.0, 4.0), (59, 100, 4.0, 5.0), (71, 100, 5.0, 6.0), (72, 100, 6.0, 7.0), (59, 100, 7.0, 8.0), (72, 100, 8.0, 9.0), (59, 100, 9.0, 10.0), (71, 100, 10.0, 11.0)]) expected.section_annotations.add(time=0, section_id=0) expected.section_annotations.add(time=1, section_id=1) expected.section_annotations.add(time=2, section_id=2) expected.section_annotations.add(time=3, section_id=1) expected.section_annotations.add(time=4, section_id=2) expected.section_annotations.add(time=5, section_id=3) expected.section_annotations.add(time=6, section_id=1) expected.section_annotations.add(time=7, section_id=2) expected.section_annotations.add(time=8, section_id=1) expected.section_annotations.add(time=9, section_id=2) expected.section_annotations.add(time=10, section_id=3) self.assertProtoEquals(expected, expanded)
def testExpandWithoutSectionGroups(self)
-
Expand source code
def testExpandWithoutSectionGroups(self): sequence = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( sequence, 0, [(60, 100, 0.0, 1.0), (72, 100, 1.0, 2.0), (59, 100, 2.0, 3.0), (71, 100, 3.0, 4.0)]) sequence.section_annotations.add(time=0, section_id=0) sequence.section_annotations.add(time=1, section_id=1) sequence.section_annotations.add(time=2, section_id=2) sequence.section_annotations.add(time=3, section_id=3) expanded = sequences_lib.expand_section_groups(sequence) self.assertEqual(sequence, expanded)
def testExtractSubsequence(self)
-
Expand source code
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.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) subsequence.control_changes.sort( key=lambda cc: (cc.instrument, cc.time)) self.assertProtoEquals(expected_subsequence, subsequence)
def testExtractSubsequencePastEnd(self)
-
Expand source code
def testExtractSubsequencePastEnd(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', 18.0)]) with self.assertRaises(ValueError): sequences_lib.extract_subsequence(sequence, 15.0, 16.0)
def testExtractSubsequencePedalEvents(self)
-
Expand source code
def testExtractSubsequencePedalEvents(self): sequence = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( sequence, 0, [(60, 80, 2.5, 5.0)]) 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_control_changes_to_sequence( sequence, 0, [(0.0, 66, 0), (2.0, 66, 127), (4.0, 66, 0), (5.0, 66, 127)]) testing_lib.add_control_changes_to_sequence( sequence, 0, [(0.0, 67, 10), (2.0, 67, 20), (4.0, 67, 30), (5.0, 67, 40)]) expected_subsequence = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( expected_subsequence, 0, [(60, 80, 0, 2.25)]) 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)]) testing_lib.add_control_changes_to_sequence( expected_subsequence, 0, [(0.0, 66, 127), (1.5, 66, 0)]) testing_lib.add_control_changes_to_sequence( expected_subsequence, 0, [(0.0, 67, 20), (1.5, 67, 30)]) expected_subsequence.control_changes.sort( key=lambda cc: (cc.instrument, cc.control_number, cc.time)) expected_subsequence.total_time = 2.25 expected_subsequence.subsequence_info.start_time_offset = 2.5 expected_subsequence.subsequence_info.end_time_offset = .25 subsequence = sequences_lib.extract_subsequence(sequence, 2.5, 4.75) subsequence.control_changes.sort( key=lambda cc: (cc.instrument, cc.control_number, cc.time)) self.assertProtoEquals(expected_subsequence, subsequence)
def testFromNoteSequence_ImplicitTempoChange(self)
-
Expand source code
def testFromNoteSequence_ImplicitTempoChange(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)]) del self.note_sequence.tempos[:] # No tempo. sequences_lib.quantize_note_sequence( self.note_sequence, self.steps_per_quarter) # Implicit tempo change. self.note_sequence.tempos.add(qpm=60, time=2) with self.assertRaises(sequences_lib.MultipleTempoError): sequences_lib.quantize_note_sequence( self.note_sequence, self.steps_per_quarter)
def testFromNoteSequence_NoImplicitTempoChangeOutOfOrder(self)
-
Expand source code
def testFromNoteSequence_NoImplicitTempoChangeOutOfOrder(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)]) del self.note_sequence.tempos[:] # No tempo. sequences_lib.quantize_note_sequence( self.note_sequence, self.steps_per_quarter) # No implicit tempo change, but tempos are added out of order. self.note_sequence.tempos.add(qpm=60, time=2) self.note_sequence.tempos.add(qpm=60, time=0) sequences_lib.quantize_note_sequence( self.note_sequence, self.steps_per_quarter)
def testFromNoteSequence_TempoChange(self)
-
Expand source code
def testFromNoteSequence_TempoChange(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)]) del self.note_sequence.tempos[:] # No tempos. sequences_lib.quantize_note_sequence( self.note_sequence, self.steps_per_quarter) # Single tempo. self.note_sequence.tempos.add(qpm=60, time=0) sequences_lib.quantize_note_sequence( self.note_sequence, self.steps_per_quarter) # Multiple tempos with no change. self.note_sequence.tempos.add(qpm=60, time=1) sequences_lib.quantize_note_sequence( self.note_sequence, self.steps_per_quarter) # Tempo change. self.note_sequence.tempos.add(qpm=120, time=2) with self.assertRaises(sequences_lib.MultipleTempoError): sequences_lib.quantize_note_sequence( self.note_sequence, self.steps_per_quarter)
def testInferDenseChordsForSequence(self)
-
Expand source code
def testInferDenseChordsForSequence(self): # Test non-quantized sequence. sequence = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( sequence, 0, [(60, 100, 1.0, 3.0), (64, 100, 1.0, 2.0), (67, 100, 1.0, 2.0), (65, 100, 2.0, 3.0), (69, 100, 2.0, 3.0), (62, 100, 3.0, 5.0), (65, 100, 3.0, 4.0), (69, 100, 3.0, 4.0)]) expected_sequence = copy.copy(sequence) testing_lib.add_chords_to_sequence( expected_sequence, [('C', 1.0), ('F/C', 2.0), ('Dm', 3.0)]) sequences_lib.infer_dense_chords_for_sequence(sequence) self.assertProtoEquals(expected_sequence, sequence) # Test quantized sequence. sequence = copy.copy(self.note_sequence) sequence.quantization_info.steps_per_quarter = 1 testing_lib.add_track_to_sequence( sequence, 0, [(60, 100, 1.1, 3.0), (64, 100, 1.0, 1.9), (67, 100, 1.0, 2.0), (65, 100, 2.0, 3.2), (69, 100, 2.1, 3.1), (62, 100, 2.9, 4.8), (65, 100, 3.0, 4.0), (69, 100, 3.0, 4.1)]) testing_lib.add_quantized_steps_to_sequence( sequence, [(1, 3), (1, 2), (1, 2), (2, 3), (2, 3), (3, 5), (3, 4), (3, 4)]) expected_sequence = copy.copy(sequence) testing_lib.add_chords_to_sequence( expected_sequence, [('C', 1.0), ('F/C', 2.0), ('Dm', 3.0)]) testing_lib.add_quantized_chord_steps_to_sequence( expected_sequence, [1, 2, 3]) sequences_lib.infer_dense_chords_for_sequence(sequence) self.assertProtoEquals(expected_sequence, sequence)
def testMultiTrack(self)
-
Expand source code
def testMultiTrack(self): testing_lib.add_track_to_sequence( self.note_sequence, 0, [(12, 100, 1.0, 4.0), (19, 100, 0.95, 3.0)]) testing_lib.add_track_to_sequence( self.note_sequence, 3, [(12, 100, 1.0, 4.0), (19, 100, 2.0, 5.0)]) testing_lib.add_track_to_sequence( self.note_sequence, 7, [(12, 100, 1.0, 5.0), (19, 100, 2.0, 4.0), (24, 100, 3.0, 3.5)]) expected_quantized_sequence = copy.deepcopy(self.note_sequence) expected_quantized_sequence.quantization_info.steps_per_quarter = ( self.steps_per_quarter) testing_lib.add_quantized_steps_to_sequence( expected_quantized_sequence, [(4, 16), (4, 12), (4, 16), (8, 20), (4, 20), (8, 16), (12, 14)]) quantized_sequence = sequences_lib.quantize_note_sequence( self.note_sequence, self.steps_per_quarter) self.assertProtoEquals(expected_quantized_sequence, quantized_sequence)
def testPianorollOnsetsToNoteSequence(self)
-
Expand source code
def testPianorollOnsetsToNoteSequence(self): onsets = np.zeros((10, 2), np.bool) velocity_values = np.zeros_like(onsets, np.float32) onsets[0:2, 0] = True velocity_values[0:2, 0] = .5 onsets[1:2, 1] = True velocity_values[1:2, 1] = 1 sequence = sequences_lib.pianoroll_onsets_to_note_sequence( onsets, frames_per_second=10, note_duration_seconds=0.05, min_midi_pitch=60, velocity_values=velocity_values) self.assertLen(sequence.notes, 3) self.assertEqual(60, sequence.notes[0].pitch) self.assertEqual(0, sequence.notes[0].start_time) self.assertAlmostEqual(0.05, sequence.notes[0].end_time) self.assertEqual(50, sequence.notes[0].velocity) self.assertEqual(60, sequence.notes[1].pitch) self.assertEqual(0.1, sequence.notes[1].start_time) self.assertAlmostEqual(0.15, sequence.notes[1].end_time) self.assertEqual(50, sequence.notes[1].velocity) self.assertEqual(61, sequence.notes[2].pitch) self.assertEqual(0.1, sequence.notes[2].start_time) self.assertAlmostEqual(0.15, sequence.notes[2].end_time) self.assertEqual(90, sequence.notes[2].velocity)
def testPianorollOnsetsToNoteSequenceFullVelocityScale(self)
-
Expand source code
def testPianorollOnsetsToNoteSequenceFullVelocityScale(self): onsets = np.zeros((10, 2), np.bool) velocity_values = np.zeros_like(onsets, np.float32) onsets[0:2, 0] = True velocity_values[0:2, 0] = .5 onsets[1:2, 1] = True velocity_values[1:2, 1] = 1 sequence = sequences_lib.pianoroll_onsets_to_note_sequence( onsets, frames_per_second=10, note_duration_seconds=0.05, min_midi_pitch=60, velocity_values=velocity_values, velocity_scale=127, velocity_bias=0) self.assertLen(sequence.notes, 3) self.assertEqual(60, sequence.notes[0].pitch) self.assertEqual(0, sequence.notes[0].start_time) self.assertAlmostEqual(0.05, sequence.notes[0].end_time) self.assertEqual(63, sequence.notes[0].velocity) self.assertEqual(60, sequence.notes[1].pitch) self.assertEqual(0.1, sequence.notes[1].start_time) self.assertAlmostEqual(0.15, sequence.notes[1].end_time) self.assertEqual(63, sequence.notes[1].velocity) self.assertEqual(61, sequence.notes[2].pitch) self.assertEqual(0.1, sequence.notes[2].start_time) self.assertAlmostEqual(0.15, sequence.notes[2].end_time) self.assertEqual(127, sequence.notes[2].velocity)
def testPianorollToNoteSequence(self)
-
Expand source code
def testPianorollToNoteSequence(self): # 100 frames of notes. frames = np.zeros((100, MIDI_PITCHES), np.bool) # Activate key 39 for the middle 50 frames. frames[25:75, 39] = True sequence = sequences_lib.pianoroll_to_note_sequence( frames, frames_per_second=DEFAULT_FRAMES_PER_SECOND, min_duration_ms=0) self.assertLen(sequence.notes, 1) self.assertEqual(39, sequence.notes[0].pitch) self.assertEqual(25 / DEFAULT_FRAMES_PER_SECOND, sequence.notes[0].start_time) self.assertEqual(75 / DEFAULT_FRAMES_PER_SECOND, sequence.notes[0].end_time)
def testPianorollToNoteSequenceAllNotes(self)
-
Expand source code
def testPianorollToNoteSequenceAllNotes(self): # Test all 128 notes frames = np.eye(MIDI_PITCHES, dtype=np.bool) # diagonal identity matrix sequence = sequences_lib.pianoroll_to_note_sequence( frames, frames_per_second=DEFAULT_FRAMES_PER_SECOND, min_duration_ms=0) self.assertLen(sequence.notes, MIDI_PITCHES) for i in range(MIDI_PITCHES): self.assertEqual(i, sequence.notes[i].pitch) self.assertAlmostEqual(i / DEFAULT_FRAMES_PER_SECOND, sequence.notes[i].start_time) self.assertAlmostEqual((i+1) / DEFAULT_FRAMES_PER_SECOND, sequence.notes[i].end_time)
def testPianorollToNoteSequenceWithOnsets(self)
-
Expand source code
def testPianorollToNoteSequenceWithOnsets(self): # 100 frames of notes and onsets. frames = np.zeros((100, MIDI_PITCHES), np.bool) onsets = np.zeros((100, MIDI_PITCHES), np.bool) # Activate key 39 for the middle 50 frames and last 10 frames. frames[25:75, 39] = True frames[90:100, 39] = True # Add an onset for the first occurrence. onsets[25, 39] = True # Add an onset for a note that doesn't have an active frame. onsets[80, 49] = True sequence = sequences_lib.pianoroll_to_note_sequence( frames, frames_per_second=DEFAULT_FRAMES_PER_SECOND, min_duration_ms=0, onset_predictions=onsets) self.assertLen(sequence.notes, 2) self.assertEqual(39, sequence.notes[0].pitch) self.assertEqual(25 / DEFAULT_FRAMES_PER_SECOND, sequence.notes[0].start_time) self.assertEqual(75 / DEFAULT_FRAMES_PER_SECOND, sequence.notes[0].end_time) self.assertEqual(49, sequence.notes[1].pitch) self.assertEqual(80 / DEFAULT_FRAMES_PER_SECOND, sequence.notes[1].start_time) self.assertEqual(81 / DEFAULT_FRAMES_PER_SECOND, sequence.notes[1].end_time)
def testPianorollToNoteSequenceWithOnsetsAndFullScaleVelocity(self)
-
Expand source code
def testPianorollToNoteSequenceWithOnsetsAndFullScaleVelocity(self): # 100 frames of notes and onsets. frames = np.zeros((100, MIDI_PITCHES), np.bool) onsets = np.zeros((100, MIDI_PITCHES), np.bool) velocity_values = np.zeros((100, MIDI_PITCHES), np.float32) # Activate key 39 for the middle 50 frames and last 10 frames. frames[25:75, 39] = True frames[90:100, 39] = True onsets[25, 39] = True velocity_values[25, 39] = 0.5 onsets[90, 39] = True velocity_values[90, 39] = 1.0 sequence = sequences_lib.pianoroll_to_note_sequence( frames, frames_per_second=DEFAULT_FRAMES_PER_SECOND, min_duration_ms=0, onset_predictions=onsets, velocity_values=velocity_values, velocity_scale=127, velocity_bias=0) self.assertLen(sequence.notes, 2) self.assertEqual(39, sequence.notes[0].pitch) self.assertEqual(63, sequence.notes[0].velocity) self.assertEqual(39, sequence.notes[1].pitch) self.assertEqual(127, sequence.notes[1].velocity)
def testPianorollToNoteSequenceWithOnsetsAndVelocity(self)
-
Expand source code
def testPianorollToNoteSequenceWithOnsetsAndVelocity(self): # 100 frames of notes and onsets. frames = np.zeros((100, MIDI_PITCHES), np.bool) onsets = np.zeros((100, MIDI_PITCHES), np.bool) velocity_values = np.zeros((100, MIDI_PITCHES), np.float32) # Activate key 39 for the middle 50 frames and last 10 frames. frames[25:75, 39] = True frames[90:100, 39] = True # Add an onset for the first occurrence with a valid velocity. onsets[25, 39] = True velocity_values[25, 39] = 0.5 # Add an onset for the second occurrence with a NaN velocity. onsets[90, 39] = True velocity_values[90, 39] = float('nan') sequence = sequences_lib.pianoroll_to_note_sequence( frames, frames_per_second=DEFAULT_FRAMES_PER_SECOND, min_duration_ms=0, onset_predictions=onsets, velocity_values=velocity_values) self.assertLen(sequence.notes, 2) self.assertEqual(39, sequence.notes[0].pitch) self.assertEqual(50, sequence.notes[0].velocity) self.assertEqual(39, sequence.notes[1].pitch) self.assertEqual(0, sequence.notes[1].velocity)
def testPianorollToNoteSequenceWithOnsetsDefaultVelocity(self)
-
Expand source code
def testPianorollToNoteSequenceWithOnsetsDefaultVelocity(self): # 100 frames of notes and onsets. frames = np.zeros((100, MIDI_PITCHES), np.bool) onsets = np.zeros((100, MIDI_PITCHES), np.bool) # Activate key 39 for the middle 50 frames and last 10 frames. frames[25:75, 39] = True frames[90:100, 39] = True onsets[25, 39] = True onsets[90, 39] = True sequence = sequences_lib.pianoroll_to_note_sequence( frames, frames_per_second=DEFAULT_FRAMES_PER_SECOND, min_duration_ms=0, onset_predictions=onsets, velocity=100) self.assertLen(sequence.notes, 2) self.assertEqual(39, sequence.notes[0].pitch) self.assertEqual(100, sequence.notes[0].velocity) self.assertEqual(39, sequence.notes[1].pitch) self.assertEqual(100, sequence.notes[1].velocity)
def testPianorollToNoteSequenceWithOnsetsOverlappingFrames(self)
-
Expand source code
def testPianorollToNoteSequenceWithOnsetsOverlappingFrames(self): # 100 frames of notes and onsets. frames = np.zeros((100, MIDI_PITCHES), np.bool) onsets = np.zeros((100, MIDI_PITCHES), np.bool) # Activate key 39 for the middle 50 frames. frames[25:75, 39] = True # Add multiple onsets within those frames. onsets[25, 39] = True onsets[30, 39] = True # If an onset lasts for multiple frames, it should create only 1 note. onsets[35, 39] = True onsets[36, 39] = True sequence = sequences_lib.pianoroll_to_note_sequence( frames, frames_per_second=DEFAULT_FRAMES_PER_SECOND, min_duration_ms=0, onset_predictions=onsets) self.assertLen(sequence.notes, 3) self.assertEqual(39, sequence.notes[0].pitch) self.assertEqual(25 / DEFAULT_FRAMES_PER_SECOND, sequence.notes[0].start_time) self.assertEqual(30 / DEFAULT_FRAMES_PER_SECOND, sequence.notes[0].end_time) self.assertEqual(39, sequence.notes[1].pitch) self.assertEqual(30 / DEFAULT_FRAMES_PER_SECOND, sequence.notes[1].start_time) self.assertEqual(35 / DEFAULT_FRAMES_PER_SECOND, sequence.notes[1].end_time) self.assertEqual(39, sequence.notes[2].pitch) self.assertEqual(35 / DEFAULT_FRAMES_PER_SECOND, sequence.notes[2].start_time) self.assertEqual(75 / DEFAULT_FRAMES_PER_SECOND, sequence.notes[2].end_time)
def testQuantizeNoteSequence(self)
-
Expand source code
def testQuantizeNoteSequence(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_quarter = ( self.steps_per_quarter) 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( self.note_sequence, steps_per_quarter=self.steps_per_quarter) self.assertProtoEquals(expected_quantized_sequence, quantized_sequence)
def testQuantizeNoteSequenceAbsolute(self)
-
Expand source code
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 testQuantizeNoteSequence_ImplicitTimeSignatureChange(self)
-
Expand source code
def testQuantizeNoteSequence_ImplicitTimeSignatureChange(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)]) del self.note_sequence.time_signatures[:] # No time signature. sequences_lib.quantize_note_sequence( self.note_sequence, self.steps_per_quarter) # Implicit time signature change. self.note_sequence.time_signatures.add(numerator=2, denominator=4, time=2) with self.assertRaises(sequences_lib.MultipleTimeSignatureError): sequences_lib.quantize_note_sequence( self.note_sequence, self.steps_per_quarter)
def testQuantizeNoteSequence_NoImplicitTimeSignatureChangeOutOfOrder(self)
-
Expand source code
def testQuantizeNoteSequence_NoImplicitTimeSignatureChangeOutOfOrder(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)]) del self.note_sequence.time_signatures[:] # No time signature. sequences_lib.quantize_note_sequence( self.note_sequence, self.steps_per_quarter) # No implicit time signature change, but time signatures are added out of # order. self.note_sequence.time_signatures.add(numerator=2, denominator=4, time=2) self.note_sequence.time_signatures.add(numerator=2, denominator=4, time=0) sequences_lib.quantize_note_sequence( self.note_sequence, self.steps_per_quarter)
def testQuantizeNoteSequence_TimeSignatureChange(self)
-
Expand source code
def testQuantizeNoteSequence_TimeSignatureChange(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)]) del self.note_sequence.time_signatures[:] sequences_lib.quantize_note_sequence( self.note_sequence, self.steps_per_quarter) # Single time signature. self.note_sequence.time_signatures.add(numerator=4, denominator=4, time=0) sequences_lib.quantize_note_sequence( self.note_sequence, self.steps_per_quarter) # Multiple time signatures with no change. self.note_sequence.time_signatures.add(numerator=4, denominator=4, time=1) sequences_lib.quantize_note_sequence( self.note_sequence, self.steps_per_quarter) # Time signature change. self.note_sequence.time_signatures.add(numerator=2, denominator=4, time=2) with self.assertRaises(sequences_lib.MultipleTimeSignatureError): sequences_lib.quantize_note_sequence( self.note_sequence, self.steps_per_quarter)
def testQuantizeToStep(self)
-
Expand source code
def testQuantizeToStep(self): self.assertEqual( 32, sequences_lib.quantize_to_step(8.0001, 4)) self.assertEqual( 34, sequences_lib.quantize_to_step(8.4999, 4)) self.assertEqual( 33, sequences_lib.quantize_to_step(8.4999, 4, quantize_cutoff=1.0))
def testRectifyBeats(self)
-
Expand source code
def testRectifyBeats(self): sequence = music_pb2.NoteSequence() testing_lib.add_track_to_sequence( sequence, 0, [(60, 100, 0.25, 0.5), (62, 100, 0.5, 0.75), (64, 100, 0.75, 2.5), (65, 100, 1.0, 1.5), (67, 100, 1.5, 2.0)]) testing_lib.add_beats_to_sequence(sequence, [0.5, 1.0, 2.0]) rectified_sequence, alignment = sequences_lib.rectify_beats( sequence, 120) expected_sequence = music_pb2.NoteSequence() expected_sequence.tempos.add(qpm=120) testing_lib.add_track_to_sequence( expected_sequence, 0, [(60, 100, 0.25, 0.5), (62, 100, 0.5, 0.75), (64, 100, 0.75, 2.0), (65, 100, 1.0, 1.25), (67, 100, 1.25, 1.5)]) testing_lib.add_beats_to_sequence(expected_sequence, [0.5, 1.0, 1.5]) self.assertEqual(expected_sequence, rectified_sequence) expected_alignment = [ [0.0, 0.5, 1.0, 2.0, 2.5], [0.0, 0.5, 1.0, 1.5, 2.0] ] self.assertEqual(expected_alignment, alignment.T.tolist())
def testRemoveRedundantData(self)
-
Expand source code
def testRemoveRedundantData(self): sequence = copy.copy(self.note_sequence) redundant_tempo = sequence.tempos.add() redundant_tempo.CopyFrom(sequence.tempos[0]) redundant_tempo.time = 5.0 sequence.sequence_metadata.composers.append('Foo') sequence.sequence_metadata.composers.append('Bar') sequence.sequence_metadata.composers.append('Foo') sequence.sequence_metadata.composers.append('Bar') sequence.sequence_metadata.genre.append('Classical') sequence.sequence_metadata.genre.append('Classical') fixed_sequence = sequences_lib.remove_redundant_data(sequence) expected_sequence = copy.copy(self.note_sequence) expected_sequence.sequence_metadata.composers.append('Foo') expected_sequence.sequence_metadata.composers.append('Bar') expected_sequence.sequence_metadata.genre.append('Classical') self.assertProtoEquals(expected_sequence, fixed_sequence)
def testRemoveRedundantDataOutOfOrder(self)
-
Expand source code
def testRemoveRedundantDataOutOfOrder(self): sequence = copy.copy(self.note_sequence) meaningful_tempo = sequence.tempos.add() meaningful_tempo.time = 5.0 meaningful_tempo.qpm = 50 redundant_tempo = sequence.tempos.add() redundant_tempo.CopyFrom(sequence.tempos[0]) expected_sequence = copy.copy(self.note_sequence) expected_meaningful_tempo = expected_sequence.tempos.add() expected_meaningful_tempo.time = 5.0 expected_meaningful_tempo.qpm = 50 fixed_sequence = sequences_lib.remove_redundant_data(sequence) self.assertProtoEquals(expected_sequence, fixed_sequence)
def testRepeatSequenceToDuration(self)
-
Expand source code
def testRepeatSequenceToDuration(self): sequence = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( sequence, 0, [(60, 100, 0.0, 1.0), (72, 100, 0.5, 1.5)]) expected_sequence = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( expected_sequence, 0, [(60, 100, 0.0, 1.0), (72, 100, 0.5, 1.5), (60, 100, 1.5, 2.5), (72, 100, 2.0, 3.0)]) repeated_seq = sequences_lib.repeat_sequence_to_duration( sequence, duration=3) self.assertProtoEquals(expected_sequence, repeated_seq)
def testRepeatSequenceToDurationProvidedDuration(self)
-
Expand source code
def testRepeatSequenceToDurationProvidedDuration(self): sequence = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( sequence, 0, [(60, 100, 0.0, 1.0), (72, 100, 0.5, 1.5)]) expected_sequence = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( expected_sequence, 0, [(60, 100, 0.0, 1.0), (72, 100, 0.5, 1.5), (60, 100, 2.0, 3.0), (72, 100, 2.5, 3.0)]) repeated_seq = sequences_lib.repeat_sequence_to_duration( sequence, duration=3, sequence_duration=2) self.assertProtoEquals(expected_sequence, repeated_seq)
def testRounding(self)
-
Expand source code
def testRounding(self): testing_lib.add_track_to_sequence( self.note_sequence, 1, [(12, 100, 0.01, 0.24), (11, 100, 0.22, 0.55), (40, 100, 0.50, 0.75), (41, 100, 0.689, 1.18), (44, 100, 1.19, 1.69), (55, 100, 4.0, 4.01)]) expected_quantized_sequence = copy.deepcopy(self.note_sequence) expected_quantized_sequence.quantization_info.steps_per_quarter = ( self.steps_per_quarter) testing_lib.add_quantized_steps_to_sequence( expected_quantized_sequence, [(0, 1), (1, 2), (2, 3), (3, 5), (5, 7), (16, 17)]) quantized_sequence = sequences_lib.quantize_note_sequence( self.note_sequence, self.steps_per_quarter) self.assertProtoEquals(expected_quantized_sequence, quantized_sequence)
def testSequenceToPianoroll(self)
-
Expand source code
def testSequenceToPianoroll(self): sequence = music_pb2.NoteSequence(total_time=1.21) testing_lib.add_track_to_sequence(sequence, 0, [(1, 100, 0.11, 1.01), (2, 55, 0.22, 0.50), (3, 100, 0.3, 0.8), (2, 45, 1.0, 1.21)]) pianoroll_tuple = sequences_lib.sequence_to_pianoroll( sequence, frames_per_second=10, min_pitch=1, max_pitch=2) output = pianoroll_tuple.active offset = pianoroll_tuple.offsets expected_pianoroll = [[0, 0], [1, 0], [1, 1], [1, 1], [1, 1], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 1], [0, 1], [0, 1]] expected_offsets = [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 1], [0, 0], [0, 0], [0, 0], [0, 0], [1, 0], [0, 0], [0, 1]] np.testing.assert_allclose(expected_pianoroll, output) np.testing.assert_allclose(expected_offsets, offset)
def testSequenceToPianorollActiveVelocities(self)
-
Expand source code
def testSequenceToPianorollActiveVelocities(self): sequence = music_pb2.NoteSequence() sequence.notes.add(pitch=60, start_time=0.0, end_time=2.0, velocity=16) sequence.notes.add(pitch=61, start_time=0.0, end_time=2.0, velocity=32) sequence.notes.add(pitch=62, start_time=0.0, end_time=2.0, velocity=64) sequence.total_time = 2.0 roll = sequences_lib.sequence_to_pianoroll( sequence, 1, 60, 62, max_velocity=64) active_velocities = roll.active_velocities self.assertEqual(np.all(active_velocities[0:2, 0] == 0.25), True) self.assertEqual(np.all(active_velocities[0:2, 1] == 0.5), True) self.assertEqual(np.all(active_velocities[0:2, 2] == 1.), True) self.assertEqual(np.all(active_velocities[2:] == 0), True)
def testSequenceToPianorollControlChanges(self)
-
Expand source code
def testSequenceToPianorollControlChanges(self): sequence = music_pb2.NoteSequence(total_time=2.0) cc = music_pb2.NoteSequence.ControlChange sequence.control_changes.extend([ cc(time=0.7, control_number=3, control_value=16), cc(time=0.0, control_number=4, control_value=32), cc(time=0.5, control_number=4, control_value=32), cc(time=1.6, control_number=3, control_value=64), ]) expected_cc_roll = np.zeros((5, 128), dtype=np.int32) expected_cc_roll[0:2, 4] = 33 expected_cc_roll[1, 3] = 17 expected_cc_roll[3, 3] = 65 cc_roll = sequences_lib.sequence_to_pianoroll( sequence, frames_per_second=2, min_pitch=1, max_pitch=4).control_changes np.testing.assert_allclose(expected_cc_roll, cc_roll)
def testSequenceToPianorollFrameOccupancy(self)
-
Expand source code
def testSequenceToPianorollFrameOccupancy(self): sequence = music_pb2.NoteSequence() sequence.notes.add(pitch=60, start_time=1.0, end_time=1.7) sequence.notes.add(pitch=61, start_time=6.2, end_time=6.55) sequence.notes.add(pitch=62, start_time=3.4, end_time=4.3) sequence.total_time = 6.55 active = sequences_lib.sequence_to_pianoroll( sequence, 2, 60, 62, min_frame_occupancy_for_label=0.5).active expected_roll = np.zeros([14, 3]) expected_roll[2:3, 0] = 1. expected_roll[12:13, 1] = 1. expected_roll[7:9, 2] = 1. np.testing.assert_equal(expected_roll, active)
def testSequenceToPianorollOnsetVelocities(self)
-
Expand source code
def testSequenceToPianorollOnsetVelocities(self): sequence = music_pb2.NoteSequence() sequence.notes.add(pitch=60, start_time=0.0, end_time=2.0, velocity=16) sequence.notes.add(pitch=61, start_time=0.0, end_time=2.0, velocity=32) sequence.notes.add(pitch=62, start_time=0.0, end_time=2.0, velocity=64) sequence.total_time = 2.0 roll = sequences_lib.sequence_to_pianoroll( sequence, 1, 60, 62, max_velocity=64, onset_window=0) onset_velocities = roll.onset_velocities self.assertEqual(onset_velocities[0, 0], 0.25) self.assertEqual(onset_velocities[0, 1], 0.5) self.assertEqual(onset_velocities[0, 2], 1.) self.assertEqual(np.all(onset_velocities[1:] == 0), True)
def testSequenceToPianorollOnsets(self)
-
Expand source code
def testSequenceToPianorollOnsets(self): sequence = music_pb2.NoteSequence() sequence.notes.add(pitch=60, start_time=2.0, end_time=5.0) sequence.notes.add(pitch=61, start_time=6.0, end_time=7.0) sequence.notes.add(pitch=62, start_time=7.0, end_time=8.0) sequence.total_time = 8.0 onsets = sequences_lib.sequence_to_pianoroll( sequence, 100, 60, 62, onset_mode='length_ms', onset_length_ms=100.0, onset_delay_ms=10.0, min_frame_occupancy_for_label=.999).onsets expected_roll = np.zeros([801, 3]) expected_roll[201:211, 0] = 1. expected_roll[601:611, 1] = 1. expected_roll[701:711, 2] = 1. np.testing.assert_equal(expected_roll, onsets)
def testSequenceToPianorollOverlappingNotes(self)
-
Expand source code
def testSequenceToPianorollOverlappingNotes(self): sequence = music_pb2.NoteSequence() sequence.notes.add(pitch=60, start_time=1.0, end_time=2.0) sequence.notes.add(pitch=60, start_time=1.2, end_time=2.0) sequence.notes.add(pitch=60, start_time=1.0, end_time=2.5) sequence.total_time = 2.5 rolls = sequences_lib.sequence_to_pianoroll( sequence, frames_per_second=10, min_pitch=60, max_pitch=60, onset_mode='length_ms', onset_length_ms=10) expected_onsets = np.zeros([26, 1]) expected_onsets[10, 0] = 1 expected_onsets[12, 0] = 1 np.testing.assert_equal(expected_onsets, rolls.onsets) expected_offsets = np.zeros([26, 1]) expected_offsets[20, 0] = 1 expected_offsets[25, 0] = 1 np.testing.assert_equal(expected_offsets, rolls.offsets) expected_active = np.zeros([26, 1]) expected_active[10:25, 0] = 1 np.testing.assert_equal(expected_active, rolls.active)
def testSequenceToPianorollShortNotes(self)
-
Expand source code
def testSequenceToPianorollShortNotes(self): sequence = music_pb2.NoteSequence() sequence.notes.add(pitch=60, start_time=1.0, end_time=1.0001) sequence.notes.add(pitch=60, start_time=1.2, end_time=1.2001) sequence.total_time = 2.5 rolls = sequences_lib.sequence_to_pianoroll( sequence, frames_per_second=10, min_pitch=60, max_pitch=60, onset_mode='length_ms', onset_length_ms=0) expected_onsets = np.zeros([26, 1]) expected_onsets[10, 0] = 1 expected_onsets[12, 0] = 1 np.testing.assert_equal(expected_onsets, rolls.onsets) expected_offsets = np.zeros([26, 1]) expected_offsets[10, 0] = 1 expected_offsets[12, 0] = 1 np.testing.assert_equal(expected_offsets, rolls.offsets) expected_active = np.zeros([26, 1]) expected_active[10:11, 0] = 1 expected_active[12:13, 0] = 1 np.testing.assert_equal(expected_active, rolls.active)
def testSequenceToPianorollWeightedRoll(self)
-
Expand source code
def testSequenceToPianorollWeightedRoll(self): sequence = music_pb2.NoteSequence(total_time=2.0) testing_lib.add_track_to_sequence(sequence, 0, [(1, 100, 0.00, 1.00), (2, 100, 0.20, 0.50), (3, 100, 1.20, 1.50), (4, 100, 0.40, 2.00), (6, 100, 0.10, 0.60)]) onset_upweight = 5.0 expected_roll_weights = [ [onset_upweight, onset_upweight, 1, onset_upweight], [onset_upweight, onset_upweight, onset_upweight, onset_upweight], [1, 1, onset_upweight, onset_upweight / 1], [1, 1, onset_upweight, onset_upweight / 2], [1, 1, 1, 1], ] expected_onsets = [ [1, 1, 0, 1], [1, 1, 1, 1], [0, 0, 1, 0], [0, 0, 1, 0], [0, 0, 0, 0], ] roll = sequences_lib.sequence_to_pianoroll( sequence, frames_per_second=2, min_pitch=1, max_pitch=4, onset_upweight=onset_upweight) np.testing.assert_allclose(expected_roll_weights, roll.weights) np.testing.assert_allclose(expected_onsets, roll.onsets)
def testSequenceToPianorollWithBlankFrameBeforeOffset(self)
-
Expand source code
def testSequenceToPianorollWithBlankFrameBeforeOffset(self): sequence = music_pb2.NoteSequence(total_time=1.5) testing_lib.add_track_to_sequence(sequence, 0, [(1, 100, 0.00, 1.00), (2, 100, 0.20, 0.50), (1, 100, 1.20, 1.50), (2, 100, 0.50, 1.50)]) expected_pianoroll = [ [1, 0], [1, 0], [1, 1], [1, 1], [1, 1], [1, 1], [1, 1], [1, 1], [1, 1], [1, 1], [0, 1], [0, 1], [1, 1], [1, 1], [1, 1], [0, 0], ] output = sequences_lib.sequence_to_pianoroll( sequence, frames_per_second=10, min_pitch=1, max_pitch=2).active np.testing.assert_allclose(expected_pianoroll, output) expected_pianoroll_with_blank_frame = [ [1, 0], [1, 0], [1, 1], [1, 1], [1, 0], [1, 1], [1, 1], [1, 1], [1, 1], [1, 1], [0, 1], [0, 1], [1, 1], [1, 1], [1, 1], [0, 0], ] output_with_blank_frame = sequences_lib.sequence_to_pianoroll( sequence, frames_per_second=10, min_pitch=1, max_pitch=2, add_blank_frame_before_onset=True).active np.testing.assert_allclose(expected_pianoroll_with_blank_frame, output_with_blank_frame)
def testSequenceToPianorollWithBlankFrameBeforeOffsetOutOfOrder(self)
-
Expand source code
def testSequenceToPianorollWithBlankFrameBeforeOffsetOutOfOrder(self): sequence = music_pb2.NoteSequence(total_time=.5) testing_lib.add_track_to_sequence(sequence, 0, [(1, 100, 0.20, 0.50), (1, 100, 0.00, 0.20)]) expected_pianoroll = [ [1], [0], [1], [1], [1], [0], ] output = sequences_lib.sequence_to_pianoroll( sequence, frames_per_second=10, min_pitch=1, max_pitch=1, add_blank_frame_before_onset=True).active np.testing.assert_allclose(expected_pianoroll, output)
def testSequenceToValuedIntervals(self)
-
Expand source code
def testSequenceToValuedIntervals(self): sequence = music_pb2.NoteSequence() sequence.notes.add(pitch=69, start_time=1.0, end_time=2.0, velocity=80) # Should be dropped because it is 0 duration. sequence.notes.add(pitch=60, start_time=3.0, end_time=3.0, velocity=90) intervals, pitches, velocities = sequences_lib.sequence_to_valued_intervals( sequence) np.testing.assert_array_equal([[1., 2.]], intervals) np.testing.assert_array_equal([440.0], pitches) np.testing.assert_array_equal([80], velocities)
def testShiftSequenceTimes(self)
-
Expand source code
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)
def testSplitNoteSequenceAtTimes(self)
-
Expand source code
def testSplitNoteSequenceAtTimes(self): # Tests splitting a NoteSequence at specified times, truncating notes. sequence = testing_lib.parse_test_proto( music_pb2.NoteSequence, """ time_signatures: { numerator: 4 denominator: 4} tempos: { qpm: 60}""") testing_lib.add_track_to_sequence( sequence, 0, [(12, 100, 0.01, 8.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.0), ('G7', 2.0), ('F', 4.0)]) expected_subsequence_1 = testing_lib.parse_test_proto( music_pb2.NoteSequence, """ time_signatures: { numerator: 4 denominator: 4} tempos: { qpm: 60}""") testing_lib.add_track_to_sequence( expected_subsequence_1, 0, [(12, 100, 0.01, 3.0), (11, 55, 0.22, 0.50), (40, 45, 2.50, 3.0)]) testing_lib.add_chords_to_sequence( expected_subsequence_1, [('C', 1.0), ('G7', 2.0)]) expected_subsequence_1.total_time = 3.0 expected_subsequence_1.subsequence_info.end_time_offset = 5.0 expected_subsequence_2 = testing_lib.parse_test_proto( music_pb2.NoteSequence, """ time_signatures: { numerator: 4 denominator: 4} tempos: { qpm: 60}""") testing_lib.add_chords_to_sequence( expected_subsequence_2, [('G7', 0.0)]) expected_subsequence_2.total_time = 0.0 expected_subsequence_2.subsequence_info.start_time_offset = 3.0 expected_subsequence_2.subsequence_info.end_time_offset = 5.0 expected_subsequence_3 = testing_lib.parse_test_proto( music_pb2.NoteSequence, """ time_signatures: { numerator: 4 denominator: 4} tempos: { qpm: 60}""") testing_lib.add_track_to_sequence( expected_subsequence_3, 0, [(55, 120, 0.0, 0.01), (52, 99, 0.75, 1.0)]) testing_lib.add_chords_to_sequence( expected_subsequence_3, [('F', 0.0)]) expected_subsequence_3.total_time = 1.0 expected_subsequence_3.subsequence_info.start_time_offset = 4.0 expected_subsequence_3.subsequence_info.end_time_offset = 3.0 subsequences = sequences_lib.split_note_sequence( sequence, hop_size_seconds=[3.0, 4.0]) self.assertLen(subsequences, 3) self.assertProtoEquals(expected_subsequence_1, subsequences[0]) self.assertProtoEquals(expected_subsequence_2, subsequences[1]) self.assertProtoEquals(expected_subsequence_3, subsequences[2])
def testSplitNoteSequenceCoincidentTimeChanges(self)
-
Expand source code
def testSplitNoteSequenceCoincidentTimeChanges(self): # Tests splitting a NoteSequence on time changes for a NoteSequence that has # two time changes occurring simultaneously. sequence = testing_lib.parse_test_proto( music_pb2.NoteSequence, """ time_signatures: { numerator: 4 denominator: 4} time_signatures: { time: 2.0 numerator: 3 denominator: 4} tempos: { qpm: 60} tempos: { time: 2.0 qpm: 80}""") 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)]) expected_subsequence_1 = testing_lib.parse_test_proto( music_pb2.NoteSequence, """ time_signatures: { numerator: 4 denominator: 4} tempos: { qpm: 60}""") testing_lib.add_track_to_sequence( expected_subsequence_1, 0, [(12, 100, 0.01, 2.0), (11, 55, 0.22, 0.50)]) testing_lib.add_chords_to_sequence( expected_subsequence_1, [('C', 1.5)]) expected_subsequence_1.total_time = 2.0 expected_subsequence_1.subsequence_info.end_time_offset = 8.0 expected_subsequence_2 = testing_lib.parse_test_proto( music_pb2.NoteSequence, """ time_signatures: { numerator: 3 denominator: 4} tempos: { qpm: 80}""") testing_lib.add_track_to_sequence( expected_subsequence_2, 0, [(40, 45, 0.50, 1.50), (55, 120, 2.0, 2.01), (52, 99, 2.75, 3.0)]) testing_lib.add_chords_to_sequence( expected_subsequence_2, [('C', 0.0), ('G7', 1.0), ('F', 2.8)]) expected_subsequence_2.total_time = 3.0 expected_subsequence_2.subsequence_info.start_time_offset = 2.0 expected_subsequence_2.subsequence_info.end_time_offset = 5.0 subsequences = sequences_lib.split_note_sequence_on_time_changes(sequence) self.assertLen(subsequences, 2) self.assertProtoEquals(expected_subsequence_1, subsequences[0]) self.assertProtoEquals(expected_subsequence_2, subsequences[1])
def testSplitNoteSequenceDuplicateTimeChanges(self)
-
Expand source code
def testSplitNoteSequenceDuplicateTimeChanges(self): # Tests splitting a NoteSequence on time changes for a NoteSequence that has # duplicate time changes. sequence = testing_lib.parse_test_proto( music_pb2.NoteSequence, """ time_signatures: { numerator: 4 denominator: 4} time_signatures: { time: 2.0 numerator: 4 denominator: 4} tempos: { qpm: 60}""") 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)]) expected_subsequence = music_pb2.NoteSequence() expected_subsequence.CopyFrom(sequence) expected_subsequence.subsequence_info.start_time_offset = 0.0 expected_subsequence.subsequence_info.end_time_offset = 0.0 subsequences = sequences_lib.split_note_sequence_on_time_changes(sequence) self.assertLen(subsequences, 1) self.assertProtoEquals(expected_subsequence, subsequences[0])
def testSplitNoteSequenceMultipleTimeChanges(self)
-
Expand source code
def testSplitNoteSequenceMultipleTimeChanges(self): # Tests splitting a NoteSequence on time changes, truncating notes on splits # that occur inside notes. sequence = testing_lib.parse_test_proto( music_pb2.NoteSequence, """ time_signatures: { numerator: 4 denominator: 4} time_signatures: { time: 2.0 numerator: 3 denominator: 4} tempos: { qpm: 60} tempos: { time: 4.25 qpm: 80}""") 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)]) expected_subsequence_1 = testing_lib.parse_test_proto( music_pb2.NoteSequence, """ time_signatures: { numerator: 4 denominator: 4} tempos: { qpm: 60}""") testing_lib.add_track_to_sequence( expected_subsequence_1, 0, [(12, 100, 0.01, 2.0), (11, 55, 0.22, 0.50)]) testing_lib.add_chords_to_sequence( expected_subsequence_1, [('C', 1.5)]) expected_subsequence_1.total_time = 2.0 expected_subsequence_1.subsequence_info.end_time_offset = 8.0 expected_subsequence_2 = testing_lib.parse_test_proto( music_pb2.NoteSequence, """ time_signatures: { numerator: 3 denominator: 4} tempos: { qpm: 60}""") testing_lib.add_track_to_sequence( expected_subsequence_2, 0, [(40, 45, 0.50, 1.50), (55, 120, 2.0, 2.01)]) testing_lib.add_chords_to_sequence( expected_subsequence_2, [('C', 0.0), ('G7', 1.0)]) expected_subsequence_2.total_time = 2.01 expected_subsequence_2.subsequence_info.start_time_offset = 2.0 expected_subsequence_2.subsequence_info.end_time_offset = 5.99 expected_subsequence_3 = testing_lib.parse_test_proto( music_pb2.NoteSequence, """ time_signatures: { numerator: 3 denominator: 4} tempos: { qpm: 80}""") testing_lib.add_track_to_sequence( expected_subsequence_3, 0, [(52, 99, 0.5, 0.75)]) testing_lib.add_chords_to_sequence( expected_subsequence_3, [('G7', 0.0), ('F', 0.55)]) expected_subsequence_3.total_time = 0.75 expected_subsequence_3.subsequence_info.start_time_offset = 4.25 expected_subsequence_3.subsequence_info.end_time_offset = 5.0 subsequences = sequences_lib.split_note_sequence_on_time_changes(sequence) self.assertLen(subsequences, 3) self.assertProtoEquals(expected_subsequence_1, subsequences[0]) self.assertProtoEquals(expected_subsequence_2, subsequences[1]) self.assertProtoEquals(expected_subsequence_3, subsequences[2])
def testSplitNoteSequenceMultipleTimeChangesSkipSplitsInsideNotes(self)
-
Expand source code
def testSplitNoteSequenceMultipleTimeChangesSkipSplitsInsideNotes(self): # Tests splitting a NoteSequence on time changes skipping splits that occur # inside notes. sequence = testing_lib.parse_test_proto( music_pb2.NoteSequence, """ time_signatures: { numerator: 4 denominator: 4} time_signatures: { time: 2.0 numerator: 3 denominator: 4} tempos: { qpm: 60} tempos: { time: 4.25 qpm: 80}""") testing_lib.add_track_to_sequence( sequence, 0, [(12, 100, 0.01, 3.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)]) expected_subsequence_1 = testing_lib.parse_test_proto( music_pb2.NoteSequence, """ time_signatures: { numerator: 4 denominator: 4} time_signatures: { time: 2.0 numerator: 3 denominator: 4} tempos: { qpm: 60}""") testing_lib.add_track_to_sequence( expected_subsequence_1, 0, [(12, 100, 0.01, 3.0), (11, 55, 0.22, 0.50), (40, 45, 2.50, 3.50), (55, 120, 4.0, 4.01)]) testing_lib.add_chords_to_sequence( expected_subsequence_1, [('C', 1.5), ('G7', 3.0)]) expected_subsequence_1.total_time = 4.01 expected_subsequence_1.subsequence_info.end_time_offset = 0.99 expected_subsequence_2 = testing_lib.parse_test_proto( music_pb2.NoteSequence, """ time_signatures: { numerator: 3 denominator: 4} tempos: { qpm: 80}""") testing_lib.add_track_to_sequence( expected_subsequence_2, 0, [(52, 99, 0.5, 0.75)]) testing_lib.add_chords_to_sequence(expected_subsequence_2, [ ('G7', 0.0), ('F', 0.55)]) expected_subsequence_2.total_time = 0.75 expected_subsequence_2.subsequence_info.start_time_offset = 4.25 subsequences = sequences_lib.split_note_sequence_on_time_changes( sequence, skip_splits_inside_notes=True) self.assertLen(subsequences, 2) self.assertProtoEquals(expected_subsequence_1, subsequences[0]) self.assertProtoEquals(expected_subsequence_2, subsequences[1])
def testSplitNoteSequenceNoTimeChanges(self)
-
Expand source code
def testSplitNoteSequenceNoTimeChanges(self): # Tests splitting a NoteSequence on time changes for a NoteSequence that has # no time changes (time signature and tempo changes). 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)]) expected_subsequence = music_pb2.NoteSequence() expected_subsequence.CopyFrom(sequence) expected_subsequence.subsequence_info.start_time_offset = 0.0 expected_subsequence.subsequence_info.end_time_offset = 0.0 subsequences = sequences_lib.split_note_sequence_on_time_changes(sequence) self.assertLen(subsequences, 1) self.assertProtoEquals(expected_subsequence, subsequences[0])
def testSplitNoteSequenceOnSilence(self)
-
Expand source code
def testSplitNoteSequenceOnSilence(self): sequence = music_pb2.NoteSequence() testing_lib.add_track_to_sequence( sequence, 0, [(12, 100, 0.01, 1.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)]) expected_subsequence_1 = music_pb2.NoteSequence() testing_lib.add_track_to_sequence( expected_subsequence_1, 0, [(12, 100, 0.01, 1.0), (11, 55, 0.22, 0.50)]) expected_subsequence_1.total_time = 1.0 expected_subsequence_1.subsequence_info.end_time_offset = 4.0 expected_subsequence_2 = music_pb2.NoteSequence() testing_lib.add_track_to_sequence( expected_subsequence_2, 0, [(40, 45, 0.0, 1.0), (55, 120, 1.50, 1.51)]) expected_subsequence_2.total_time = 1.51 expected_subsequence_2.subsequence_info.start_time_offset = 2.50 expected_subsequence_2.subsequence_info.end_time_offset = 0.99 expected_subsequence_3 = music_pb2.NoteSequence() testing_lib.add_track_to_sequence( expected_subsequence_3, 0, [(52, 99, 0.0, 0.25)]) expected_subsequence_3.total_time = 0.25 expected_subsequence_3.subsequence_info.start_time_offset = 4.75 subsequences = sequences_lib.split_note_sequence_on_silence( sequence, gap_seconds=0.5) self.assertLen(subsequences, 3) self.assertProtoEquals(expected_subsequence_1, subsequences[0]) self.assertProtoEquals(expected_subsequence_2, subsequences[1]) self.assertProtoEquals(expected_subsequence_3, subsequences[2])
def testSplitNoteSequenceOnSilenceInitialGap(self)
-
Expand source code
def testSplitNoteSequenceOnSilenceInitialGap(self): sequence = music_pb2.NoteSequence() testing_lib.add_track_to_sequence( sequence, 0, [(12, 100, 1.5, 2.0), (11, 55, 1.5, 3.0), (40, 45, 2.5, 3.5)]) expected_subsequence_1 = music_pb2.NoteSequence() expected_subsequence_1.total_time = 0.0 expected_subsequence_1.subsequence_info.end_time_offset = 3.5 expected_subsequence_2 = music_pb2.NoteSequence() testing_lib.add_track_to_sequence( expected_subsequence_2, 0, [(12, 100, 0.0, 0.5), (11, 55, 0.0, 1.5), (40, 45, 1.0, 2.0)]) expected_subsequence_2.total_time = 2.0 expected_subsequence_2.subsequence_info.start_time_offset = 1.5 subsequences = sequences_lib.split_note_sequence_on_silence( sequence, gap_seconds=1.0) self.assertLen(subsequences, 2) self.assertProtoEquals(expected_subsequence_1, subsequences[0]) self.assertProtoEquals(expected_subsequence_2, subsequences[1])
def testSplitNoteSequenceSkipSplitsInsideNotes(self)
-
Expand source code
def testSplitNoteSequenceSkipSplitsInsideNotes(self): # Tests splitting a NoteSequence at regular hop size, skipping splits that # would have occurred inside a note. sequence = testing_lib.parse_test_proto( music_pb2.NoteSequence, """ time_signatures: { numerator: 4 denominator: 4} tempos: { qpm: 60}""") testing_lib.add_track_to_sequence( sequence, 0, [(12, 100, 0.01, 3.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', 0.0), ('G7', 3.0), ('F', 4.5)]) expected_subsequence_1 = testing_lib.parse_test_proto( music_pb2.NoteSequence, """ time_signatures: { numerator: 4 denominator: 4} tempos: { qpm: 60}""") testing_lib.add_track_to_sequence( expected_subsequence_1, 0, [(12, 100, 0.01, 3.0), (11, 55, 0.22, 0.50), (40, 45, 2.50, 3.50)]) testing_lib.add_chords_to_sequence( expected_subsequence_1, [('C', 0.0), ('G7', 3.0)]) expected_subsequence_1.total_time = 3.50 expected_subsequence_1.subsequence_info.end_time_offset = 1.5 expected_subsequence_2 = testing_lib.parse_test_proto( music_pb2.NoteSequence, """ time_signatures: { numerator: 4 denominator: 4} tempos: { qpm: 60}""") testing_lib.add_track_to_sequence( expected_subsequence_2, 0, [(55, 120, 0.0, 0.01), (52, 99, 0.75, 1.0)]) testing_lib.add_chords_to_sequence( expected_subsequence_2, [('G7', 0.0), ('F', 0.5)]) expected_subsequence_2.total_time = 1.0 expected_subsequence_2.subsequence_info.start_time_offset = 4.0 subsequences = sequences_lib.split_note_sequence( sequence, hop_size_seconds=2.0, skip_splits_inside_notes=True) self.assertLen(subsequences, 2) self.assertProtoEquals(expected_subsequence_1, subsequences[0]) self.assertProtoEquals(expected_subsequence_2, subsequences[1])
def testSplitNoteSequenceWithHopSize(self)
-
Expand source code
def testSplitNoteSequenceWithHopSize(self): # Tests splitting a NoteSequence at regular hop size, truncating notes. sequence = testing_lib.parse_test_proto( music_pb2.NoteSequence, """ time_signatures: { numerator: 4 denominator: 4} tempos: { qpm: 60}""") testing_lib.add_track_to_sequence( sequence, 0, [(12, 100, 0.01, 8.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.0), ('G7', 2.0), ('F', 4.0)]) expected_subsequence_1 = testing_lib.parse_test_proto( music_pb2.NoteSequence, """ time_signatures: { numerator: 4 denominator: 4} tempos: { qpm: 60}""") testing_lib.add_track_to_sequence( expected_subsequence_1, 0, [(12, 100, 0.01, 3.0), (11, 55, 0.22, 0.50), (40, 45, 2.50, 3.0)]) testing_lib.add_chords_to_sequence( expected_subsequence_1, [('C', 1.0), ('G7', 2.0)]) expected_subsequence_1.total_time = 3.0 expected_subsequence_1.subsequence_info.end_time_offset = 5.0 expected_subsequence_2 = testing_lib.parse_test_proto( music_pb2.NoteSequence, """ time_signatures: { numerator: 4 denominator: 4} tempos: { qpm: 60}""") testing_lib.add_track_to_sequence( expected_subsequence_2, 0, [(55, 120, 1.0, 1.01), (52, 99, 1.75, 2.0)]) testing_lib.add_chords_to_sequence( expected_subsequence_2, [('G7', 0.0), ('F', 1.0)]) expected_subsequence_2.total_time = 2.0 expected_subsequence_2.subsequence_info.start_time_offset = 3.0 expected_subsequence_2.subsequence_info.end_time_offset = 3.0 expected_subsequence_3 = testing_lib.parse_test_proto( music_pb2.NoteSequence, """ time_signatures: { numerator: 4 denominator: 4} tempos: { qpm: 60}""") testing_lib.add_chords_to_sequence( expected_subsequence_3, [('F', 0.0)]) expected_subsequence_3.total_time = 0.0 expected_subsequence_3.subsequence_info.start_time_offset = 6.0 expected_subsequence_3.subsequence_info.end_time_offset = 2.0 subsequences = sequences_lib.split_note_sequence( sequence, hop_size_seconds=3.0) self.assertLen(subsequences, 3) self.assertProtoEquals(expected_subsequence_1, subsequences[0]) self.assertProtoEquals(expected_subsequence_2, subsequences[1]) self.assertProtoEquals(expected_subsequence_3, subsequences[2])
def testSplitNoteSequenceWithStatelessEvents(self)
-
Expand source code
def testSplitNoteSequenceWithStatelessEvents(self): # Tests splitting a NoteSequence at specified times with stateless events. sequence = testing_lib.parse_test_proto( music_pb2.NoteSequence, """ time_signatures: { numerator: 4 denominator: 4} tempos: { qpm: 60}""") testing_lib.add_track_to_sequence( sequence, 0, [(12, 100, 0.01, 8.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_beats_to_sequence(sequence, [1.0, 2.0, 4.0]) expected_subsequence_1 = testing_lib.parse_test_proto( music_pb2.NoteSequence, """ time_signatures: { numerator: 4 denominator: 4} tempos: { qpm: 60}""") testing_lib.add_track_to_sequence( expected_subsequence_1, 0, [(12, 100, 0.01, 3.0), (11, 55, 0.22, 0.50), (40, 45, 2.50, 3.0)]) testing_lib.add_beats_to_sequence(expected_subsequence_1, [1.0, 2.0]) expected_subsequence_1.total_time = 3.0 expected_subsequence_1.subsequence_info.end_time_offset = 5.0 expected_subsequence_2 = testing_lib.parse_test_proto( music_pb2.NoteSequence, """ time_signatures: { numerator: 4 denominator: 4} tempos: { qpm: 60}""") expected_subsequence_2.total_time = 0.0 expected_subsequence_2.subsequence_info.start_time_offset = 3.0 expected_subsequence_2.subsequence_info.end_time_offset = 5.0 expected_subsequence_3 = testing_lib.parse_test_proto( music_pb2.NoteSequence, """ time_signatures: { numerator: 4 denominator: 4} tempos: { qpm: 60}""") testing_lib.add_track_to_sequence( expected_subsequence_3, 0, [(55, 120, 0.0, 0.01), (52, 99, 0.75, 1.0)]) testing_lib.add_beats_to_sequence(expected_subsequence_3, [0.0]) expected_subsequence_3.total_time = 1.0 expected_subsequence_3.subsequence_info.start_time_offset = 4.0 expected_subsequence_3.subsequence_info.end_time_offset = 3.0 subsequences = sequences_lib.split_note_sequence( sequence, hop_size_seconds=[3.0, 4.0]) self.assertLen(subsequences, 3) self.assertProtoEquals(expected_subsequence_1, subsequences[0]) self.assertProtoEquals(expected_subsequence_2, subsequences[1]) self.assertProtoEquals(expected_subsequence_3, subsequences[2])
def testStepsPerBar(self)
-
Expand source code
def testStepsPerBar(self): qns = sequences_lib.quantize_note_sequence( self.note_sequence, self.steps_per_quarter) self.assertEqual(16, sequences_lib.steps_per_bar_in_quantized_sequence(qns)) self.note_sequence.time_signatures[0].numerator = 6 self.note_sequence.time_signatures[0].denominator = 8 qns = sequences_lib.quantize_note_sequence( self.note_sequence, self.steps_per_quarter) self.assertEqual(12.0, sequences_lib.steps_per_bar_in_quantized_sequence(qns))
def testStepsPerQuarterToStepsPerSecond(self)
-
Expand source code
def testStepsPerQuarterToStepsPerSecond(self): self.assertEqual( 4.0, sequences_lib.steps_per_quarter_to_steps_per_second(4, 60.0))
def testStretchNoteSequence(self)
-
Expand source code
def testStretchNoteSequence(self): expected_stretched_sequence = copy.deepcopy(self.note_sequence) expected_stretched_sequence.tempos[0].qpm = 40 testing_lib.add_track_to_sequence( self.note_sequence, 0, [(12, 100, 0.0, 10.0), (11, 55, 0.2, 0.5), (40, 45, 2.5, 3.5)]) testing_lib.add_track_to_sequence( expected_stretched_sequence, 0, [(12, 100, 0.0, 15.0), (11, 55, 0.3, 0.75), (40, 45, 3.75, 5.25)]) testing_lib.add_chords_to_sequence( self.note_sequence, [('B7', 0.5), ('Em9', 2.0)]) testing_lib.add_chords_to_sequence( expected_stretched_sequence, [('B7', 0.75), ('Em9', 3.0)]) prestretched_sequence = copy.deepcopy(self.note_sequence) stretched_sequence = sequences_lib.stretch_note_sequence( self.note_sequence, stretch_factor=1.5, in_place=False) self.assertProtoEquals(expected_stretched_sequence, stretched_sequence) # Make sure the proto was not modified self.assertProtoEquals(prestretched_sequence, self.note_sequence) sequences_lib.stretch_note_sequence( self.note_sequence, stretch_factor=1.5, in_place=True) self.assertProtoEquals(stretched_sequence, self.note_sequence)
def testTransposeNoteSequence(self)
-
Expand source code
def testTransposeNoteSequence(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)]) sequence.text_annotations.add( time=1, annotation_type=CHORD_SYMBOL, text='N.C.') sequence.text_annotations.add( time=2, annotation_type=CHORD_SYMBOL, text='E7') sequence.key_signatures.add( time=0, key=music_pb2.NoteSequence.KeySignature.E, mode=music_pb2.NoteSequence.KeySignature.MIXOLYDIAN) expected_sequence = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( expected_sequence, 0, [(13, 100, 0.01, 10.0), (12, 55, 0.22, 0.50), (41, 45, 2.50, 3.50), (56, 120, 4.0, 4.01), (53, 99, 4.75, 5.0)]) expected_sequence.text_annotations.add( time=1, annotation_type=CHORD_SYMBOL, text='N.C.') expected_sequence.text_annotations.add( time=2, annotation_type=CHORD_SYMBOL, text='F7') expected_sequence.key_signatures.add( time=0, key=music_pb2.NoteSequence.KeySignature.F, mode=music_pb2.NoteSequence.KeySignature.MIXOLYDIAN) transposed_sequence, delete_count = sequences_lib.transpose_note_sequence( sequence, 1) self.assertProtoEquals(expected_sequence, transposed_sequence) self.assertEqual(delete_count, 0)
def testTransposeNoteSequenceOutOfRange(self)
-
Expand source code
def testTransposeNoteSequenceOutOfRange(self): sequence = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( sequence, 0, [(35, 100, 0.01, 10.0), (36, 55, 0.22, 0.50), (37, 45, 2.50, 3.50), (38, 120, 4.0, 4.01), (39, 99, 4.75, 5.0)]) expected_sequence_1 = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( expected_sequence_1, 0, [(39, 100, 0.01, 10.0), (40, 55, 0.22, 0.50)]) expected_sequence_2 = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( expected_sequence_2, 0, [(30, 120, 4.0, 4.01), (31, 99, 4.75, 5.0)]) sequence_copy = copy.copy(sequence) transposed_sequence, delete_count = sequences_lib.transpose_note_sequence( sequence_copy, 4, 30, 40) self.assertProtoEquals(expected_sequence_1, transposed_sequence) self.assertEqual(delete_count, 3) sequence_copy = copy.copy(sequence) transposed_sequence, delete_count = sequences_lib.transpose_note_sequence( sequence_copy, -8, 30, 40) self.assertProtoEquals(expected_sequence_2, transposed_sequence) self.assertEqual(delete_count, 3)
def testTrimNoteSequence(self)
-
Expand source code
def testTrimNoteSequence(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)]) expected_subsequence = copy.copy(self.note_sequence) testing_lib.add_track_to_sequence( expected_subsequence, 0, [(40, 45, 2.50, 3.50), (55, 120, 4.0, 4.01)]) expected_subsequence.total_time = 4.75 subsequence = sequences_lib.trim_note_sequence(sequence, 2.5, 4.75) self.assertProtoEquals(expected_subsequence, subsequence)
Inherited members