Example #1
0
  def testSimpleSequenceToPrettyMidi_DropEventsAfterLastNote(self):
    source_midi = pretty_midi.PrettyMIDI(self.midi_simple_filename)
    multi_tempo_sequence_proto = midi_io.midi_to_sequence_proto(source_midi)
    # Add a final tempo long after the last note.
    multi_tempo_sequence_proto.tempos.add(time=600.0, qpm=120)

    # Translate without dropping.
    translated_midi = midi_io.sequence_proto_to_pretty_midi(
        multi_tempo_sequence_proto)
    self.CheckPrettyMidiAndSequence(translated_midi, multi_tempo_sequence_proto)

    # Translate dropping anything after the last note.
    translated_midi = midi_io.sequence_proto_to_pretty_midi(
        multi_tempo_sequence_proto, drop_events_n_seconds_after_last_note=0)
    # The added tempo should have been dropped.
    del multi_tempo_sequence_proto.tempos[-1]
    self.CheckPrettyMidiAndSequence(translated_midi, multi_tempo_sequence_proto)

    # Add a final tempo 15 seconds after the last note.
    last_note_time = max([n.end_time for n in multi_tempo_sequence_proto.notes])
    multi_tempo_sequence_proto.tempos.add(time=last_note_time + 15, qpm=120)
    # Translate dropping anything 30 seconds after the last note, which should
    # preserve the added tempo.
    translated_midi = midi_io.sequence_proto_to_pretty_midi(
        multi_tempo_sequence_proto, drop_events_n_seconds_after_last_note=30)
    self.CheckPrettyMidiAndSequence(translated_midi, multi_tempo_sequence_proto)
Example #2
0
  def testEmptySequenceToPrettyMidi_DropEventsAfterLastNote(self):
    source_sequence = music_pb2.NoteSequence()

    # Translate without dropping.
    translated_midi = midi_io.sequence_proto_to_pretty_midi(
        source_sequence)
    self.assertEqual(1, len(translated_midi.instruments))
    self.assertEqual(0, len(translated_midi.instruments[0].notes))

    # Translate dropping anything after 30 seconds.
    translated_midi = midi_io.sequence_proto_to_pretty_midi(
        source_sequence, drop_events_n_seconds_after_last_note=30)
    self.assertEqual(1, len(translated_midi.instruments))
    self.assertEqual(0, len(translated_midi.instruments[0].notes))
Example #3
0
  def testNonEmptySequenceWithNoNotesToPrettyMidi_DropEventsAfterLastNote(self):
    source_sequence = music_pb2.NoteSequence()
    source_sequence.tempos.add(time=0, qpm=120)
    source_sequence.tempos.add(time=10, qpm=160)
    source_sequence.tempos.add(time=40, qpm=240)

    # Translate without dropping.
    translated_midi = midi_io.sequence_proto_to_pretty_midi(
        source_sequence)
    self.CheckPrettyMidiAndSequence(translated_midi, source_sequence)

    # Translate dropping anything after 30 seconds.
    translated_midi = midi_io.sequence_proto_to_pretty_midi(
        source_sequence, drop_events_n_seconds_after_last_note=30)
    del source_sequence.tempos[-1]
    self.CheckPrettyMidiAndSequence(translated_midi, source_sequence)
Example #4
0
    def testNonEmptySequenceWithNoNotesToPrettyMidi_DropEventsAfterLastNote(
            self):
        source_sequence = music_pb2.NoteSequence()
        source_sequence.tempos.add(time=0, qpm=120)
        source_sequence.tempos.add(time=10, qpm=160)
        source_sequence.tempos.add(time=40, qpm=240)

        # Translate without dropping.
        translated_midi = midi_io.sequence_proto_to_pretty_midi(
            source_sequence)
        self.CheckPrettyMidiAndSequence(translated_midi, source_sequence)

        # Translate dropping anything after 30 seconds.
        translated_midi = midi_io.sequence_proto_to_pretty_midi(
            source_sequence, drop_events_n_seconds_after_last_note=30)
        del source_sequence.tempos[-1]
        self.CheckPrettyMidiAndSequence(translated_midi, source_sequence)
