Hi, how is the sonic pi with timing accuracy? Would it be possible to implement clock ticking at midi tick interval ? ( e.g. 20833 us = 120bpm@24ppq )
I would like to build midi looper, but all implementations here work with real-time timestamp, which is not really suitable for evenly spaced events or quantization.
i have something like this in c++, would it be possible to code this in sonic pi ?
BPM = 120.0;
PPQ = 24;
uSecsPerTick = 60000000/(BPM*PPQ);
uint32_t Ticks = 0;
auto when_started = clock_type::now();
auto target_time = when_started + std::chrono::microseconds(uSecsPerTick);
target_time += std::chrono::microseconds(uSecsPerTick);
auto now = clock_type::now();
I think it should manage alright - there’s a
midi_clock_beat function that sends one beat’s worth of clock ticks (24) spread over the specified time, so it should be as simple as:
live_loop :midi_clock do
The exact timing accuracy is likely platform dependent. I have heard of people having issues with timing accuracy on Windows, but I’m not sure if that is still an issue or not.
You can also restrict the midi_clock_beat to a single midi port and channel with the
channel: options, which should help with efficiency.
There is also
midi_clock_tick that allows you to send each clock tick individually:
live_loop :midi_clock do
# do something
sleep 1.0 / 24
Whether this also keeps up accurately will depend on how much you do on each tick. I’d also recommend restricting the clock tick with the
channel: options as before to improve efficiency.
You mention reading an incoming midi message on each tick, in which case this might not be quite the right approach. You might want to
sync on the incoming midi message in your live_loop and use that to trigger whatever you are doing instead of calling
If you are able to explain exactly what you are trying to achieve (what are the incoming midi messages, what are you doing on each one, …), it might be easier to suggest a good way to go about it.
Oh, i did not know i can sleep for fraction of beat!
Well, as time in MIDI world is discrete i thought it would be wise to timestamp all events in ticks not in real time from the very beginning.
So instead of calculating corresponding tick for every event, i would know that events read in tick n belong to tick n-1 . I know i`m losing precision in time domain using this discretization, but i want to quantize events further into steps…
I`m trying to make something called “retrospective looper” - instead of regular mode of recording - “rec - perfom - loop” it would record always into kind of ring buffer and after pressing a button it would loop last x bars. Of course i want to hear what notes are being played maybe already quantized to steps in real time.
So with clock ticking at 24 ticks per beat i would something like this at each tick :
- read buffered midi messages (note ons, offs)
- write these events into record buffer at current_tick position, clear position if nothing was read
- write these events into play buffer into “next step” position ( (tick/6+1)*ticksPerStep - 1/16t quantization)
- send midi events from play buffer at this position
running this with clock ticking 24 times per beat and resetting after let`s say 384 ticks you would have 4 bar loop with overdubbing and quantization to sixteenth notes / steps . if play buffer position is cleared after playing it, you would get something like real-time quantization. played notes would “sound” on next step , (in real world it is needed to place note-off one step further if on-off happens inside one step)
now consider player is feeding the recording buffer with notes (overwritten each 4 bars), hearing quantized notes from play buffer and metronome, after multiple 2-bar takes he decides that the last 2-bars were nice, stops playing and hits “loop” button. boundaries for last 2-bars are calculated and part of the record buffer is copied into sequence (quantized), this sequence is then used to fill play buffer for corresponding ticks. i know that in this case only positions at steps (ticks mod 6 = 0 ) are really used , but consider having this also for unquantized CCs.
Have you seen this post by @robin.newman? I think it sounds quite close to what you want, so it might be a good starting point.