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))
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.
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.
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
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.