def to_midi(self, tempo=120, instrument=PIANO, tonic=None, lib=None):
        if not self.progression:
            Logging.error("Progression not assigned!")
            return None
        if not tonic:
            tonic = self.meta['tonic']
        midi = PrettyMIDI()
        unit_length = 30 / tempo
        ins = Instrument(instrument)
        if not self.saved_in_source_base:
            current_pos = 0
            for i in self.get_chord_progression():
                memo = -1
                length = 0
                for j in i:
                    if j == memo:
                        length += unit_length
                    else:
                        if memo != -1:
                            for pitch in memo.to_midi_pitch(
                                    tonic=self.__key_changer(
                                        self.meta['tonic'], memo.root, tonic)):
                                note = Note(pitch=pitch,
                                            velocity=80,
                                            start=current_pos,
                                            end=current_pos + length)
                                ins.notes.append(note)
                        current_pos += length
                        length = unit_length
                        memo = j
                for pitch in memo.to_midi_pitch(tonic=self.__key_changer(
                        self.meta['tonic'], memo.root, tonic)):
                    note = Note(pitch=pitch,
                                velocity=80,
                                start=current_pos,
                                end=current_pos + length)
                    ins.notes.append(note)
                current_pos += length

        else:
            if lib is None:
                lib = pickle_read('lib')
            try:
                all_notes = lib[self.meta['source']]
            except:
                Logging.error(
                    'Progression with source name {n} '
                    'cannot be find in library! '
                    'Call set_in_lib(in_lib=False) to generate MIDI '
                    'by progression list itself'.format(n=self.meta['source']))
                return False
            for note in all_notes:
                ins.notes.append(
                    Note(start=note[0] * unit_length,
                         end=note[1] * unit_length,
                         pitch=note[2] + str_to_root[tonic],
                         velocity=note[3]))

        midi.instruments.append(ins)
        return midi
Exemple #2
0
def create_drum_pretty_midi(
    onset_matrix: np.ndarray,
    vel_matrix: np.ndarray,
    q_note_res: int = Q_NOTE_RES,
    bpm: float = 120.0,
    playback_midi_map: List[int] = CHRISTHETREE_PLAYBACK_MIDI_MAP_8
) -> PrettyMIDI:
    pm = PrettyMIDI(initial_tempo=bpm)

    res = (60.0 / bpm) / q_note_res
    drum_inst = Instrument(0, is_drum=True)
    n_beats = vel_matrix.shape[0]

    for beat_idx in range(n_beats):
        onset_vals = onset_matrix[beat_idx, :]
        onsets = np.nonzero(onset_vals >= 0.5)[0]
        vels = vel_matrix[beat_idx, :]

        for onset in onsets:
            pitch = playback_midi_map[onset]
            vel = int((vels[onset] * 127) + 0.5)
            _add_note(drum_inst, pitch, vel, res * beat_idx,
                      (res * beat_idx) + 0.2)

    pm.instruments.append(drum_inst)
    return pm
Exemple #3
0
    def melo_to_midi(melo, tonic='C', unit=0.125):
        # ins = Instrument(program=0)
        # cursor = 0
        # for i in melo:
        #     root = major_map_backward[i]
        #     if root != -1:
        #         pitch = root_to_pitch[root]
        #         ins.notes.append(Note(start=cursor, end=cursor + 0.125, pitch=pitch, velocity=60))
        #     cursor += 0.125
        ins = Instrument(program=0)
        current_pitch = MIDILoader.__melo_number_to_pitch(melo[0])
        start = 0
        for i in range(len(melo)):
            if i == len(melo) - 1:
                note = Note(pitch=current_pitch, velocity=80, start=start * unit, end=(i + 1) * 0.125)
                ins.notes.append(note)
                break
            if melo[i + 1] != melo[i]:
                if current_pitch is not 0:
                    note = Note(pitch=current_pitch, velocity=80, start=start * unit, end=(i + 1) * 0.125)
                    ins.notes.append(note)
                current_pitch = MIDILoader.__melo_number_to_pitch(melo[i + 1])
                start = i + 1

        return ins
Exemple #4
0
def listen_pitches(midi_pitch: list, time, instrument=0):
    midi = PrettyMIDI()
    ins = Instrument(instrument)
    for i in midi_pitch:
        ins.notes.append(Note(pitch=i, start=0, end=time, velocity=80))
    midi.instruments.append(ins)
    listen(midi)
