Pianoroll matrix to MIDI conversion

101 Views Asked by At

For my project, I have taken midi files, and with the midi2dat function of SoundForge converted them to a matrix with 88 columns, 88 being the number of possible notes. The values in the columns are either 0 or the velocity of the note played. The sampling rate is 20Hz, meaning that the time interval between 2 rows is 50 milliseconds. After doing some modifications etc, I want to have a way to convert the pianoroll back into a MIDI file that I can listen at. What is the most efficient method to do so? Also, in terms of the Metainformation such as tempo etc, I would just copy the one from my initial midi file prior to the modifications made.

I tried following idea but the output mid file isn't even playable even if I put normal time.

import mido
import numpy as np
midi_file = mido.MidiFile(type=0)


pianoroll = np.genfromtxt('dataset/1.csv', delimiter=' ', dtype=int)
print(pianoroll)

track = mido.MidiTrack()
midi_file.tracks.append(track)

for i in range(pianoroll.shape[0]):
    for j in range(pianoroll.shape[1]):
        
        if pianoroll[i,j] > 0:
            note_on = mido.Message('note_on', note=j, velocity=int(pianoroll[i,j]), time=int(???/20))
            track.append(note_on)
        
        # Change maybe to a note on event looking at prior column and checking if nonzero entry etc
        if j < pianoroll.shape[1] - 1 and pianoroll[i,j+1] == 0:
            note_off = mido.Message('note_off', note=j, velocity=0, time=int(??/20))
            track.append(note_off)


midi_file.save('output.mid')
1

There are 1 best solutions below

0
On

One issue with your code is that the time attribute of the note_on and note_off messages is not being set correctly. The time attribute represents the delta time in ticks since the last message, not the absolute time in seconds. You can calculate the delta time by keeping track of the time of the last message and subtracting it from the current time.

Here’s an example that shows how to convert a piano roll representation back into a MIDI file:

import mido
import numpy as np

# Load the piano roll from a CSV file
pianoroll = np.genfromtxt('dataset/1.csv', delimiter=' ', dtype=int)

# Create a new MIDI file
midi_file = mido.MidiFile(type=0)
track = mido.MidiTrack()
midi_file.tracks.append(track)

# Set the ticks per beat (this should match the original MIDI file)
ticks_per_beat = 480
midi_file.ticks_per_beat = ticks_per_beat

# Set the tempo (this should match the original MIDI file)
tempo = 500000
track.append(mido.MetaMessage('set_tempo', tempo=tempo))

# Set the sampling rate (this should match the piano roll)
sampling_rate = 20

# Initialize some variables
last_time = 0
current_time = 0

# Iterate over the rows of the piano roll
for row in pianoroll:
    # Increment the current time
    current_time += mido.second2tick(1 / sampling_rate, ticks_per_beat, tempo)

    # Iterate over the columns of the piano roll
    for note, velocity in enumerate(row):
        # Check if a note is played
        if velocity > 0:
            # Calculate the delta time
            delta_time = int(current_time - last_time)

            # Create a note on message
            note_on = mido.Message('note_on', note=note, velocity=int(velocity), time=delta_time)
            track.append(note_on)

            # Update the last time
            last_time = current_time

        # Check if a note is released
        if note < pianoroll.shape[1] - 1 and row[note + 1] == 0:
            # Calculate the delta time
            delta_time = int(current_time - last_time)

            # Create a note off message
            note_off = mido.Message('note_off', note=note, velocity=0, time=delta_time)
            track.append(note_off)

            # Update the last time
            last_time = current_time

# Save the MIDI file
midi_file.save('output.mid')