Example #5
0
def split_on_downbeats(sequence,
                       bars_per_segment,
                       downbeats=None,
                       skip_bars=0,
                       min_notes_per_segment=0,
                       include_span=False,
                       tolerance=1e-6):
    """Split a note sequence on bar boundaries.

    Args:
        sequence: A `NoteSequence`.
        bars_per_segment: The number of bars (downbeats) per segment.
        downbeats: Downbeat times in seconds. If not specified, they will be determined using
            `pretty_midi`.
        skip_bars: The index of the bar to start at.
        min_notes_per_segment: The minimum required number of notes per segment. Segments
            containing fewer notes will be skipped.
        include_span: If `True`, the output will be tuples of the form
            `(start_bar_idx, end_bar_idx, segment)`.
        tolerance: The maximum time in seconds a note can precede a downbeat to be included in the
            following bar.
    Yields:
        `NoteSequence`s, or tuples of the form `(start_bar_idx, end_bar_idx, segment)`.
    """
    if downbeats is None:
        downbeats = midi_io.sequence_proto_to_pretty_midi(
            sequence).get_downbeats()
    downbeats = [d - tolerance for d in downbeats]
    downbeats = [d for d in downbeats if d < sequence.total_time]

    try:
        iter(bars_per_segment)
    except TypeError:
        bars_per_segment = [bars_per_segment]

    for bps in bars_per_segment:
        first_split = skip_bars or bps  # Do not split at time 0
        split_times = list(downbeats[first_split::bps])
        segments = sequences_lib.split_note_sequence(
            sequence, hop_size_seconds=split_times)
        if skip_bars:
            # The first segment will contain the bars we want to skip
            segments.pop(0)

        for i, segment in enumerate(segments):
            start = skip_bars + i * bps
            end = start + bps

            if len(segment.notes) < min_notes_per_segment:
                print(
                    f'Skipping segment {start}-{end} with {len(segment.notes)} notes',
                    file=sys.stderr)
                continue

            if include_span:
                yield start, end, segment
            else:
                yield segment
Example #6
0
  def testSimpleSequenceToPrettyMidi_MultipleTempos(self):
    source_midi = pretty_midi.PrettyMIDI(self.midi_simple_filename)
    multi_tempo_sequence_proto = midi_io.midi_to_sequence_proto(source_midi)
    multi_tempo_sequence_proto.tempos.add(time=1.0, qpm=60)
    multi_tempo_sequence_proto.tempos.add(time=2.0, qpm=120)

    translated_midi = midi_io.sequence_proto_to_pretty_midi(
        multi_tempo_sequence_proto)

    self.CheckPrettyMidiAndSequence(translated_midi, multi_tempo_sequence_proto)
Example #7
0
  def testSimpleSequenceToPrettyMidi_MultipleTempos(self):
    source_midi = pretty_midi.PrettyMIDI(self.midi_simple_filename)
    multi_tempo_sequence_proto = midi_io.midi_to_sequence_proto(source_midi)
    multi_tempo_sequence_proto.tempos.add(time=1.0, qpm=60)
    multi_tempo_sequence_proto.tempos.add(time=2.0, qpm=120)

    translated_midi = midi_io.sequence_proto_to_pretty_midi(
        multi_tempo_sequence_proto)

    self.CheckPrettyMidiAndSequence(translated_midi, multi_tempo_sequence_proto)
Example #8
0
  def testSimpleSequenceToPrettyMidi_DefaultTicksAndTempo(self):
    source_midi = pretty_midi.PrettyMIDI(self.midi_simple_filename)
    stripped_sequence_proto = midi_io.midi_to_sequence_proto(source_midi)
    del stripped_sequence_proto.tempos[:]
    stripped_sequence_proto.ClearField('ticks_per_quarter')

    expected_sequence_proto = music_pb2.NoteSequence()
    expected_sequence_proto.CopyFrom(stripped_sequence_proto)
    expected_sequence_proto.tempos.add(
        qpm=constants.DEFAULT_QUARTERS_PER_MINUTE)
    expected_sequence_proto.ticks_per_quarter = constants.STANDARD_PPQ

    translated_midi = midi_io.sequence_proto_to_pretty_midi(
        stripped_sequence_proto)

    self.CheckPrettyMidiAndSequence(translated_midi, expected_sequence_proto)
