Interapplication Communication

Forward: Yes, this post is long enough to have a forward. :sob::sob::sob: But, the issue discussed is probably fundamental to many potential uses of MIDI Translator; establishing a standard way (algorithm) to address real-time inter-application issues as discussed below could benefit many users, existing and potential—and not require changes to MT. In this case, I’m communicating with Keyboard Maestro, which is a natural pairing with MT. The issue here would be the same for any real-time application paired with MT, tho.

Cross-Reference: The same issue being discussed on the Keyboard Maestro forum.

Setup: I’m using MT to process real-time MIDI CC data (each on a different CC#), and send it to an external application via AppleScript (at the Output stage). The external application controls the brightness of lights. There’s 50 lights, so I want to encode and send data packets of 50-integer data structures (e.g. arrays).

Question: What is the most efficient way to do this?

My guess:

Step 1: Allocate 50 global variables. (As I understand, there is no array syntax in MT.)

Step 2: Use one program to process all 50, instead of 50 programs that process one CC#.

Step 3: Implement a time-slice, for two reasons:

  • a) There’s more incoming MIDI data than MT will be able to process in real-time (and even if it could, the information wouldn’t all arrive in time via AppleScript).
  • b) All 50 CC#s are rarely used.

The time-slice would:

  • Step i:. Set all 50 variables to zero.
  • Step ii: Spin for a predetermined duration (e.g. 2ms).
  • Step iii: While spinning, it will collect whatever data it can, and write incoming data to the corresponding variables.

Step 4: Export the 50 variables to the other process.

Step 5: Goto Step 1.


There would presumably be significant data loss here, but that’s fine. Any CC# values received during the time-slice are valid. It’s critical, though, that every CC# sent to MT during the time-slice has at least one of the values recorded by MT. In other words, if 32 different CC#'s are sent during the time-slice window, then MT should record data for 32 variables.

There’s a few ways I can think to export the CC# data from MT at the end of each time-slice. I’m assuming all of them:

Use one MT programme instance (i.e. no multi-threaded, concurrent ‘programme’ instances).
Use a single AppleScript function instance (i.e. coded in MT’s ‘Global’ section), the need only be compiled once, and no additional memory needs to be allocated (by MT or the AppleScript runtime system).

So, after each time-slice, we could…

  1. All 50 variables are passed with a single AppleScript (AS) ‘Do’ call to the external application. A potential problem here is that the external app must process the data, and the lights must change, before my MT programme’'s next time-slice. I suppose this could be tweaked by trial and error, adjusting the time-slice to fit.`

or

  1. Use AS in MT to ‘directly’ write to all 50 variables in the external application, then issue a ‘Do’ call to the external application. For this to work, all 50 variables need to have been recorded by the external application before it acts on the ‘Do’ request from MT. This begs the question of how interrupts are implemented in MT, AppleScript, and the receiving application’s implementation of AS.

Ideally, we could use spin-locks or semaphores, but I don’t believe those are explicitly supported in MT’s scripting language.

So, another way to implement this might be for MT and the external application (EA) to message each other, and pause their executions until receiving the correct reply. This approach might slow things down.

The only speed optimisation we can take advantage of is that we are free to drop data—within a 2ms time window, we need only one value for each CC#.


OK, that’s a lot!

Any thoughts are greatly appreciated, thank you!

Bill

Hi Bill,

So from what I’m reading, you want to capture CC messages and send output them via AppleScript.
Is there a reason we can’t just do that as they come in instead of waiting for a given time period? If so, you shouldn’t need any global variables. Do you have an example of the AppleScript that you would be using for output? Does there need to be any delays to ensure we are not sending too fast?

Steve Caldwell
Bome Customer Care


Also available for paid consulting services: bome@sniz.biz
1 Like

Hi Steve, thank you for your reply. As below:

So from what I’m reading, you want to capture CC messages and send output them via AppleScript.

Yes.

Is there a reason we can’t just do that as they come in instead of waiting for a given time period?

Good question—that approach would (generally) require additional processing resources, and be unpredictable in execution time. Think of a D/A converter for audio input—you are always recording, and the amount you record and transfer is the same for each buffer cycle.

Within this context, global variables increase processing efficiency because they only need to be allocated once, are (from the application’s perspective) always in the same place, and are persistent. The latter quality (if supported in AppleScript) enables MT to pass a pointer to the data, instead of sending the data itself.

Do you have an example of the AppleScript that you would be using for output?

That is indeed my question. :sweat_smile: One way (presumably) is to create an AppleScript list, which might go like this (say for six CC values)…

set CCbufferFrame to {ga, gb, gc, gd, ge, gf) tell application "Keyboard Maestro Engine
do script "UpdateLights" with CCbuffer. end tell

The other route (presumably) is to set the corresponding remote global variables directly, followed by a remote function call.

tell application "Keyboard Maestro Engine" setvariable gKMcc1 to ga
setvariable gKMcc2 to gb setvariable gKMcc3 to gc
setvariable gKMcc4 to gd setvariable gKMcc5 to ge
setvariable gKMcc6 to gf do script ‘UpdateLights’
`end tell

Does there need to be any delays to ensure we are not sending too fast?

Probably? It’s a good idea to schedule the data transfers, regardless. We could start at the frame rate we want, and see what happens.

Much of this comes down to how stuff is done under the hood in MT, AS, and the receiving application (which I don’t know).

Thank you for reading my ridiculously long post.

So one translator to update the global variables in MT Pro as they come in depending on the CC value.
Then a translator with output tell application to start the transaction to KeyBoard Maestro
After a delay start sending the variables with a repeating timer (if AppleScript can handle it fast enough).

And when that is done send the end tell.

I’m not an AppleScript programmer.

The repeating timer is generally the approach I used updating MIDI LED lights.

Steve Caldwell
Bome Customer Care


Also available for paid consulting services: bome@sniz.biz
1 Like

Hi Steve, thank you for your reply.

1 Like