Pass data to helper app(for midi timing)?

I see this code in another post about methods to improve midi timing. I need an app for a specific role that can’t be done with my other programs like Logic. The goal is the have a 2 track sequence plus a metronome player, so 3 tracks playing at once. Using thread1 for metronome and thread2 for playing back chords is not usable. After hours of reading everything I can find I see some examples using an external app to do some work which claims better timing. This example does not allow for testing my own midi notes. Question: Is there a way to pass a helper app an array(ie sequence(0)) as taking time to save an array( midi track), BPM, plus start time to disk would produce unwanted additional latency. Second: Can a helper app be stopped by the main app. In their example they just have the help app die when it’s done.

Ideally I want to send an array for midi playback, and give it a start time of currentTime + offset so that it gives the helper time to start and wait for a time ie 200ms in the future. Then the main app can allow recording midi on top of the other track and both be in sync against a known clock time. Likewise, send the helper app a start time, BPM, and have it play a metronome sound until the helper is told to quit. If there is no way to communicate from Main>helper to tell it to stop, and give it BPM, StartTime, anArray, then this wont work. Any suggestion welcome.

Load this example in a windowless app, call the app from main via this method:

Sub Thread1Run(Sender as Thread)
  dim s as new shell
  dim f as FolderItem = GetFolderItem("").child("notes.app")
  s.Execute("open "+f.ShellPath)
End Sub

(notes.app:)

Noteplayer1 = New NotePlayer NotePlayer1.Instrument = 1 // Notes for Do Re Mi Fa So La Ti Do // see http://en.wikipedia.org/wiki/Do-Re-Mi from The Sound of Music http://en.wikipedia.org/wiki/The_Sound_of_Music // (C, D, E, F, G, A, B, C) Dim doReMi(7) As Integer doReMi = Array(60, 62, 64, 65, 67, 69, 71, 60) For Each note As Integer In doReMi NotePlayer1.PlayNote(note, 100) // Pause to let note play App.SleepCurrentThread(500) Next quit

Copy array to clipboard ?

What if a helper app was always running. Scanning the copy buffer for a play command, bpm, starttime, stop.

So in testing using a separate app as a metronome using a thread for counting off 1000ms, there was significant improvement when going back to the main app and playing a piano track and launching the separate app with the metronome. I find that running 2 threads in the same app unusable for music. The main app copies “Play” into the clipboard. The helper is in a loop watching the clipboard. I don’t notice any lag time from pressing the record or play button to the helper starting. It’s not noticable the lag time from pressing play on the main app to getting the metronome started on the helper.

Next I tried launching a metronome in the main app and the helper app. There is a slight flam( low milliseconds between the sounds). I think that by copying the current time + an offset and including that value in the clipboard will allow both the main and helper app to start more precisely on the clock by syncing on a future timestamp, I predict it will get rid of most the flam. After that I’ll try launching an additional helper app and see how it works with one helper playing a metronome and another app playing chords, while the main app plays another instrument.

This is the code in the separate helper app.

Var c As New Clipboard Var s As String s = c.Text c.Close dim starttime as int64 dim t0 as int64 dim t1 as int64 StartTime = microseconds / 1000 t0 = microseconds / 1000 dim BPM as integer = 60 dim RecordLength as integer NotePlayer1.Instrument = 116 '16385 RecordLength = 0 While RecordLength < 500 //Tick off metronome 10 times While s <>"Play" s = c.Text c.Close Wend While s = "Play" //NotePlayer1.Reverb = false NotePlayer1.PlayNote(60, 100) NotePlayer1.PlayNote(60, 0) RecordLength = RecordLength + 1 t0 = 0 'clear from previous t1 = 0 'clear from previous t0 = (microseconds / 1000) Do until t1 >= (60 / BPM) * 1000 t1 = (microseconds / 1000) - t0 loop s = c.Text c.Close Wend Wend

Personally I would use shared memory for sharing the data; are you intending to ship on the Mac App Store?

I don’t know about shared memory. I’ll look into it. Thanks. I don’t know if Mac App Store is an option at this stage.

Shared memory can easily be used with the MBS plugin. But you really only need that if you exchange a lot of data between main app and helper app. Otherwise, a simple communication with some type of json is much simpler.

