Pygame keyboard event too slow

571 views Asked by At

I've been trying to create a MIDI keyboard using my raspberry pi 4 and I'm trying to link my keyboard events with MIDI messages. The problem is the code runs very slowly on the raspberry pi and there is an input lag when the sound can be heard. It is not the synths problem and is Pygame's latency because I've tried running it without Pygame. Can anyone spot any major issues with the following code?

import pygame as pg
from pygame.locals import *
from sonic_pi_api import SynthStream
from keyboard import Keyboard
import sys

class Main_Menu(object):
    def __init__(self):
        pg.init()
        pg.display.set_caption('OP1 interface test')
        self.screen = pg.display.set_mode((500, 500), 32)
        self.font = pg.font.SysFont(None, 20)
        self.mainClock = pg.time.Clock()
        self.stream = SynthStream(stream_num=0)
        self.keyboard = Keyboard(self.stream)
        self.instrum_index = 0
        self.key_set = self.make_key_mapping()
        self.on_notes = set()

        self.loop()

    def draw_interface(self):
        self.screen.fill((0, 0, 255))
        self.draw_text('main menu', self.font, (255, 255, 255), self.screen, 20, 20)

        mx, my = pg.mouse.get_pos()

        button_1 = pg.Rect(50, 100, 200, 50)
        pg.draw.rect(self.screen, (255, 0, 0), button_1)

    def get_events(self):
        e = pg.event.wait()
        if e.type == pg.QUIT:
            sys.exit()
        elif e.type == pg.KEYDOWN:
            try:
                note = self.key_set[e.key]
            except KeyError:
                pass
            else:
                if note not in self.on_notes:
                    self.keyboard.key_down(note)
                    self.on_notes.add(note)
        elif e.type == pg.KEYUP:
            try:
                note = self.key_set[e.key]
            except KeyError:
                pass
            else:
                if note in self.on_notes:
                    self.keyboard.key_up(note)
                    self.on_notes.remove(note)


        # if keys[pg.K_UP]:
        #     self.keyboard.next_prog(0,pressed=True)
        # elif not keys[pg.K_UP]:
        #     self.keyboard.next_prog(0,pressed=False)
        # if keys[pg.K_DOWN]:
        #     self.keyboard.prev_prog(1, pressed=True)
        # elif not keys[pg.K_DOWN]:
        #     self.keyboard.prev_prog(1, pressed=False)

    def make_key_mapping(self):
        key_list = [
            pg.K_z,
            pg.K_s,
            pg.K_x,
            pg.K_d,
            pg.K_c,
            pg.K_v,
            pg.K_g,
            pg.K_b,
            pg.K_h,
            pg.K_n,
            pg.K_j,
            pg.K_m,
            pg.K_COMMA,
        ]
        mapping = {}
        for i in range(len(key_list)):
            mapping[key_list[i]] = (i)
        return mapping



    def loop(self):
        while True:
            self.draw_interface()
            self.get_events()
            pg.display.update()
            self.mainClock.tick(120)

    def draw_text(self, text, font, color, surface, x, y):
        textobj = font.render(text, 1, color)
        textrect = textobj.get_rect()
        textrect.topleft = (x, y)
        surface.blit(textobj, textrect)

The entire project can be found at https://github.com/Riddy21/Portable_Synthesizer

1

There are 1 answers

2
hyred- On

The way you are receiving the events only allows it to handle one event per frame. This is because you use the function pygame.event.wait() which waits until any event happens and returns given event. If you want to process every event there is every frame and not one single event every frame you can use the function pygame.event.get() which returns a tuple of every event that was not handled already.

So you can change your code from this:

def get_events(self):
    e = pg.event.wait()
    if e.type == pg.QUIT:
        sys.exit()
    elif e.type == pg.KEYDOWN:
        try:
            note = self.key_set[e.key]
        except KeyError:
            pass
        else:
            if note not in self.on_notes:
                self.keyboard.key_down(note)
                self.on_notes.add(note)
    elif e.type == pg.KEYUP:
        try:
            note = self.key_set[e.key]
        except KeyError:
            pass
        else:
            if note in self.on_notes:
                self.keyboard.key_up(note)
                self.on_notes.remove(note)

To this:

def get_events(self):
for e in pg.event.get():
    if e.type == pg.QUIT:
        sys.exit()
    elif e.type == pg.KEYDOWN:
        try:
            note = self.key_set[e.key]
        except KeyError:
            pass
        else:
            if note not in self.on_notes:
                self.keyboard.key_down(note)
                self.on_notes.add(note)
    elif e.type == pg.KEYUP:
        try:
            note = self.key_set[e.key]
        except KeyError:
            pass
        else:
            if note in self.on_notes:
                self.keyboard.key_up(note)
                self.on_notes.remove(note)