Exemple #5
0
    def write_midi(self,
                   score_events,
                   midi_program,
                   pitch_low_passband=0,
                   pitch_high_passband=127,
                   time_offset=0.0):
        """
        Given a sequence of NoteEvents, calculate the MIDI ticks for each note and write these to a MIDI file.

        PARAMETERS:
            score_events (list): list of ScoreEvents
            midi_program (int): MIDI program to use for the MIDI track
            pitch_low_passband (int): discard any pitches with midi numbers less than this bound
            pitch_high_passband (int): discard any pitches with midi numbers greater than this bound
            time_offset (float): amount of time (s) to delay MIDI note events
        """

        midi = PrettyMIDI(resolution=MidiIO.DEFAULT_PPQ,
                          initial_tempo=MidiIO.DEFAULT_TEMPO)
        instr = Instrument(program=midi_program)
        for e in score_events:
            for n in e.underlying_events():
                if pitch_low_passband <= n.midi_number <= pitch_high_passband:
                    note = MidiNote(velocity=MidiIO.DEFAULT_VELOCITY,
                                    pitch=n.midi_number,
                                    start=(n.onset_ts + time_offset),
                                    end=(n.offset_ts + time_offset))
                    instr.notes.append(note)
        midi.instruments.append(instr)
        midi.remove_invalid_notes()

        directory = os.path.split(self._path)[0]
        if not os.path.exists(directory):
            os.makedirs(directory)
        midi.write(self._path)
Exemple #6
0
 def to_midi(self, program=DEFAULT_SAVING_PROGRAM,
             resolution=DEFAULT_RESOLUTION, tempo=DEFAULT_TEMPO):
     midi = PrettyMIDI(resolution=resolution, initial_tempo=tempo)
     inst = Instrument(program, False, 'NoteSeq')
     inst.notes = copy.deepcopy(self.notes)
     midi.instruments.append(inst)
     return midi
 def __construct_midi(self):
     final_progression_list = []
     for i in range(len(self.progression_lib_filtered)):
         if len(self.progression_lib_filtered[i]) == 0:
             final_progression_list.append(
                 max(self.progression_lib[i], key=lambda x: x.reliability))
         else:
             final_progression_list.append(
                 random.choice(self.progression_lib_filtered[i]))
     ins = Instrument(program=0)
     note_list = []
     shift_count = 0
     log = []
     for progression in final_progression_list:
         log.append(self.__info(progression))
         temp_midi = progression.to_midi(lib=self.midi_lib,
                                         tempo=self.meta['tempo'],
                                         tonic=self.meta['tonic'])
         temp_midi = midi_shift(temp_midi,
                                shift=shift_count + self.note_shift,
                                tempo=self.meta['tempo'])
         note_list += temp_midi.instruments[0].notes
         shift_count += len(progression) * 2
     note_list = self.__smooth_notes(note_list)
     ins.notes += note_list
     return ins, log
def extract_drums(midi_path: str) -> Optional[PrettyMIDI]:
    """
  Extracts a PrettyMIDI instance of all the merged drum tracks
  from the given MIDI path.

  :param midi_path: the path to the MIDI file
  :return: the PrettyMIDI instance of the merged drum tracks
  """
    os.makedirs(args.path_output_dir, exist_ok=True)
    pm = PrettyMIDI(midi_path)
    pm_drums = copy.deepcopy(pm)
    pm_drums.instruments = [
        instrument for instrument in pm_drums.instruments if instrument.is_drum
    ]
    if len(pm_drums.instruments) > 1:
        # Some drum tracks are split, we can merge them
        drums = Instrument(program=0, is_drum=True)
        for instrument in pm_drums.instruments:
            for note in instrument.notes:
                drums.notes.append(note)
        pm_drums.instruments = [drums]
    if len(pm_drums.instruments) != 1:
        raise Exception(f"Invalid number of drums {midi_path}: "
                        f"{len(pm_drums.instruments)}")
    return pm_drums
