How to convert midi files to keypresses (in Python)?

4.9k Views Asked by At

I'm trying to read a MIDI file and then convert each note (midi number) to a simulated keypress on the keyboard (A,f,h,J,t...).

I'm able to read any MIDI file with the python-midi library like this:

pattern = midi.read_midifile("example.mid")

and I can also simulate keypresses with pywin32 like this:

shell = win32com.client.Dispatch("WScript.Shell")
shell.SendKeys(key)

But I have no idea on how to actually convert midi numbers (that are in midi files) to keypresses.

To be more precise I'm trying to convert MIDI numbers to notes and then notes to keypresses according to virtualpiano.net (61-key) so that the program would play that piano by pressing the corresponding button on the keyboard (you can press key assist in settings of the piano to see which key is what button)

Of course I would also have to wait between keypresses but that's easy enough.

Any help is appreciated. (Windows 10 64-bit (32-bit Python 2.7))

3

There are 3 best solutions below

0
On BEST ANSWER

If you take a look at the midi module that you are using, you will see that there are some constants that can be used to convert notes to their MIDI number and vice versa.

>>> import midi
>>> midi.C_0    # note C octave 0
0
>>> midi.G_3    # G octave 3
43
>>> midi.Gs_4   # G# octave 4
56
>>> midi.A_8    # A octave 8
105

>>> midi.NOTE_VALUE_MAP_SHARP[0]
C_0
>>> midi.NOTE_VALUE_MAP_SHARP[56]
Gs_4
>>> midi.NOTE_VALUE_MAP_SHARP[105]
A_8

Opening a MIDI file with read_midifile() returns a Pattern object which looks like this (taken from the examples):

>>> midi.read_midifile('example.mid')
midi.Pattern(format=1, resolution=220, tracks=\
[midi.Track(\
  [midi.NoteOnEvent(tick=0, channel=0, data=[43, 20]),
   midi.NoteOffEvent(tick=100, channel=0, data=[43, 0]),
   midi.EndOfTrackEvent(tick=1, data=[])])])

The NoteOnEvent contains timing, MIDI number/pitch and velocity which you can retrieve:

>>> on = midi.NoteOnEvent(tick=0, channel=0, data=[43, 20])
>>> on.pitch
43
>>> midi.NOTE_VALUE_MAP_SHARP[on.pitch]
'G_3'

Now all of that is interesting, but you don't really need to convert the MIDI number to a note, you just need to convert it to the keyboard key for that note as used by http://virtualpiano.net/.

Middle C is equal to MIDI 60 and this note corresponds to the 25th key on the virtualpiano keyboard which is activated by pressing the letter t. The next note, Cs_5, is MIDI 61 which is uppercase T (<shift>-t). From there you can work out the mapping for the MIDI numbers to the supported virtualpiano keys; it's this:

midi_to_vk = (
    [None]*36 +
    list('1!2@34$5%6^78*9(0qQwWeErtTyYuiIoOpPasSdDfgGhHjJklLzZxcCvVbBnm') +
    [None]*31
)

The next problem that you will face is sending the key events. Note that in MIDI multiple notes can be played simultaneously, or can overlap in time. This means that you might need to be able to send more than one key press event at the same time.

I don't think that you can handle the velocity using a computer keyboard. There is also the issue of timing, but you said that that's not a problem for you.

1
On

I understand that what you really need is a way to convert a midi note number to a note in standard notation.

Here are some elements from Wikipedia:

... GM specifies that note number 69 plays A440, which in turn fixes middle C as note number 60 ...

(from MIDI - General MIDI

Converting from midi note number (d) to frequency (f) is given by the following formula:
f = 2 (d-69)/12 * 440 Hz

(from MIDI Tuning Standard - Frequency values)

And finally from C (musical note) - Designation by octave

Scientific designation  |Octave name |    Frequency (Hz) |    Other names
C4                      | One-lined  |           261.626 |    Middle C

Image of the C4 on partition and piano keyboard from wikipedia

So C4 is midi note 69, and midi notes are separated with a semitone(*). As you have 12 semitones in one octave, you will get C5 at midi note 81, and the famous A440 (midi note 69) is A4 in scientific notation.

As an example for the table

Midi note | Scientific notation
60        | C4
62        | D4
64        | E4
65        | F4
67        | G4
69        | A4
70        | B4
71        | C5

And you would get F♯4 at midi 66...

(*) it is musically what is called equal temperament. Midi allows for finer pitch definition but it would be far beyond this answer.

1
On

Check this question first. This is general idea on how to simulate keypresses, it may look a lot but it's just a big list of keys. Then to convert midi to keyboard buttons you would create a dictionary mapping between notes and Keyboard buttons.