spatialaudio / jackclient-python

🂻 JACK Audio Connection Kit (JACK) Client for Python :snake:
https://jackclient-python.readthedocs.io/
MIT License
132 stars 26 forks source link

AssertionError: -105 #54

Closed matheusfillipe closed 6 years ago

matheusfillipe commented 6 years ago

I was trying to make a experimental computer keyboard from a Midi keyboard and was using this code:


import binascii
import  os

from evdev import uinput, ecodes as e
import jack
from subprocess import Popen

Keys={
    "3c":"a",
    "3e":"s",
    "40":"d",
    "41":"f",
    "43":"g",
    "48":"a",
    "4a":"s",
    "4c":"d",
    "4d":"f",
    "4f":"g",
    "51":"DOWN",
    "52":"up",
    "53":"left",
    "54":"right",
    "54":"enter",
    "55":"backspace",
    "56":"p",
}

def press(key):
    pid=os.fork()
    if pid==0:
        with uinput.UInput() as ui:
            eval("ui.write(e.EV_KEY, e.KEY_" + key.upper() +", 1)")
            ui.syn()

setup = ['a2j', 'a2jmidid -e']
a2j = [Popen(cmd, shell=True) for cmd in setup]

client=jack.Client("VirtualControl")
inp=client.midi_inports.register("Input")

@client.set_process_callback
def process(frames):

    for offset, data in inp.incoming_midi_events():
        midihex = binascii.hexlify(data).decode()

        if(midihex!='f8'):
            pressed = midihex[0:2] =='90'
            code=midihex[2:4]
            intensity=midihex[4:6]

            if pressed:
                press(Keys[code])

            #print(midihex)

        #print('{0}: 0x{1}'.format(client.last_frame_time + offset, sbinascii.hexlify(data).decode()))

#press("b")
with client:
    print('#' * 80)
    print('press Return to quit')
    print('#' * 80)
    input()

Problem is that it crashes right after i press one or two keys. With the error:

  File "/usr/local/lib/python3.6/dist-packages/jack.py", line 1787, in incoming_midi_events
    assert not err, err
AssertionError: -105

This fork is there because i first thought it was something related with the small delay on the simulated keypress. If i just use print it works fine. What is causing this issue? The keys get inputed and then it crashes.

I noticed your code:


    for i in range(_lib.jack_midi_get_event_count(buf)):
            err = _lib.jack_midi_event_get(event, buf, i)
            # TODO: proper error handling if this ever happens:
            assert not err, err
            yield event.time, _ffi.buffer(event.buffer, event.size)

I just noticed that if I simply comment out the assert line my script works! I don't know what it does so.... Ill keep this way for now.

mgeier commented 6 years ago

Thanks for the report!

When writing this I wasn't sure if jack_midi_event_get() would ever fail when I use it like this, that's why I inserted the assert statement for the case this happens some day. And now it did!

I'm not sure why ... are you using a very small block size?

Did you try the MIDI examples that use incoming_midi_events() from the example directory https://github.com/spatialaudio/jackclient-python/tree/master/examples?

Do they also cause the AssertionError?

This fork is there because [...]

I don't really understand what problem os.fork() is supposed to solve here, can you please elaborate?

I just noticed that if I simply comment out the assert line my script works!

That's good. But could you please check what the generator generates in this case? Is it an empty buffer? Or some garbage bytes?

How many key presses generate how many MIDI events?

I think I can add proper checks for the possible error codes and stop the generator whenever an error occurs. But I'll ask at the JACK mailing list first how exactly those errors are supposed to be handled.

BTW, I think in your code you don't really have to decode() the MIDI data to str. I assume it's more straightforward just to handle the data as raw bytes, right?

matheusfillipe commented 6 years ago

Wow so many questions haha. I'm using just that code i showed with python 3.6 on ubuntu bionic. I didn't try incoming_midi_events(), I'm don't know much about jack and just wanted this hacked up keyboard translating thing. The midi_monitor example worked fine.

Nah I had no idea at all what was causing that problem, with or without the fork it was causing the issue and with that line commented now all works fine, all the keys are detected.

Yes i could just decode the bytes, i just copied the example midi-monitor and tried to get things working fast from it and noticed that issue.

You might be able too reproduce this issue with that same code.

mgeier commented 6 years ago

The midi_monitor example worked fine.

OK, that's good to know.

all the keys are detected

OK, that's good, but I'd be interested in a bit more information: Can you please replace the assert statement with print(err) and add a print() call showing the data of each MIDI event your receive?