Exemple #9
0
def extract_pianos(msd_id: str) -> List[PrettyMIDI]:
    """
  Extracts a list of PrettyMIDI instance of all the separate piano tracks
  from the given MSD id.

  :param msd_id: the MSD id
  :return: the list of PrettyMIDI instances of the separate piano tracks
  """
    os.makedirs(args.path_output_dir, exist_ok=True)
    midi_md5 = get_matched_midi_md5(msd_id, MSD_SCORE_MATCHES)
    midi_path = get_midi_path(msd_id, midi_md5, args.path_dataset_dir)
    pm = PrettyMIDI(midi_path)
    pm.instruments = [
        instrument for instrument in pm.instruments
        if instrument.program in PIANO_PROGRAMS and not instrument.is_drum
    ]
    pm_pianos = []
    if len(pm.instruments) > 1:
        for piano_instrument in pm.instruments:
            pm_piano = copy.deepcopy(pm)
            pm_piano_instrument = Instrument(program=piano_instrument.program)
            pm_piano.instruments = [pm_piano_instrument]
            for note in piano_instrument.notes:
                pm_piano_instrument.notes.append(note)
            pm_pianos.append(pm_piano)
    else:
        pm_pianos.append(pm)
    for index, pm_piano in enumerate(pm_pianos):
        if len(pm_piano.instruments) != 1:
            raise Exception(f"Invalid number of piano {msd_id}: "
                            f"{len(pm_piano.instruments)}")
        if pm_piano.get_end_time() > 1000:
            raise Exception(f"Piano track too long {msd_id}: "
                            f"{pm_piano.get_end_time()}")
    return pm_pianos
Exemple #10
0
 def convert2midi(self):
     midi = PrettyMIDI(resolution=STATE_RESOLUTION,
                       initial_tempo=STATE_TEMP)
     inst = Instrument(1, False, 'Note_Seqce')
     inst.notes = copy.deepcopy(self.notes)
     midi.instruments.append(inst)
     return midi
def extract_drums(msd_id: str) -> Optional[PrettyMIDI]:
  """
  Extracts a PrettyMIDI instance of all the merged drum tracks
  from the given MSD id.

  :param msd_id: the MSD id
  :return: the PrettyMIDI instance of the merged drum tracks
  """
  os.makedirs(args.path_output_dir, exist_ok=True)
  midi_md5 = get_matched_midi_md5(msd_id, MSD_SCORE_MATCHES)
  midi_path = get_midi_path(msd_id, midi_md5, args.path_dataset_dir)
  pm = PrettyMIDI(midi_path)
  pm_drums = copy.deepcopy(pm)
  pm_drums.instruments = [instrument for instrument in pm_drums.instruments
                          if instrument.is_drum]
  if len(pm_drums.instruments) > 1:
    # Some drum tracks are split, we can merge them
    drums = Instrument(program=0, is_drum=True)
    for instrument in pm_drums.instruments:
      for note in instrument.notes:
        drums.notes.append(note)
    pm_drums.instruments = [drums]
  if len(pm_drums.instruments) != 1:
    raise Exception(f"Invalid number of drums {msd_id}: "
                    f"{len(pm_drums.instruments)}")
  return pm_drums
Exemple #12
0
def array_to_pm(array,
                fs=DEFAULT_FS,
                velocity=DEFAULT_VELOCITY,
                pitch_range=DEFAULT_PITCH_RANGE):

    pm = PrettyMIDI()
    inst = Instrument(1)
    pm.instruments.append(inst)

    last_notes = {}
    last_state = np.zeros(array.shape[1]) > 0.5

    for i, step in enumerate(array):
        now = i / fs
        step = step > 0.5
        changed = step != last_state
        for pitch in np.where(changed)[0]:
            if step[pitch]:
                last_notes[pitch] = Note(velocity, pitch + pitch_range.start,
                                         now, None)
                inst.notes.append(last_notes[pitch])
            else:
                last_notes[pitch].end = now
                del last_notes[pitch]
        last_state = step

    now = (i + 1) / fs
    for note in last_notes.values():
        note.end = now

    return pm
Exemple #13
0
def to_pretty_midi_instrument(track: Track) -> Instrument:
    """Return a Track object as a pretty_midi Instrument object."""
    instrument = Instrument(program=track.program,
                            is_drum=track.is_drum,
                            name=track.name)
    for note in track.notes:
        instrument.notes.append(to_pretty_midi_note(note))
    return instrument
