Reading MIDI input from MDI Keyboard

Hi, I need some pointers please!

I have a StudioLogic TMK88 MIDI keyboard and an iRig Pro USB Interface and would like to be able to read the input from this in Blender using, initially a script node in Animation Nodes (AN) ultimately I want to write my own Realtime MIDI AN node to go with the ones I have already developed to read MIDI files.

What I cannot get started with is how to get the MIDI signal from my keyboard into a variable, so the code line:

variable = [input from my keyboard]

I am confident I can work it from this position, once I have the input as a variable.

I have looked on the web and found pointers to arecordmidi - this is Linux only and I want it to be cross-platform. I have also found a reference to using pygame, but don’t really understand how to get started with this, having never done anything with any game engine.

So, can anyone get me started on this? All help gratefully received! Once it is working I will add this to my GitHub pages for anyone to use.

Cheers, Clock. :beers:

PS. Happy New Year to everyone!

OK I am getting somewhere:

I installed Python 3.7.2 and then did a

python3 -m pip install -U pygame --user

Then I wrote the little script you see in my Atom window and ran it, then pressed keys on my TMK88 and Hey Presto! you see the output from my script in the terminal window.

However, when I tried to do this in Blender, I got this error straight away:

Does this mean that pygame is not installed for Blender’s version on Python (he said purely guessing) and how do I correct that?

So near, yet so far… Thanks for the help I am sure you are going to offer!

Cheers, Clock. :beers:

Merde! So I tried copying the pygame folder from python 3.7 to Blender’s python site-packages and that didn’t work:

Now I get this error:

I give up for today!!!

Cheers, Clock.

PS. Why is the forum trying to be a smart-arse and suggesting I PM myself…

This is getting boring!!!

I found a snippet on the 'net that suggested I do this:

Didn’t bloody work - grrr, anybody got any suggestions please? This works perfectly well with an external python script BTW, just won’t work in Blender.

Cheers, Clock. :beers:

This is frustrating, I tried Mido, but it needs rtmidi, so when I try to instal this in Blender’s Python structure I get this:

clang -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -arch i386 -arch x86_64 -D__MACOSX_CORE__= -I/Users/mac/Documents/Blender/blender-2.79-rc2-macOS-10.6/blender.app/Contents/Resources/2.79/python/include/python3.5m -c cpp_src/PyMidiMessage.cpp -o build/temp.macosx-10.6-intel-3.5/cpp_src/PyMidiMessage.o -Wno-missing-braces
cpp_src/PyMidiMessage.cpp:22:10: fatal error: ‘Python.h’ file not found
#include <Python.h>
^~~~~~~~~~
1 error generated.
error: command ‘clang’ failed with exit status 1

In other words, where the **** should this file be for Blender to cope with installing a simple module?

Cheers, Clock. :beers:

PS. is there really nobody out there who understands this stuff?

Look on his Git page. It should help you with midi stuffs in blender.
http://www.jpfep.net/pages/addmidi/
Good luck

Thank you very much!! I will get on it tomorrow, once I get back from my day’s flying.

Cheers, Clock.

Cracked it!

Loaded pip into the Blender python bin directory, then got the MacOS 3.5 pygame.whl file, then loaded it with local pip from the Blender python bin directory (note the ./pip to stop it using system pip):

./pip install blah-blah-blah.whl

You cannot use something like ./pip install pygame - doesn’t work. Pygame works now, so we can get on with writing the realtime MIDI interface for AN. I haven’t looked at the add-on yet, on my todo list for later.

I have written the first node to initialise pygame.midi and got it to talk to my iRig, just need to connect my TMK88 keyboard when I get home from the airfield:

25

Cheers, Clock. :beers:

Here is our current progress:

Me playing basic C chord on my TMK88 and the three keys down in Blender, full screen below:

I can decide whether to use MIDI velocity. or not and am running at 0.001 seconds Interval Time for AN.

Just thought I would update this thread to show where we are at just now. A big thank-you to my new French friend helping me with this work. Yes it runs real-time and yes it is very fast…

Cheers, Ever-So-Happy-Clock. :beers: :smiley:

Hi,
I am on windows 7 x64 and Visual Studio 2019 and I am a complete zero in music, but your project is exactly what I’ would like to do in python. I have a Ableton Q49 for the beginning and try to connect to that device by python midi. I do that for fun and for i am beginning to be interested in music and sound theory.

First pygame was completely uninstallable throug pip and through conda. But suddenly visual Studio installed it. I don’t know why this was suddenly possible. It is a miracle :slight_smile: Pygame-Blender-Installation-VisualStudio2019.

So now I could begin to read some of your code snippets or try to understand how you constructed your very fine tool - if you would allow that?

Best regards and thanks for your work (and possible help)
Axel Arnold Bangert

You’re welcome to use the code, it’s under GNU license so you MUST quote me as co-author to anything you write yourself, if it contains my code. This is not too much to ask I hope!

The GitHub repository for my project as it stands now (I have switched to my own node system in Blender) is here:

The other stuff to work with Animation Nodes in Blender may be helpful also, but it is restricted to AN modes of operation, that can be found here:

Just follow the path nodes/midi - bear in mind these have not been developed lately and are probably out of date.

Good to hear you have now got PyGame installed, have you found this:

https://www.pygame.org/docs/

There is some good stuff in there also.

Let me know how you get on and if you need any more help.

Cheers, Clock.

Hi clockmender,
thank you very much for your help. I have a idiotic :slight_smile: problem. How do you handle that double-sound which seems to come frome note_off?
:
player.note_on(note_number, 127)
pygame.time.wait(100)
player.note_off(note_number,127)

Best thanks
Axel

I think a note_off should be:

player.note_off(note_number, 0)