Is every single MIDI event coming through (note that there are separate note-on and note-off events)?

Is any of the events empty?

You might be able too reproduce this issue with that same code.

I tried, but I'm getting this error:

evdev.uinput.UInputError: "/dev/uinput" cannot be opened for writing
matheusfillipe commented 6 years ago

I had the same permission error, you just have to temporally chmod 777 that file or something that will allow your user to write there. The error only happened when i hit the keys, so the problem doesnt happen for empty midi events, those happening all the time. That's why i had if(midihex!='f8') on that code.

okay i changed that jack.py part to this:


       for i in range(_lib.jack_midi_get_event_count(buf)):
            err = _lib.jack_midi_event_get(event, buf, i)
            # TODO: proper error handling if this ever happens:

             print("Error: " + str(err))
            assert not err, err
            yield event.time, _ffi.buffer(event.buffer, event.size)

It was printing 0 all the time when I run the code i showed above., but when i hit a key it crashes with:

(...)
Error: 0
From cffi callback <function Client.set_process_callback.<locals>.callback_wrapper at 0x7f5baba15158>:
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/dist-packages/jack.py", line 668, in callback_wrapper
    callback(frames)
  File "/home/matheus/.PyCharm2018.1/config/scratches/scratch_2.py", line 58, in process
    for offset, data in inp.incoming_midi_events():
  File "/usr/local/lib/python3.6/dist-packages/jack.py", line 1790, in incoming_midi_events
    assert not err, err
AssertionError: -105
Error: -105
Error: 0

Look, I had other experimental codes where i was just print or Popen on that press function, and also thinking that it could be eval, I replaced those with a string and called eval on them, and so far, only ui.write() seems to cause the problem.

mgeier commented 6 years ago

OK, I changed the permissions of /dev/uinput which gets rid of the error, but I can still not reproduce your error. I cannot really create your functionality, either. I don't even know what exactly is supposed to happen. But anyway, I don't really want to try to reproduce your code, I'd rather get some missing information from you to be able to fix my module.

Can you please change the code in jack.py like this:

        for i in range(_lib.jack_midi_get_event_count(buf)):
            err = _lib.jack_midi_event_get(event, buf, i)
            if err:
                print('Error:', err)
            data = _ffi.buffer(event.buffer, event.size)
            if not data:
                print('empty MIDI event!')
            else:
                print(binascii.hexlify(data).decode())
            yield event.time, data

... and then show for a few key-presses (and key-releases) what is printed to the terminal?

the problem doesnt happen for empty midi events, those happening all the time

What exactly do you mean by empty MIDI events? Why are those happening all the time? Shouldn't events just happen when you do something (i.e. press or release a key)?

matheusfillipe commented 6 years ago

The goal is being able to type letters from a midi keyboard (play guitar hero with a music keyboard :P)

An empty midi event is this f8 that keeps repeating every few miliseconds, I dont know why they happen all the time, maybe some sort of time counting? (I'm seeing this on the midi_monitor, i'm calling every line printed by this script a midi event)

Different events show up when i press and release a key thats correct.

I replaced the jack.py code with what you give me and run my old script again. Here's some debug info and the printed data. I had to press 9 keys before it crashes.

variables.txt output.txt

matheusfillipe commented 6 years ago

I am using a Roland UM_ONE midi to usb adapter, my OS is ubuntu 18.04 LTS 64 bits, I connected the midi using qjackctl after that program is launched. I am running the script with python 3.6.

$ lsusb | grep Roland Bus 001 Device 030: ID 0582:012a Roland Corp. UM-ONE

I don't think there's any additional information I can provide. This is just a bad error management thing case it seems and you could maybe add an try and except assertError in there so the program doesn't stop?

mgeier commented 6 years ago

Sorry for the late response.

The output you provided was very helpful.

The f8 messages are not empty messages. They are one-byte messages which are part of a "MIDI clock" signal, see https://en.wikipedia.org/wiki/MIDI_beat_clock. I guess one of your external MIDI devices is creating this clock signal.

I asked on the JACK mailing list and got a response (link is subscribers-only): http://lists.jackaudio.org/private.cgi/jack-devel-jackaudio.org/2018-July/001519.html

From the answer, I got links to the implementations in JACK1 and JACK2:

And I got the recommendation to just ignore the event if an error is happening.

I've created pull request #56, can you please check if that works for your case?

matheusfillipe commented 6 years ago

Yes Solved! Oh clock events... yeah makes a lot of sense.

Thanks for worrying about it and solving the issue!

mgeier commented 6 years ago

Great, thanks for testing!