Exemple #14
0
def nmat2ins(nmat, program=0, tempo=120, sixteenth_notes_in_bar=16) -> Instrument:
    ins = Instrument(program=program)
    snib = sixteenth_notes_in_bar
    unit_length = (60 / tempo) / 4
    for note in nmat:
        midi_note = Note(pitch=note[0], velocity=note[1],
                         start=(note[2][0] - 1) * unit_length * snib + note[2][1] * unit_length,
                         end=(note[3][0] - 1) * unit_length * snib + note[3][1] * unit_length + 0.05)
        ins.notes.append(midi_note)
    return ins
Exemple #15
0
def pitch_lists_to_midi_file(pitch_lists, midi_path):
    midi = PrettyMIDI()
    ins = Instrument(0)
    cursor = 0
    unit_length = 0.125
    for pitch_list in pitch_lists:
        for pitch in pitch_list:
            if pitch != 0:
                ins.notes.append(Note(start=cursor, end=cursor + unit_length, pitch=pitch, velocity=60))
            cursor += unit_length
    midi.instruments.append(ins)
    midi.write(midi_path)
Exemple #16
0
def write_midi(note_sequence, output_dir, filename):

    #make output directory
    pathlib.Path(output_dir).mkdir(parents=True, exist_ok=True)

    #generate midi
    midi = PrettyMIDI()
    piano_track = Instrument(program=0, is_drum=False, name=filename)
    piano_track.notes = note_sequence
    midi.instruments.append(piano_track)
    output_name = output_dir + f"{filename}.midi"
    midi.write(output_name)
def piano_roll2midi(piano_roll, fs=SAMPLE_RATE):

    frames, notes = piano_roll.shape
    pm = PrettyMIDI()
    instrument_program = instrument_name_to_program('Acoustic Grand Piano')
    instrument = Instrument(program=instrument_program)

    piano_roll = np.pad(piano_roll, [(0, 0), (1, 1)], 'constant')
    velocity_changes = np.nonzero(np.diff(piano_roll))

    print(velocity_changes)
    print(*velocity_changes[0])
    print(np.array(velocity_changes).shape)

    prev_velocities = np.zeros(notes, dtype=int)
    note_on_time = np.zeros(notes)
 def __load_pop909_melo(self):
     pop909_loader = MIDILoader(files='POP909')
     pitch_list = pop909_loader.get_full_midi_ins_from_pop909(index=self.midi_path, change_key_to='C')
     ins = Instrument(program=0)
     current_pitch = pitch_list[0]
     start = 0
     for i in range(len(pitch_list)):
         if i == len(pitch_list) - 1:
             note = Note(pitch=current_pitch, velocity=80, start=start * 0.125, end=(i + 1) * 0.125)
             ins.notes.append(note)
             break
         if pitch_list[i + 1] != pitch_list[i]:
             if current_pitch != 0:
                 note = Note(pitch=current_pitch, velocity=80, start=start * 0.125, end=(i + 1) * 0.125)
                 ins.notes.append(note)
             current_pitch = pitch_list[i + 1]
             start = i + 1
     return ins
Exemple #19
0
 def tokens2midi(self, tokens):
     """
     Игнорирование токенов педали
     :param tokens:
     :return:
     """
     midi = PrettyMIDI()
     program = pretty_midi.instrument_name_to_program('Acoustic Grand Piano')
     piano = Instrument(program=program)
     velocity = 0
     t = 0
     pitch2start = {}
     pitch2end = {}
     pitch2velocity = {}
     for token in tokens:
         if token.startswith("PEDAL"):
             continue
         value = int(token.split("_")[-1])
         if token.startswith("SET_VELOCITY"):
             velocity = value
         if token.startswith("TIME_SHIFT"):
             t += value
             pitch2end = {k: v + value for k, v in pitch2end.items()}
         if token.startswith("NOTE_ON"):
             pitch2start[value] = t
             pitch2end[value] = t
             pitch2velocity[value] = velocity
         if token.startswith("NOTE_OFF"):
             if value in pitch2start:
                 start = pitch2start.pop(value)
                 end = pitch2end.pop(value)
                 if end > start:
                     note = Note(
                         velocity=self._bin2velocity(pitch2velocity.pop(value)),
                         pitch=value,
                         start=start / 1000,
                         end=end / 1000
                     )
                     piano.notes.append(note)
     midi.instruments.append(piano)
     return midi
