Programmatically get available MIDI ports

Hello all,

I’m pretty new to Sonic Pi, so apologies if I’m missing something obvious here. I’ve been using VSCode with Jackson Kearl’s excellent Sonic Pi extension and loopMIDI to create virtual midi ports to connect Sonic Pi to my DAW.

What I’d like to do is programmatically get the names of the ports that Sonic Pi can see, so I can determine the input/output port numbers without opening the GUI. I’ve found a few likely-looking candidates in the Ruby Sonic Pi docs: “__resolve_midi_ports(opts)”, " “current_midi_ports”, and “available_midi_ports”.

Unfortunately I can’t get the former to work (not sure what opts it wants, and it looks like it relies on current_midi_ports anyway). If I print the results of the latter two I get ["*"] which is not particularly informative. Any suggestions?

Hello @Elenchus :slight_smile:
You’re not missing anything obvious. Those functions that you have discovered are of course part of the internal server code, not the official Sonic Pi DSL - so it’s not guaranteed that they’ll work as desired if accessed directly, as you can see!

This does sound to me like something that could go on our wishlist. I’ve been hoping for a few more ways to do things ‘programmatically’ myself, and something like this sounds fairly reasonable for a 10 year old child to understand, which is one of the main guiding principles for Sonic Pi features.

1 Like

That’s a good guiding principle, I like it.

What I’ve ended up doing is using Python to send some initial code to sonic pi via UDP - really useful for setting up basic commands, and it lets me look up the ports in Python. The hard part was finding a package that would do that - most of them seem out of date. MIDI is remarkably poorly supported in Windows generally.

Leaving some basic Python code here in case anyone has a similar use case

from time import sleep
from pythonosc import osc_message_builder, udp_client

LOCALHOST = "127.0.0.1"
PORT = 51235

class PiConn:
    def __init__(self, address = LOCALHOST, port=PORT):
        self.udp = udp_client.SimpleUDPClient(address, port)

    def send_multiple_commands(self, lst): # use when sending a code block
        command = "".join(lst)
        self.send_command(command)

    def send_command(self, command=None): # use to send a single line
        message = osc_message_builder.OscMessageBuilder(address = "/run-code")
        message.add_arg("VSCodeInit")
        if command is not None:
            message.add_arg(command)

        msg = message.build()
        self.udp.send(msg)
        sleep(0.005)
import rtmidi # NOTE: pip install python-rtmidi, NOT rtmidi

class PiMidi:
    def get_available_ports(self):
       midiout = rtmidi.MidiOut() #pylint: disable=no-member
       return midiout.get_ports()
2 Likes