Leonhard Wolf
Leonhard Wolf

Reputation: 123

Pygame read MIDI input

I referenced the Pygame MIDI documentation and this code to try to get MIDI input to work. The MIDI Interface (Avid Eleven Rack) receives MIDI data from my MIDI controller just fine in my audio software (Pro Tools). Using Pygame, however, I can not seem to read any information at all.

Source Code

import pygame
from pygame.locals import *
from pygame import midi

class MidiInput():
    def __init__(self):
        # variables
        self.elevenRackInID = 2

        # init methods
        pygame.init()
        pygame.midi.init()
        self.midiInput = pygame.midi.Input(self.elevenRackInID, 100)

    def run(self):
        # print(pygame.midi.Input(3, 100))
        # for i in range(10):
            # print(pygame.midi.get_device_info(i), i)
        self.read = self.midiInput.read(100)
        # self.convert = pygame.midi.midis2events(self.read, self.elevenRackInID)
        print(self.read)

test = MidiInput()
while True:
   test.run()

The only thing printed to the console are empty square brackets:

[]

Additional Info

I just checked again: the input ID is the right one and it is in fact an input.

"self.midiInput.poll()" returns False. So according to the Pygame documentation there is no data coming in.

You can see the data, poll and device info below:

data: [] || poll: False || device info: (b'MMSystem', b'Eleven Rack', 1, 0, 1)

A list of all my MIDI devices according to Pygame (with indexes):

(b'MMSystem', b'Microsoft MIDI Mapper', 0, 1, 0) 0
(b'MMSystem', b'External', 1, 0, 0) 1
(b'MMSystem', b'Eleven Rack', 1, 0, 1) 2
(b'MMSystem', b'Maschine Mikro MK2 In', 1, 0, 0) 3
(b'MMSystem', b'Microsoft GS Wavetable Synth', 0, 1, 0) 4
(b'MMSystem', b'External', 0, 1, 0) 5
(b'MMSystem', b'Eleven Rack', 0, 1, 0) 6
(b'MMSystem', b'Maschine Mikro MK2 Out', 0, 1, 0) 7

Any help or suggestions are greatly appreciated!

Upvotes: 3

Views: 6083

Answers (2)

Jonathan Allin
Jonathan Allin

Reputation: 89

I don't believe the code posted below by Leonhard W is usable: neither the pygame.midi poll() method nor the pygame.midi read() method are blocking. The result is that CPU consumption goes through the roof (~50%).

Of course in practice the code to read the MIDI events would be run in a separate thread, though this won't help with CPU consumption.

In response to another very useful comment elsewhere, I've taken a look at the Mido library (https://mido.readthedocs.io/en/latest/index.html#). This provides blocking read methods and with just a few lines of code I can look for messages from a MIDI controller keyboard and pass them onto a MIDI synth.

import mido

names = mido.get_input_names()
print(names)
out_port = mido.open_output()

with mido.open_input(names[0]) as inport:
    for msg in inport:
        out_port.send(msg)

The only downside is that I'm getting a significant delay (perhaps 1/4s) between hitting the key and hearing the note. Oh well, onwards and upwards.

Upvotes: 2

Leonhard Wolf
Leonhard Wolf

Reputation: 123

I got an answer in another forum. It turns out that there is an example file which shows how to get the code to work. So if someone else stumbles over this problem here is the useful part of example code:

import sys
import os

import pygame as pg
import pygame.midi


def print_device_info():
    pygame.midi.init()
    _print_device_info()
    pygame.midi.quit()


def _print_device_info():
    for i in range(pygame.midi.get_count()):
        r = pygame.midi.get_device_info(i)
        (interf, name, input, output, opened) = r

        in_out = ""
        if input:
            in_out = "(input)"
        if output:
            in_out = "(output)"

        print(
            "%2i: interface :%s:, name :%s:, opened :%s:  %s"
            % (i, interf, name, opened, in_out)
        )


def input_main(device_id=None):
    pg.init()
    pg.fastevent.init()
    event_get = pg.fastevent.get
    event_post = pg.fastevent.post

    pygame.midi.init()

    _print_device_info()

    if device_id is None:
        input_id = pygame.midi.get_default_input_id()
    else:
        input_id = device_id

    print("using input_id :%s:" % input_id)
    i = pygame.midi.Input(input_id)

    pg.display.set_mode((1, 1))

    going = True
    while going:
        events = event_get()
        for e in events:
            if e.type in [pg.QUIT]:
                going = False
            if e.type in [pg.KEYDOWN]:
                going = False
            if e.type in [pygame.midi.MIDIIN]:
                print(e)

        if i.poll():
            midi_events = i.read(10)
            # convert them into pygame events.
            midi_evs = pygame.midi.midis2events(midi_events, i.device_id)

            for m_e in midi_evs:
                event_post(m_e)

    del i
    pygame.midi.quit()

You can find the file yourself in this directory: C:\Users\myUser\AppData\Roaming\Python\Python37\site-packages\pygame\examples\midi.py

Replace 'myUser' with your Win username. Also, 'Python37' can vary on the version of Python you have installed.

Upvotes: 7

Related Questions