Exemple #20
0
def save_cleaned_instrument(instrument: Instrument):
    """
		Save the instrument as a standalone midi file in the cleaned directory
		This function also removes long pauses in the new tracks to compensate for instruments that only play in a small part of the original song
	"""
    try:
        # Create dummy
        midi = pretty_midi.PrettyMIDI(initial_tempo=80)
        midi.instruments.append(Instrument(program=instrument.program))

        # Adjust timing: Removes all pauses longer than 1 second
        max_pause = 1  # in seconds (float)
        original_notes: List[pretty_midi.Note] = pm.instruments[0].notes
        original_times = []
        new_times = []
        last_end = 0
        shift = 0
        for note in original_notes:
            original_times.append(note.start)

            # Check timings
            if note.start > last_end + max_pause:
                # Pause is too long! adjust timing by shifting left
                diff = note.start - last_end
                shift += diff - max_pause

            new_times.append(note.start - shift)
            last_end = note.end

        # Synthesize
        midi.adjust_times(original_times, new_times)
        midi.synthesize()

        # Save
        filename = f"file{file_id}-ins{i}-notes{len(midi.instruments[0].notes)}-pitch{len(midi.instruments[0].pitch_bends)}-controls{len(midi.instruments[0].control_changes)}"
        print(f"\t\tSaving instrument: {filename}")
        midi.write(
            f"{output_dir}/{filename}-ori_len{int(last_end)}-new_len{int(last_end-shift)}.mid"
        )
    except Exception as e:
        print(f"Exception while cleaning instrument: {str(e)}")
Exemple #21
0
def dump_midi(data, note_sets, path):
    midi_file = PrettyMIDI(resolution=220, initial_tempo=120)
    track = Instrument(0)
    time = 0

    # Shift first timing to 0
    #time -= note_sets['timing'][data[0][0]] * 30

    for note in data:
        # <padding> == 0
        if note[0] == 0:
            continue
        time += note_sets['timing'][note[0]] * 15 / 120
        track.notes.append(
            Note(velocity=100,
                 start=time,
                 end=time + note_sets['duration'][note[1]] * 15 / 120,
                 pitch=note_sets['pitch'][note[2]]))
        #print(track.notes[-1])
    midi_file.instruments.append(track)
    midi_file.write(path)
Exemple #22
0
def write_to_midi(note_sequences, output_dir, n_to_write=None):

    if len(note_sequences) == 0:
        print("No note sequences to write out...")
        return
    #number of sequences to write out as MIDI files
    if n_to_write is None:
        n_to_write = len(note_sequences)
    #make the output directory if it doesn't already exist
    pathlib.Path(output_dir).mkdir(parents=True, exist_ok=True)
    for i in range(n_to_write):
        #for note_sequence in note_sequences:
        midi = PrettyMIDI(initial_tempo=80)
        piano = Instrument(program=0, is_drum=False, name="test{}".format(i))
        piano.notes = note_sequences[i]
        midi.instruments.append(piano)
        output_name = output_dir + "/test{}.midi".format(i)
        #with open(output_name, 'w')
        midi.write(output_name)
    print("Piano data successfully extracted from midis, navigate to {} to listen"\
            .format(output_dir))
Exemple #23
0
def read_one_midi_and_cut(filename, output, cut_length=16):
    data = PrettyMIDI(filename)
    notes = []
    for track in data.instruments:
        notes += track.notes
    notes = sorted(notes, key=lambda note: note.start)
    #print(notes)
    end_time = 0
    for note in reversed(notes):
        if note.end > end_time:
            end_time = note.end

    start_time = 0
    end_time = max(0, end_time - cut_length)

    import random
    cut_start_time = random.uniform(start_time, end_time)
    cut_end_time = cut_start_time + cut_length

    print('%s: start=%.1f, end=%.1f' %
          (filename, cut_start_time, cut_end_time))

    midi_length = len(notes)
    for i in reversed(range(midi_length)):
        if notes[i].start < cut_start_time or\
                notes[i].start > cut_end_time:
            del notes[i]
        elif notes[i].end > cut_end_time:
            notes[i].end = cut_end_time
    for note in notes:
        note.start -= cut_start_time
        note.end -= cut_start_time

    midi_file = PrettyMIDI(resolution=220, initial_tempo=120)
    track = Instrument(0)
    track.notes = notes
    midi_file.instruments.append(track)
    midi_file.write(output)