Example #9
0
  def testSimpleSequenceToPrettyMidi_DefaultTicksAndTempo(self):
    source_midi = pretty_midi.PrettyMIDI(self.midi_simple_filename)
    stripped_sequence_proto = midi_io.midi_to_sequence_proto(source_midi)
    del stripped_sequence_proto.tempos[:]
    stripped_sequence_proto.ClearField('ticks_per_quarter')

    expected_sequence_proto = music_pb2.NoteSequence()
    expected_sequence_proto.CopyFrom(stripped_sequence_proto)
    expected_sequence_proto.tempos.add(
        qpm=constants.DEFAULT_QUARTERS_PER_MINUTE)
    expected_sequence_proto.ticks_per_quarter = constants.STANDARD_PPQ

    translated_midi = midi_io.sequence_proto_to_pretty_midi(
        stripped_sequence_proto)

    self.CheckPrettyMidiAndSequence(translated_midi, expected_sequence_proto)
Example #10
0
def synthesize(sequence, sample_rate, wave=np.sin):
  """Synthesizes audio from a music_pb2.NoteSequence using a waveform.

  This uses the pretty_midi `synthesize` method. Sound quality will be lower
  than using `fluidsynth` with a good SoundFont.

  Args:
    sequence: A music_pb2.NoteSequence to synthesize.
    sample_rate: An integer audio sampling rate in Hz.
    wave: Function that returns a periodic waveform.

  Returns:
    A 1-D numpy float array containing the synthesized waveform.
  """
  midi = midi_io.sequence_proto_to_pretty_midi(sequence)
  return midi.synthesize(fs=sample_rate, wave=wave)
Example #11
0
def fluidsynth(sequence, sample_rate, sf2_path=None):
  """Synthesizes audio from a music_pb2.NoteSequence using FluidSynth.

  This uses the pretty_midi `fluidsynth` method. In order to use this synth,
  you must have FluidSynth and pyFluidSynth installed.

  Args:
    sequence: A music_pb2.NoteSequence to synthesize.
    sample_rate: An integer audio sampling rate in Hz.
    sf2_path: A string path to a SoundFont. If None, uses the TimGM6mb.sf2 file
        included with pretty_midi.

  Returns:
    A 1-D numpy float array containing the synthesized waveform.
  """
  midi = midi_io.sequence_proto_to_pretty_midi(sequence)
  return midi.fluidsynth(fs=sample_rate, sf2_path=sf2_path)
Example #12
0
  def testSimpleSequenceToPrettyMidi_FirstTempoNotAtZero(self):
    source_midi = pretty_midi.PrettyMIDI(self.midi_simple_filename)
    multi_tempo_sequence_proto = midi_io.midi_to_sequence_proto(source_midi)
    del multi_tempo_sequence_proto.tempos[:]
    multi_tempo_sequence_proto.tempos.add(time=1.0, qpm=60)
    multi_tempo_sequence_proto.tempos.add(time=2.0, qpm=120)

    translated_midi = midi_io.sequence_proto_to_pretty_midi(
        multi_tempo_sequence_proto)

    # Translating to MIDI adds an implicit DEFAULT_QUARTERS_PER_MINUTE tempo
    # at time 0, so recreate the list with that in place.
    del multi_tempo_sequence_proto.tempos[:]
    multi_tempo_sequence_proto.tempos.add(
        time=0.0, qpm=constants.DEFAULT_QUARTERS_PER_MINUTE)
    multi_tempo_sequence_proto.tempos.add(time=1.0, qpm=60)
    multi_tempo_sequence_proto.tempos.add(time=2.0, qpm=120)

    self.CheckPrettyMidiAndSequence(translated_midi, multi_tempo_sequence_proto)
