MIDI clock slave mode: fix timing and note-off behavior #1
Conversation
- play() will reset both frame and pulse counts to 0, for alingment - stop() will silence - tap() will update immediately upon play(), not on the next frame
1637cd9
to
e06cd58
|
@unthingable I notice the |
|
@unthingable I looked up the MIDI specification about the MIDI clock, and the change seems to implement how start and stop was intended: "The master needs to be able to start the slave precisely when the master starts. The master does this by sending a MIDI Start message. The MIDI Start message alerts the slave that, upon receipt of the very next MIDI Clock message, the slave should start the playback of its sequence." (midi.teragonaudio.com/tech/midispec/seq.htm) I think the Also, the PR implements the following rule: "If a slave receives MIDI Start or MIDI Continue messages while it’s in play, it should ignore those messages. Likewise, if it receives MIDI Stop messages while stopped, it ignores those. " I ran a first test of this branch with TidalCycles and Orca seems to respond nicely. I'll play around with it a little bit more (mayble also with Ableton) to see if clock sync has become as predictable as I expect it to be now. Thanks! |
|
@unthingable When I use MIDI start and stop messages, beat syncing indeed seems to work nicely. However, when I use Since What do you think? |
|
@njanssen thank you for the thorough read and for for digging up the standards.
Oops, I missed that one. Here
It's only a guess but it looks like an attempt to sync based on the presence of the clock signal itself rather than START and STOP messages (notice how
That is an interesting detail!
I believe that was already implemented, |
Nice catch, I ignored that use case and focused on starting and stopping Orca from clock master, primarily because this is nontrivial to solve. But if we were to solve it... The problem is not only we'd have to count pulses in the background, we also need to count phantom frames. Otherwise, for example, let's say Orca was stopped on pulse 4 and started on pulse 8, so we didn't advance the frame that would have happened on pulse 6. Next frame advance will happen on pulse 12 — in alignment with pulse count but we're now 1 frame behind the clock and our beats are out of alignment. So, a possible solution: we count pulses and frames in the background, and then once Orca is unpaused, the next frame update sets the frame number to what it would have been as if Orca was playing all this time. It will be almost like we muted Orca, but with weirdness: clock-based things (D, C, etc.) will be in sync with the background clock, others (I, E/W/S/N, etc.) will not. I can try adding this, my guess you probably won't want to use The other solution is to not pause Orca when slaved :) No harm in having both available. |
f7a2a98
to
6cce494
|
@njanssen I did it! And it's more useful than I pessimistically predicted :-D In summary:
I'm a bit unsure about that last one, it is going beyond the MIDI standard and further reinforces the assumption that master always sends START on the beat. Then again, I'm looking at my Novation Circuit, it always assumes that and it ignores START when already playing (but it does handle SPP, achieving the same goal and a little more). I can't shake the feeling that this is a little too janky and controversial, but maybe that's ok. A more standard-friendly way would be to re-synchronize via SPP instead of clock START, I think I'd prefer to redo it that way or even not at all. I don't remember TidalCycles sending SPP, does it? |
| if (pulse.count % 6 === 0) { | ||
| client.run() | ||
| pulse.count = 0 | ||
| } else { |
unthingable
Sep 29, 2020
Author
oops, this didn't need to be an else
oops, this didn't need to be an else
Nice! I tried it out, and the only issue I ran into was timing offsets which I could solve by setting MIDI latency in SuperCollider or by using
No, TidalCycles doesn't keep track of time in terms of seconds, has its own internal cycle count. The standard way of controlling midi clock is with start messages (i.e. send a start command at the beginning of a cycle), see also https://tidalcycles.org/index.php/MIDI_Clock Thanks for working on this PR, I'm going to merge it in |
|
Yay!
Yeah, I played with different initial pulse count values to see if it would help line up with the beat without additional latency at the source and it didn't, it was always off by some other much larger amount. The current solution produces best results. My naive assumption is that my pulse math is correct and there is some inherent latency somewhere in the chain (in my case, Bitwig sending clock -> IAC transmitting clock -> Electron reacting to MIDI -> Electron emitting MIDI -> IAC transmitting event -> Bitwig reacting to event), this wouldn't be surprising. The good news is the latency seems consistent, I'm getting repeatable solid sync with a 40ms offset. |
play()will reset both frame and pulse counts to 0, for alignmentstop()will silencetap()willupdateimmediately upon play(), not on the next frame