Just off out for a while, I’ll check properly later, but I think note_off should be 0 velocity, not 127…

Hi clockmender,
thank you for your reply.What you said is absolutely plausible, but there is no change.

I tried to bypass that problem by making some Loopback ports for MIDI with loopMIDI
from Tobias Erichson. So I could play the sound about Ableton Live like that in the
pic here: MultipleMIDI-Ports-Erichson
I tried all posiible active/inactive combinations in these ports.

But I got the problem, that I recieve no data in input(2) from input(1). I asked Erichson.
Perhaps I get an answer then I’ll post it here.
Best thanks
Axel

Hi clockmender,
I got no answer from Erichson but I tried to handle it by the seperated events KEYDOWN and KEYUP.

In case of piano (instrument(0)) the short double intonation sound is played and the note ends by default even if I comment note_off completely out.

In case of hamond (instrument(19)) the short double intonation sound is played and is the note is never ended if I comment note_off completely out.

So the double intonation sound comes from both note_on and (if activated) from note_off too - in the picture here you see the doubling for note_on if I extremely shortly hit the key even when I use output.write it is doubling : PygameDoublePlayingNoteOn-2020.

import pygame
import pygame.midi
import time

def print_devices():
    for n in range(pygame.midi.get_count()):
        print (n,pygame.midi.get_device_info(n))

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():
            if pygame.KEYDOWN:
                event = input_device.read(1)[0]
                data = event[0]
                timestamp = event[1]
                note_number = data[1]
                #print(note_number)
                velocity = data[2]
                channel = data[3]
                #if note_number == 60:
                    #print ("Middle C", number_to_note(note_number), note_number)
                #print ("channel",channel, number_to_note(note_number), note_number, velocity)
                #print ('Playing...')
                #player.note_on(note_number, velocity)
                test = [[[0xc0, 0, 0], 20000], [[0x90, 60, 100], 20500]]
                pygame.midi.Output(5,0).write(test)
                note_number = test[1][0][1]
                velocity = test[1][0][2]
                print(number_to_note(note_number),velocity, "Rohwert=", test)
            #if pygame.KEYUP:
                #pygame.time.wait(100)
                #player.note_off(note_number,0)
                #print ('Played')
            
if __name__ == '__main__':
    pygame.midi.init()
    print_devices()
    my_input = pygame.midi.Input(1) 
    #player = pygame.midi.Output(0,0)
    #player.set_instrument(19)
    readInput(my_input)

For virtual bus connection to Ableton live I tried that code snippet:
pygame.midi.Output.write([[[0xc0, 0, 0], 20000], [[0x90, 60, 100], 20500]])
It works (it not had to be set to remote -as said from Ableton - but to track) .There are now sent Data (always 10 Bytes for the test Dataset), and now Ableton plays the note, but twice too :slight_smile: :slight_smile: :slight_smile: PygameDoublePlayingWriteAbleton-2020

For even with

test = [[[0xc0, 0, 0], 20000], [[0x90, 60, 100], 20500]]
pygame.midi.Output(5,0).write(test)

it prints two times (see the adde picture in the cmd console) I guess that input_device.poll() reacts on two Key_send events in C++ source. I’m extremely bad in C++ :slight_smile: .

 def poll(self):
        """returns true if there's data, or false if not.
        Input.poll(): return Bool

        raises a MidiException on error.
        """
        _check_init()
        self._check_open()

        r = self._input.Poll()
        if r == _pypm.TRUE:
            return True
        elif r == _pypm.FALSE:
            return False
        else:
            err_text = GetErrorText(r)
            raise MidiException( (r, err_text) )

Perhaps there is a possibility to block the KEYUP by using :

pygame.init() 
pygame.event.set_blocked(pygame.KEYUP)

Not working … :joy: So it cannot depend on the key-events. The poll method seems to dig it out twice on keyup and keydown but without respect to the event it simply grabs the data from the stream.

pygame.event.clear() clears all so only one note is played correctly.
Best
Axel

That’s good to hear you are getting it sorted! I had looked at the API and was about to ask you to post your full code. I do not instigate the sound through PyGame in Blender, so it is not exactly what I have done.

Just one thing, when you post code here, put three back ticks (`) and the word “python” on the first line, then paste your code on the next line, then add a further line with three back ticks, it then looks like this:

import pygame.midi
import time

def print_devices():
    for n in range(pygame.midi.get_count()):
    print (n,pygame.midi.get_device_info(n))

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

if __name__ == __‘main__’:
    pygame.midi.init()

This make it easier for us to read and help to debug if we have to.

Cheers, Clock.

Hi Clock,
I found the solution :slight_smile: I found a program that works as expected about pygame to Ableton Live and directly from VisualStudio Q49 too. The miracle solution seems to be a redirect of midi-events to pygame-events, which seems to be done by midis2events.

Here is a screenshot. PygamePlayingNoteOnAbleton3-2020

It plays like expected … no doubling :slight_smile: :slight_smile: :). I still have to analyse the code. You find it in github.com/bgr/midistuff

I thought I would be blocked because of something - all is fine - :slight_smile:

The old method had been that here. But it does not work anymore because - filter - has been depreciated from the class MidiEvents.

raw_ons = midi_events.filter(lambda ev: ev.command == midis2events.NOTE_ON)
raw_offs = midi_events.filter(lambda ev: ev.command == midis2events.NOTE_OFF)

I got all running and there is a relative simple way to add instantanious notesheet-production on the fly with mingus and lilypond. It seems to work very fine and reliable with several formats.

Best
Axel

What do you mean by “Blocked” If you are new you have to be moderated for the first 10 posts and can only post so many images, or files, maybe one of the mods could look at this…

Cheers, Clock.