Search code examples
pythonkeyboardpygamemidipiano

Reading piano notes on Python


I'd like to listen to the port having my midi output device (a piano) with my RPi, running on Debian. I've looked into pygame.midi, I managed to listen to the port, but somehow can not extract all midi information. Please find code below [edited code snippet]

EDIT: Fixed, thanks a lot!


Solution

  • First of all you need to find out which device-id your keyboard has inside pygame. I wrote this little function to find out:

    import pygame.midi
    
    def print_devices():
        for n in range(pygame.midi.get_count()):
            print (n,pygame.midi.get_device_info(n))
    
    if __name__ == '__main__':
        pygame.midi.init()
        print_devices()
    

    It looks something like this:

    (0, ('MMSystem', 'Microsoft MIDI Mapper', 0, 1, 0))
    (1, ('MMSystem', '6- Saffire 6USB', 1, 0, 0))
    (2, ('MMSystem', 'MK-249C USB MIDI keyboard', 1, 0, 0))
    (3, ('MMSystem', 'Microsoft GS Wavetable Synth', 0, 1, 0))
    

    From the pygame manual you can learn that the first One inside this info-tuple determines this device as a suitable Input-Device. So let's read some data from it in an endless-loop:

    def readInput(input_device):
        while True:
            if input_device.poll():
                event = input_device.read(1)
                print (event)
    
    if __name__ == '__main__':
        pygame.midi.init()
        my_input = pygame.midi.Input(2) #only in my case the id is 2
        readInput(my_input)
    

    That shows:

    [[[144, 24, 120, 0], 1321]]
    

    that we have a list of a list with 2 items:

    • A list of midi-data and
    • a timestamp

    The second value is the one you're interested in. So we print it out as a note:

    def number_to_note(number):
        notes = ['c', 'c#', 'd', 'd#', 'e', 'f', 'f#', 'g', 'g#', 'a', 'a#', 'b']
        return notes[number%12]
    
    def readInput(input_device):
        while True:
            if input_device.poll():
                event = input_device.read(1)[0]
                data = event[0]
                timestamp = event[1]
                note_number = data[1]
                velocity = data[2]
                print (number_to_note(note_number), velocity)
    

    I hope this helped. It's my first answer, I hope it's not too long. :)