I am trying to create a program that will
I've seen plenty of examples of being able to do the above separately, but am stuck on how to combine them all in one. I have not been able to find any examples of bindings to MIDI messages -- only for key and button presses.
Currently my code can open up a matplotlib
window and display first image, but the code is stuck until the window is closed. After closing window, I can see MIDI notes in streaming output but no window for images is visible.
When I tried to use show(block=False)
a window with no image would open, but I can at least see MIDI input stream. An image never shows up in window.
I'm open to trying other libraries (TKinter, PyQT5, etc) but I couldn't find any examples that they would be any different.
Here is my code so far
import matplotlib
import os, sys
import mido
import cv2
def get_images(path: str) -> list:
"""
Recursively crawl through dir and load in all images
"""
images = []
image_files = glob.glob(os.path.join(path, '*'))
print(image_files)
for i in image_files:
images.append(cv2.imread(i))
return images
def get_midi_port():
# Get a list of available input port names
input_ports = mido.get_input_names()
# Get first port that is not a Through type
for p in input_ports:
if "Midi Through" not in p:
print(p)
port = p
return p
if not input_ports or not p:
raise Exception("No MIDI input ports found.")
class MIDI_Images():
def __init__(
self,
path="."
):
self.path = path
self.loaded_images = get_images(self.path)
self.curr = 0
if auto_start:
self.fig, self.ax = plt.subplots()
self.display()
plt.show(block=False) ## opens window but no image
plt.pause(0.1)
### plt.show() ## shows image but code doesn't continue for MIDI notes
### Ingest MIDI messages
try:
p = get_midi_port()
with mido.open_input(p) as inport:
inport.poll()
print(f"Listening for MIDI messages on port: {inport.name}")
while True:
for msg in inport.iter_pending():
print(msg)
self.curr = msg.note - 24 ## lowest note on my controller is 24
self.update_image()
except Exception as e:
print(f"Error: {e}")
def update_image(self):
"""
Load in next image
"""
# advance to next image
try:
self.im.set_array(self.loaded_images[self.curr])
self.display()
## self.fig.canvas.draw_idle() ## waits for image window to close if uncommented
except IndexError:
print("Sorry no image in index: ", n)
def display(self):
"""
Orchestrating function to run
"""
image = self.loaded_images[self.curr]
self.im = self.ax.imshow(image)
### Examples of key bindings working just fine
# self.fig.canvas.mpl_connect("key_press_event", self.next_image)
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description='')
parser.add_argument('--imagedir', action="store", dest='imagedir', default='')
args = parser.parse_args()
MIDI_Images(args.imagedir)
Try executing the midi polling message loop in a child thread instead. Then you can use plt.show()
instead of plt.show(block=False)
. Also you need to call plt.draw()
after updating the image inside display()
.
Below is the required changes:
...
import threading
...
class MIDI_Images():
def __init__(
self,
path="."
):
self.path = path
self.loaded_images = get_images(self.path)
self.curr = 0
if auto_start: # note that auto_start is undefined in your code
self.fig, self.ax = plt.subplots()
self.display()
# start the polling message loop in a child thread
threading.Thread(target=self.poll_midi, daemon=1).start()
plt.show() # use blocking show()
def poll_midi(self):
try:
p = get_midi_port()
with mido.open_input(p) as inport:
inport.poll()
print(f"Listening for MIDI messages on port: {inport.name}")
while True:
for msg in inport.iter_pending():
print(msg)
self.curr = msg.note - 24
self.update_image()
except Exception as e:
print(f"Error: {e}")
def update_image(self):
"""
Load in next image
"""
# advance to next image
try:
self.im.set_array(self.loaded_images[self.curr])
self.display()
except IndexError:
print("Sorry no image in index: ", self.curr) # original is n which is undefined in your code
def display(self):
"""
Orchestrating function to run
"""
image = self.loaded_images[self.curr]
self.im = self.ax.imshow(image)
plt.draw() # update plot
...