Out of curiosity, what is your preferred “simple communication” for json? Using stdin/stdout? IPC? TCP sockets? Something like Aloe?

Json over TCP. This is what I use: https://github.com/sworteu/JSONRPCSocket .

Thanks. I’m always interested in alternative viewpoints. I’ve been using a combination of Aloe Express and Shared Memory where I felt that was appropriate. Aloe has worked out better for me than IPC, and easier than rolling everything myself. Most of the payloads have been JSON though (using POST), but not necessarily conforming to the JSON-RPC spec.

The ReadMe for that GitHub project says in bold “NOT WORKING YET” but has no updates in about two years. You have found it to be stable though in the current commit?

The “NOT WORKING YET” seems quite misleading. The code IS working and it’s stable.

Can that be done with declared for both Mac and Windows? if so any understandable (by a non expert) references on how to set it up?

Thanks,
-karen

That is something i have been thinking about, but don’t know anything about setting up and using shared memory… but it seems the most logical way to maximize throughput with helpers for a server with Xojo.

  • Karen

I have done it for macOS; but not Windows. I didn’t actually use it as I ended up writing an Objective-C plugin that uses GCD to do the processing. Aside from figuring out how to do what I wanted in Obj-C, multi-core programming is insanly simple with GCD. I would go as far as to say shockingly simple compared to the hoops we have to do to get helper apps working, and the performance gain is core-1 fold (8 core processor nets you a 7x factor). I’m only crunching numbers, so YMMV on what you’re doing.

I’d like to see some GCD support in Xojo, and quite frankly I think it’s much needed in today’s age of throwing more and more cores into processors. I read that theres already 56 core processors available.

I’m sure better support for concurrency would be welcomed by all. It is sad that in 2019 we are still stuck on one core when using Xojo even though more cores is clearly the future of CPUs. I imagine it would also help to solve IDE performance issues such as the woeful code editor performance which seems to be partly caused by the autocomplete code blocking the main thread.

[quote=459623:@Karen Atkocius]That is something i have been thinking about, but don’t know anything about setting up and using shared memory… but it seems the most logical way to maximize throughput with helpers for a server with Xojo.

  • Karen[/quote]

I use the MBS plugins

i don’t know if the engineers they have now have the right background for doing it, but I think providing good Xplatform support for using multiple cores, would have provided a much better ROI than API 2.0 or the Xojo framework.

  • Karen

I took a look at the MBS shared memory docs and their example, but I don’t really understand how they work - is it possible to have more than one shared memory block? Or do you set up the data in the shared memory block that the helper app parses? And then how does the helper app return the result (or whatever) - by modifying the data in the shared memory block? And how does the parent app know when the helper app is done? By polling a location in the memory block?

Ideally, I’d like to have as many as 3 or 4 helper apps processing data. Is there an example somewhere - or docs that explain better how to use this for sharing?

would more cores help - maybe - figuring how to segment it into multiple tasks would be the trick here
a lot of it has to do with autocomplete just using a completely wrong method to do what its doing that doesnt match what the compiler would do

Yes you can have as many shared memory blocks as you like. You just need to name them uniquely.

I based my implementation by looking at the MBS examples included with the plugins. The basic code of which is also available at the example links near the bottom of the page here

Note that with macOS, a named shared memory location will exist until reboot or you explicitly remove it, even if no app is holding a reference to it. I believe under Windows, that is not true. But I could remember that wrong – for now I have only used it in macOS.

[quote=459726:@John McKernon]I took a look at the MBS shared memory docs and their example, but I don’t really understand how they work - is it possible to have more than one shared memory block?
[/quote]

Looking at the MBS docs I would say yes.

[quote] Or do you set up the data in the shared memory block that the helper app parses?
[/quote]

That depend on exactly what one is doing… It could work that way

it could.

I would think sending messages through TCP sockets so you get a data available event message to know when the helper is done

[quote]
Ideally, I’d like to have as many as 3 or 4 helper apps processing data. Is there an example somewhere - or docs that explain better how to use this for sharing?[/quote]

I don’t think so… logically at a high level what needs to be done is straight forward, but the devil is always in the details.

-Karen