def create_midi(title,
                data,
                instrument_name='Acoustic Grand Piano',
                treshold=0.6):
    # Create a PrettyMIDI object
    song = PrettyMIDI()
    # Create an Instrument instance for a cello instrument
    instrument_program = instrument_name_to_program(instrument_name)
    instrument = Instrument(program=instrument_program)
    # Iterate over all note probabilities
    for sec in range(len(data)):
        # Iterate over all notes
        for note_number in range(NOTE_RANGE):
            if data[note_number] > treshold:
                # Create a Note instance for this note, starting at 0s and ending at .5s
                note = Note(velocity=100, pitch=note_number, start=0, end=.5)
                # Add it to our cello instrument
                instrument.notes.append(note)

    # Add the cello instrument to the PrettyMIDI object
    title.instruments.append(instrument)
    # Write out the MIDI data
    title.write('{}.mid'.format(title))
Exemple #25
0
 def to_wave(self,
             instrument,
             font=None,
             stereo=False,
             rate=44100,
             mono_dim2=False,
             clip=True):
     # find default soundfont if needed
     if font is None: font = default_path('TimGM6mb.sf2')
     assert 0 <= instrument and instrument < 128
     # 1.create midi file
     from pretty_midi import PrettyMIDI, Instrument, Note
     midi = PrettyMIDI(resolution=960, initial_tempo=self.tempo)
     inst = Instrument(instrument)
     reso = 60 / self.tempo * 4 / self.beat
     for i, ns in enumerate(self.notes):
         for n in ns:
             inst.notes.append(
                 Note(velocity=100,
                      pitch=n,
                      start=i * reso,
                      end=i * reso + reso))
     midi.instruments.append(inst)
     midi.write('temp.mid')
     # 2.create wave file
     from midi2audio import FluidSynth
     fs = FluidSynth(font, sample_rate=rate)
     fs.midi_to_audio('temp.mid', 'temp.wav')
     # 3.import wav file
     from scipy.io import wavfile
     _, wave = wavfile.read('temp.wav')
     # clip
     if clip:
         le = len(self.notes)
         wave = wave[:int(rate * reso * le)]
     wave = wave.astype(float) / abs(wave).max() * 0.9
     return wave
Exemple #26
0
 def tokens2midi(self, tokens: List[str]) -> PrettyMIDI:
     midi = PrettyMIDI()
     program = instrument_name_to_program('Acoustic Grand Piano')
     piano = Instrument(program=program)
     velocity = 0
     t = 0
     pitch2start = {}
     pitch2end = {}
     pitch2velocity = {}
     n_tokens = len(tokens)
     for i in range(n_tokens):
         tok_i = tokens[i]
         value = int(tok_i.split("_")[-1])
         if tok_i.startswith("SET_VELOCITY"):
             velocity = value
         elif tok_i.startswith("TIME_SHIFT"):
             t += value
             pitch2end = {k: v + value for k, v in pitch2end.items()}
         elif tok_i.startswith("NOTE_ON"):
             pitch2start[value] = t
             pitch2end[value] = t
             pitch2velocity[value] = velocity
         elif tok_i.startswith("NOTE_OFF"):
             if value in pitch2start:
                 start = pitch2start.pop(value)
                 end = pitch2end.pop(value)
                 if end > start:
                     note = Note(
                         velocity=self._bin2velocity(pitch2velocity.pop(value)),
                         pitch=value,
                         start=start / 1000,
                         end=end / 1000
                     )
                     piano.notes.append(note)
     midi.instruments.append(piano)
     return midi
Exemple #27
0
 def create_beat(
     self,
     samples: Dict[float, NDArray[Float32]] = None,
     sample_rate: int = 44100,
 ) -> AudioFile:
     """
     Create a beat from the pattern in the sequencer.
     """
     if samples is not None:
         raise ValueError(
             "Samples are not needed to create a beat for an MidiSequencer!"
         )
     mid = PrettyMIDI(initial_tempo=self.bpm)
     drum_track = Instrument(program=0, is_drum=True, name="drums")
     mid.instruments.append(drum_track)
     for row in self.pattern:
         for note in row:
             if note is not None:
                 drum_track.notes.append(note)
     return AudioFile(
         np.array(mid.fluidsynth(fs=sample_rate), dtype=np.float32),
         self.bpm,
         sample_rate,
     )