Example #13
0
  def testSimpleSequenceToPrettyMidi_FirstTempoNotAtZero(self):
    source_midi = pretty_midi.PrettyMIDI(self.midi_simple_filename)
    multi_tempo_sequence_proto = midi_io.midi_to_sequence_proto(source_midi)
    del multi_tempo_sequence_proto.tempos[:]
    multi_tempo_sequence_proto.tempos.add(time=1.0, qpm=60)
    multi_tempo_sequence_proto.tempos.add(time=2.0, qpm=120)

    translated_midi = midi_io.sequence_proto_to_pretty_midi(
        multi_tempo_sequence_proto)

    # Translating to MIDI adds an implicit DEFAULT_QUARTERS_PER_MINUTE tempo
    # at time 0, so recreate the list with that in place.
    del multi_tempo_sequence_proto.tempos[:]
    multi_tempo_sequence_proto.tempos.add(
        time=0.0, qpm=constants.DEFAULT_QUARTERS_PER_MINUTE)
    multi_tempo_sequence_proto.tempos.add(time=1.0, qpm=60)
    multi_tempo_sequence_proto.tempos.add(time=2.0, qpm=120)

    self.CheckPrettyMidiAndSequence(translated_midi, multi_tempo_sequence_proto)
Example #14
0
  def testInstrumentInfo_NoteSequenceToPrettyMidi(self):
    source_sequence = music_pb2.NoteSequence()
    source_sequence.notes.add(
        pitch=60, start_time=0.0, end_time=0.5, velocity=80, instrument=0)
    source_sequence.notes.add(
        pitch=60, start_time=0.5, end_time=1.0, velocity=80, instrument=1)
    instrument_info1 = source_sequence.instrument_infos.add()
    instrument_info1.name = 'inst_0'
    instrument_info1.instrument = 0
    instrument_info2 = source_sequence.instrument_infos.add()
    instrument_info2.name = 'inst_1'
    instrument_info2.instrument = 1
    translated_midi = midi_io.sequence_proto_to_pretty_midi(source_sequence)
    translated_sequence = midi_io.midi_to_note_sequence(translated_midi)

    self.assertEqual(
        len(source_sequence.instrument_infos),
        len(translated_sequence.instrument_infos))
    self.assertEqual(source_sequence.instrument_infos[0].name,
                     translated_sequence.instrument_infos[0].name)
    self.assertEqual(source_sequence.instrument_infos[1].name,
                     translated_sequence.instrument_infos[1].name)
Example #15
0
  def testInstrumentInfo_NoteSequenceToPrettyMidi(self):
    source_sequence = music_pb2.NoteSequence()
    source_sequence.notes.add(
        pitch=60, start_time=0.0, end_time=0.5, velocity=80, instrument=0)
    source_sequence.notes.add(
        pitch=60, start_time=0.5, end_time=1.0, velocity=80, instrument=1)
    instrument_info1 = source_sequence.instrument_infos.add()
    instrument_info1.name = 'inst_0'
    instrument_info1.instrument = 0
    instrument_info2 = source_sequence.instrument_infos.add()
    instrument_info2.name = 'inst_1'
    instrument_info2.instrument = 1
    translated_midi = midi_io.sequence_proto_to_pretty_midi(source_sequence)
    translated_sequence = midi_io.midi_to_note_sequence(translated_midi)

    self.assertEqual(
        len(source_sequence.instrument_infos),
        len(translated_sequence.instrument_infos))
    self.assertEqual(source_sequence.instrument_infos[0].name,
                     translated_sequence.instrument_infos[0].name)
    self.assertEqual(source_sequence.instrument_infos[1].name,
                     translated_sequence.instrument_infos[1].name)
Example #16
0
 def CheckSequenceToPrettyMidi(self, filename):
   """Test the translation from Sequence proto to PrettyMIDI."""
   source_midi = pretty_midi.PrettyMIDI(filename)
   sequence_proto = midi_io.midi_to_sequence_proto(source_midi)
   translated_midi = midi_io.sequence_proto_to_pretty_midi(sequence_proto)
   self.CheckPrettyMidiAndSequence(translated_midi, sequence_proto)
Example #17
0
def get_downbeats(sequence):
    downbeats = midi_io.sequence_proto_to_pretty_midi(sequence).get_downbeats()
    return [d for d in downbeats if d < sequence.total_time]
Example #18
0
 def CheckSequenceToPrettyMidi(self, filename):
     """Test the translation from Sequence proto to PrettyMIDI."""
     source_midi = pretty_midi.PrettyMIDI(filename)
     sequence_proto = midi_io.midi_to_sequence_proto(source_midi)
     translated_midi = midi_io.sequence_proto_to_pretty_midi(sequence_proto)
     self.CheckPrettyMidiAndSequence(translated_midi, sequence_proto)