MIDI on the server side

I’m working with a remote machine (a gorgeous Bela Pepper) and use it to route CV into my modular system from MIDI controllers that are plugged in to it. I am forever reconfiguring how this happens, and which controllers I’m plugging in that day.

To speed up workflow I set up MIDIdefs on the Bela’s language client to respond to CC and note messages etc and stick the values directly onto buses on the server.

I’m taking note of the bus numbers allocated on the Bela and creating Bus objects on my laptop client manually so that I can easily refer to them in SynthDefs and code entirely from there.

Every change in set-up requires that I change code on Bela client and carefully ensure the Bus numbers are synced on my laptop client. (I’m working on automating the remote creation of MIDI responders and bus setters but this is less than ideal).

I’m dreaming of UGens (perhaps pseudo-ugens) that let me say something akin to this inside a SynthDef:

freq = MIDINoteNumber(CHANNEL, MIDI_ID).midicps;
gate = MIDIGate(NOTE, CHANNEL, MIDI_ID);
cutoff = MIDICC(MIDI_CC, CHANNEL, MIDI_ID).midicps;

What are the reasons we have no MIDI support on the Server? As I understand it we have data come in at all sorts of different rates on the server these days with AnalogIn, DigitalIO etc for the Bela. What are the technical reasons we don’t have such nice UGens focussed on MIDI?

Has anyone else looked at smoothing this sort of workflow out? What have you done?

I think the best way to smooth out this workflow is to avoid manually handling bus numbers.

Use the Bus object to allocate bus numbers on the language client side, and use Synth argument lists to pass, those bus numbers to synths (or .asMap to connect synth parameters to buses). Then, if the setup changes, both sides automatically adapt.

hjh

1 Like

ah yes. I think I’m already doing what you’re describing though? I often use the argument lists for synths/synthdefs and mapping to appropriate busses (often neater just to refer to Bus directly with the stuff I throw together though).

But I still need to reboot my Bela, tweak a script, note down new Bus numbers and create Bus objects with them on my laptop client script each time I tweak my MIDI setup…

Am I missing something?

(I’m working with a drummer with a very fancy but poorly documented electric drum kit over the weekend and fear shaping the code so it responds sensibly to all the CCs it throws out could be quite a task as it is. In an ideal world I’d have all the code I’m hacking on in a single script and wouldn’t need to restart the system as I integrate each new input… Guess I’ll have to tell them and myself to be patient for now!)

Hi @tris

If you’re using JACK, you have a point somehow. JACKMIDI is processed in the same callback as audio data, you can see that in one of the examples here jack-example-tools/example-clients/midisine.c at main · jackaudio/jack-example-tools · GitHub


for(i = 0; i < nframes; i++) {
    if ((in_event.time == i) && (event_index < event_count)) {
        // Process MIDI event exactly at frame i
        ...
    }
    // Generate audio sample for frame i
    ramp += note_frqs[note];
    out[i] = note_on*sin(2*M_PI*ramp);
}
1 Like

Sorry about that – the problem description was a bit confusing to me.

I wonder if it’s possible to do exactly what you suggest here – that is, rereading your description, I found myself thinking, what if everything goes through your laptop and there is no language client on the Bela at all? What if everything, including MIDI CCs, goes through your laptop as a control center? Consolidating control in one place would reduce the amount of hand syncing.

You could use a shell script on the Bela to boot a server, and connect to it from your laptop using Server.remote. Maybe not even necessary to reboot the Bela server even when resetting the laptop script.

For more detailed advice… I don’t quite understand what CCs you’re processing and where they’re plugged in. If it’s CC → CV on the Bela, what is the laptop client’s involvement in that? I suspect there’s something in your setup that could be simplified (and I have a loose idea), but your current design isn’t clear to me, and I’d rather not make another useless suggestion due to lack of understanding.

Even if you really do need the Bela script, this script could use routing synths like a routing matrix, so that the laptop client doesn’t have to worry about the final output bus numbers. Edit: E.g., what if the MIDI communication is just cc0 → kr bus 0, cc1 → kr bus 1, etc? Then both the Bela and the laptop know where the cc values are living, without noting down and syncing anything. If you’ll need to allocate other buses, s.controlBusAllocator.reserve(0, 128) upon server boot. But again, I’m not clear why you have two clients manipulating the same buses, so I might be off base.

Edit edit: If you did this – mirror the state of every CC in a set of contiguous control buses – then In.kr is MIDI CC in the server.

MIDI notes in the server, btw, would require a voice manager, which is possible though more involved.

hjh

1 Like

Ok. That’s nice! I’ll block out a Bus for every CC upon boot on the Bela and shuffle readings in from MIDI responders. (I’ll do a similar set-up for notes since I mainly use them for transposition and MIDI to CV type stuff)

(I should have been clearer about why I’m doing this. I don’t enjoy the Bela UI for editing code as I don’t have access to documentation (or VIM shortcuts!) Creating synths from a remote language client is an absolute joy and feels just like I’m working with a full SC install, I can use ProxySpaces, Patterns and all sorts. Sadly I feel much of the convenience and fluidity disappears once I start to handle MIDI IO on that device)

Thanks!

I’m not sure I got the setup right, either. But what if you write a custom program running directly on the Bela (outside of SuperCollider), creating a lightweight OSC protocol between your custom Bela program (any efficient language) and SuperCollider running on your laptop? The configuration could be a text file (any format you like), for example.

benefits:
a) lower latency
b) more flexibility with clear separation (Bela for hardware IO (MIDI/OSC), SC on your laptop just for synthesis)
c) no need to restart SC because of MIDI changes

1 Like

If you’re receiving MIDI on the laptop and sending OSC to the Bela, wouldn’t it be more direct just to send scsynth messages straight to scsynth, and not write an intermediary process?

There’s not a strict necessity to have a client on the same machine where a scsynth server is running.

hjh

1 Like

Oh. I understood that Bela receives MIDI IO. I thought it would have a small (lightweight) program rerouting the data as OSC to the laptop. So, there is a clear separation between what each system does.

I didn’t understand Bela has other functions in the setup.

EDIT: Oh, maybe I see what you mean. In a multiclient setup, bela could run sclang and it would communicate directly with scsynth on the laptop. But multiclient setups need extra planning, right? In order to sync their states?

1 Like

Ok, I see my misunderstandings now. But, in any case, both solutions bring something good.

If low latency and system stability are top priorities (with a little bit more flexibility) → a custom lightweight router is good, like I wrote

If quick implementation and staying within the SuperCollider ecosystem are priorities → sclang script that reserves control buses and sets up MIDI routing is the best option indeed

1 Like

Actually my initial proposal is the opposite: I suggested removing the client from the Bela!

I could be wrong but I think currently there’s MIDI going into the Bela, being converted to CVs there, and connected to a modular system. The client on the Bela is primarily handling this. Then, for some reason that isn’t clear to me, a client on the laptop needs to interact with the same buses handling the MIDI data on the Bela (and the laptop client I guess is doing other things too).

My suggestion to simplify is:

  • No Bela client – only a server.
  • MIDI would go into the laptop client, which would transmit the values to control buses on the Bela server.
  • The laptop client could also create and destroy synths on the Bela server to convert the MIDI values to voltages and forward them to the CV outputs, like a routing matrix.
  • Any other interaction with those buses would be coordinated in the same client, on the laptop.

So there would be no state to sync.

Single-client, multi-server isn’t a common use case, but it could be a good fit for this use case.

hjh

2 Likes