Exemple #28
0
def to_pretty_midi(
    multitrack: "Multitrack",
    default_tempo: float = None,
    default_velocity: int = DEFAULT_VELOCITY,
) -> PrettyMIDI:
    """Return a Multitrack object as a PrettyMIDI object.

    Parameters
    ----------
    default_tempo : int, default: `pypianoroll.DEFAULT_TEMPO` (120)
        Default tempo to use. If attribute `tempo` is available, use its
        first element.
    default_velocity : int, default: `pypianoroll.DEFAULT_VELOCITY` (64)
        Default velocity to assign to binarized tracks.

    Returns
    -------
    :class:`pretty_midi.PrettyMIDI`
        Converted PrettyMIDI object.

    Notes
    -----
    - Tempo changes are not supported.
    - Time signature changes are not supported.
    - The velocities of the converted piano rolls will be clipped to
      [0, 127].
    - Adjacent nonzero values of the same pitch will be considered
      a single note with their mean as its velocity.

    """
    if default_tempo is not None:
        tempo = default_tempo
    elif multitrack.tempo is not None:
        tempo = float(scipy.stats.hmean(multitrack.tempo))
    else:
        tempo = DEFAULT_TEMPO

    # Create a PrettyMIDI instance
    midi = PrettyMIDI(initial_tempo=tempo)

    # Compute length of a time step
    time_step_length = 60.0 / tempo / multitrack.resolution

    for track in multitrack.tracks:
        instrument = Instrument(
            program=track.program, is_drum=track.is_drum, name=track.name
        )
        if isinstance(track, BinaryTrack):
            processed = track.set_nonzeros(default_velocity)
        elif isinstance(track, StandardTrack):
            copied = deepcopy(track)
            processed = copied.clip()
        else:
            raise ValueError(
                f"Expect BinaryTrack or StandardTrack, but got {type(track)}."
            )
        clipped = processed.pianoroll.astype(np.uint8)
        binarized = clipped > 0
        padded = np.pad(binarized, ((1, 1), (0, 0)), "constant")
        diff = np.diff(padded.astype(np.int8), axis=0)

        positives = np.nonzero((diff > 0).T)
        pitches = positives[0]
        note_ons = positives[1]
        note_on_times = time_step_length * note_ons
        note_offs = np.nonzero((diff < 0).T)[1]
        note_off_times = time_step_length * note_offs

        for idx, pitch in enumerate(pitches):
            velocity = np.mean(clipped[note_ons[idx] : note_offs[idx], pitch])
            note = pretty_midi.Note(
                velocity=int(velocity),
                pitch=pitch,
                start=note_on_times[idx],
                end=note_off_times[idx],
            )
            instrument.notes.append(note)

        instrument.notes.sort(key=attrgetter("start"))
        midi.instruments.append(instrument)

    return midi
