Midi Clock using AMIDI, which thread to use (writing is blocking?)

51 views
Skip to first unread message

Andrea Fiorito

unread,
May 26, 2023, 11:30:44 AM5/26/23
to android-ndk
Hello,
As a pet project, I'm trying to implement an app that sends different midi clocks to different devices at the same time (to control multiple external sequencers).
Being midi clock signals, the thread where this would run should have ideally a real-time behaviour. With a maximum bpm of 300 per quarter, and 24 midi clock messages per quarter,  I need to send a message every 60 / (300 * 24) = 0.00833333333 sec. (to have an acceptable jitter it means the thread loop should run every ~ 0.08ms)

To receive midi messages, it's suggested to use: "the AudioStream data callback in AAudio). Since AMidiOutputPort_receive() is non-blocking, there is very little performance impact." Even with a buffer of 64, for 44.1k sampling, seems that the audio callback would run every 64/44.100 = 0.00145124716 sec, that would give a quite significant jitter for a clock.

To send midi messages, it's said: "Since the timing of the outgoing MIDI data is well understood and controlled by the app itself, the data transmission can be done in the MIDI app's main thread. However, for performance reasons (as in a sequencer) the generation and transmission of MIDI can be done in a separate thread.
Apps can send MIDI data whenever required. Note that AMidi blocks when writing data."
I don't see using a background thread fulfilling those strict timing requirements, and as far as I understand:
- I cannot set SCHED_FIFO or SCHED_RR,
- I can set the niceness of a thread to -19 using that convoluted trick with JNI but not with pthread_setschedprio (why is that, any differences?)
- I can set the affinity of the processor as done on the megadrone sample of oboe library, function setThreadAffinity, although there is no guarantee i can get an exclusive core.

So my question are:
- how to get the best thread configuration for sending and receiving midi clock signals with the lowest jitter possible?
- do I need multiple threads for different couples of (device, midiport) given that the calls are blocking?
- would the use of the timestamp in AMidiInputPort_sendWithTimestamp be able to mitigate those problems? is there any example how to use correctly the timestamp and any guarantee its mechanism is not implementation dependant?

Thanks,

enh

unread,
May 26, 2023, 11:35:20 AM5/26/23
to andro...@googlegroups.com, Robert Wu
+Robert Wu for all the midi stuff...

On Fri, May 26, 2023 at 8:30 AM Andrea Fiorito <ing.f...@gmail.com> wrote:
Hello,
As a pet project, I'm trying to implement an app that sends different midi clocks to different devices at the same time (to control multiple external sequencers).
Being midi clock signals, the thread where this would run should have ideally a real-time behaviour. With a maximum bpm of 300 per quarter, and 24 midi clock messages per quarter,  I need to send a message every 60 / (300 * 24) = 0.00833333333 sec. (to have an acceptable jitter it means the thread loop should run every ~ 0.08ms)

To receive midi messages, it's suggested to use: "the AudioStream data callback in AAudio). Since AMidiOutputPort_receive() is non-blocking, there is very little performance impact." Even with a buffer of 64, for 44.1k sampling, seems that the audio callback would run every 64/44.100 = 0.00145124716 sec, that would give a quite significant jitter for a clock.

To send midi messages, it's said: "Since the timing of the outgoing MIDI data is well understood and controlled by the app itself, the data transmission can be done in the MIDI app's main thread. However, for performance reasons (as in a sequencer) the generation and transmission of MIDI can be done in a separate thread.
Apps can send MIDI data whenever required. Note that AMidi blocks when writing data."
I don't see using a background thread fulfilling those strict timing requirements, and as far as I understand:
- I cannot set SCHED_FIFO or SCHED_RR,
- I can set the niceness of a thread to -19 using that convoluted trick with JNI but not with pthread_setschedprio (why is that, any differences?)

from the docs (which we really need to get out of the header files and onto the web)...
```
/**
 * [pthread_setschedprio(3)](https://man7.org/linux/man-pages/man3/pthread_setschedprio.3.html)
 * sets the scheduler priority of the given thread.
 *
 * This call is not useful to applications on Android, because they don't
 * have permission to set their scheduling policy, and the only priority
 * for their policy is 0 anyway. If you only need to set your scheduling
 * priority, see setpriority() instead.
 *
 * Returns 0 on success and returns an error number on failure.
 *
 * Available since API level 28.
 */
int pthread_setschedprio(pthread_t __pthread, int __priority) __INTRODUCED_IN(28);
```

- I can set the affinity of the processor as done on the megadrone sample of oboe library, function setThreadAffinity, although there is no guarantee i can get an exclusive core.

So my question are:
- how to get the best thread configuration for sending and receiving midi clock signals with the lowest jitter possible?
- do I need multiple threads for different couples of (device, midiport) given that the calls are blocking?
- would the use of the timestamp in AMidiInputPort_sendWithTimestamp be able to mitigate those problems? is there any example how to use correctly the timestamp and any guarantee its mechanism is not implementation dependant?

Thanks,

--
You received this message because you are subscribed to the Google Groups "android-ndk" group.
To unsubscribe from this group and stop receiving emails from it, send an email to android-ndk...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/android-ndk/9076e89b-6220-4afa-8b4b-7095ab7b8af3n%40googlegroups.com.

Phil Burk

unread,
May 26, 2023, 11:59:13 AM5/26/23
to andro...@googlegroups.com, Robert Wu
Hello Andrea,

> Even with a buffer of 64, for 44.1k sampling, seems that the audio callback would run every 64/44.100 = 0.00145124716 sec, that would give a quite significant jitter for a clock.

If you use sample rate 44100 then you will probably not get a low latency stream. The burst size will then be much bigger than 64.
You should not specify a sample rate or a data callback size. Then you may get a 48000 Hz stream with a callback every 2 msec.

Note that the USB MIDI Service is written in Java. So your timing may be more dependent on the Java scheduling jitter than the jitter in your native application thread.

Phil Burk


Reply all
Reply to author
Forward
0 new messages