Exemple #29
0
def test_dataset_hook_slakh(benchmark_audio):
    # make a fake slakh directory.
    band = {"guitar": [30, 31], "drums": [127]}
    only_guitar = {"guitar": [30, 31]}
    empty = {}
    bad = {"guitar": [30], "guitar_2": [30]}
    with tempfile.TemporaryDirectory() as tmpdir:
        track_dir = os.path.join(tmpdir, "Track")
        os.mkdir(track_dir)
        # Create Metadata file
        metadata = "audio_dir: stems"
        metadata += "\nmidi_dir: MIDI"
        metadata += "\nstems:"
        metadata += "\n  S00:"
        metadata += "\n    program_num: 30"
        metadata += "\n  S01:"
        metadata += "\n    program_num: 127"
        metadata += "\n  S02:"
        metadata += "\n    program_num: 30"
        metadata += "\n  S03:"
        metadata += "\n    program_num: 30"
        metadata_path = os.path.join(track_dir, "metadata.yaml")
        metadata_file = open(metadata_path, "w")
        metadata_file.write(metadata)
        metadata_file.close()

        stems_dir = os.path.join(track_dir, "stems")
        midi_dir = os.path.join(track_dir, "MIDI")
        os.mkdir(stems_dir)
        os.mkdir(midi_dir)

        # Note: These aren't actually guitar and drums
        guitar_path1 = os.path.join(stems_dir, "S00.wav")
        guitar_path2 = os.path.join(stems_dir, "S02.wav")
        guitar_path3 = os.path.join(stems_dir, "S03.wav")
        drums_path = os.path.join(stems_dir, "S01.wav")
        mix_path = os.path.join(track_dir, "mix.wav")

        # making midi objects
        midi_0 = PrettyMIDI()
        midi_1 = PrettyMIDI()
        guitar = Instrument(30, name="guitar")
        guitar.notes = [Note(70, 59, 0, 1)]
        drum = Instrument(127, is_drum=True, name="drum")
        drum.notes = [Note(40, 30, 0, 1)]
        midi_0.instruments.append(guitar)
        midi_1.instruments.append(drum)
        midi_0.write(os.path.join(midi_dir, "S00.mid"))
        midi_1.write(os.path.join(midi_dir, "S01.mid"))
        midi_0.write(os.path.join(midi_dir, "S02.mid"))
        midi_0.write(os.path.join(midi_dir, "S03.mid"))

        midi_mix = PrettyMIDI()
        midi_mix.instruments += [guitar, drum]
        midi_mix.write(os.path.join(track_dir, "all_src.mid"))

        # Move them within directory
        shutil.copy(benchmark_audio['K0140.wav'], guitar_path1)
        shutil.copy(benchmark_audio['K0149.wav'], drums_path)
        # Make a mix from them.
        guitar_signal = nussl.AudioSignal(path_to_input_file=guitar_path1)
        drums_signal = nussl.AudioSignal(path_to_input_file=drums_path)
        guitar_signal.truncate_seconds(2)
        drums_signal.truncate_seconds(2)
        mix_signal = guitar_signal + drums_signal

        mix_signal.write_audio_to_file(mix_path)
        drums_signal.write_audio_to_file(drums_path)
        guitar_signal.write_audio_to_file(guitar_path1)
        guitar_signal.write_audio_to_file(guitar_path3)
        guitar_signal.write_audio_to_file(guitar_path2)

        # now that our fake slakh has been created, lets try some mixing
        band_slakh = nussl.datasets.Slakh(tmpdir,
                                          band,
                                          midi=True,
                                          make_submix=True,
                                          max_tracks_per_src=1)
        assert len(band_slakh) == 1
        data = band_slakh[0]
        _mix_signal, _sources = data["mix"], data["sources"]
        assert np.allclose(mix_signal.audio_data, _mix_signal.audio_data)
        assert len(_sources) == 2
        assert np.allclose(_sources["drums"].audio_data,
                           drums_signal.audio_data)
        assert np.allclose(_sources["guitar"].audio_data,
                           guitar_signal.audio_data)
        _midi_mix, _midi_sources = data["midi_mix"], data["midi_sources"]
        assert len(_midi_mix.instruments) == 2
        assert len(_midi_sources) == 2
        assert _midi_sources["guitar"][0].instruments[0].program == 30
        assert _midi_sources["drums"][0].instruments[0].program == 127

        band_slakh = nussl.datasets.Slakh(tmpdir,
                                          band,
                                          midi=True,
                                          make_submix=False)
        data = band_slakh[0]
        _mix_signal, _sources = data["mix"], data["sources"]
        assert isinstance(_sources["guitar"], list)
        assert len(_sources) == 2
        assert len(_sources["guitar"]) == 3
        assert len(_sources["drums"]) == 1
        assert np.allclose(
            sum(_sources["guitar"]).audio_data, 3 * guitar_signal.audio_data)

        with pytest.raises(DataSetException):
            not_enough_instruments = nussl.datasets.Slakh(
                tmpdir,
                band,
                midi=True,
                make_submix=True,
                min_acceptable_sources=3)

        guitar_slakh = nussl.datasets.Slakh(tmpdir,
                                            only_guitar,
                                            make_submix=True,
                                            min_acceptable_sources=1,
                                            max_tracks_per_src=1)
        data = guitar_slakh[0]
        _guitar_signal, _sources = data["mix"], data["sources"]
        assert len(_sources) == 1
        assert np.allclose(_sources["guitar"].audio_data,
                           guitar_signal.audio_data)
        assert np.allclose(_guitar_signal.audio_data, guitar_signal.audio_data)

        with pytest.raises(DataSetException):
            empty_slakh = nussl.datasets.Slakh(tmpdir,
                                               empty,
                                               min_acceptable_sources=1)

        with pytest.raises(ValueError):
            nussl.datasets.Slakh(tmpdir, band, min_acceptable_sources=0)
        with pytest.raises(ValueError):
            nussl.datasets.Slakh(tmpdir, band, max_tracks_per_src=0)
        with pytest.raises(ValueError):
            bad_slakh = nussl.datasets.Slakh(tmpdir, bad)