What does time mean in Sonic Pi?

This is the paper that I reference in the talk which should also shed some light:

1 Like

Thanks Sam. The formal proof is a bit over my head, but I get the idea I think. If I can rely on Sonic Pi to be always right time-wise (ignoring little blips here and there) that’s massively useful practically.

I was looking at getting some hardware to act as a primary clock, like a BeatstepPro or similar, as I’m a bit frustrated using laptops that go AWOL in the middle of a song. But with my current setup of Sonic Pi playing loops and acting as the primary clock then that should be solid? I don’t actually want to play a BeatstepPro, it’s much more limited.

It’s interesting that we are living in a world were things used to be in perfect time - remember phone calls on land-lines? Now we’ve got super-accurate clocks everywhere but everything’s slightly out of sync - zoom, skype, DAB. I’ve got two radios on somewhere in the house, both slightly out of step. Uuugh.

1 Like

Delightful and scholarly . I guess You are more than a Rock Star
with a Raspberry Pi … :smile:

This paper described an enhancement to the Sonic Pi language that
improves the quality of musical experience for novice programmers
in a live coding context. This is achieved by modifying the semantics of the familiar “sleep” operator in a manner that is consistent
with musical expectations, while differing from the conventional
interpretation in many languages. As a result, the enhanced Sonic
Pi is able to retain identical concrete syntax to earlier versions,
while implementing behaviour that is simple and predictable from a
programmer perspective. Other music programming systems often
provide similar mechanisms in order to achieve predictable timing
behaviour, and our solution is comparable to those that have been
implemented in other systems. We therefore introduced a formal
semantics that can be used to prove the desirable properties of this
kind of temporal behaviour. This combination of simple syntax,
with formally defined semantics that correspond to user expectations, promises to be beneficial beyond the domain of music programming, to other types of physical world interface.
1 Like


Interesting to read the history too, with V1 that used the more conventional model. Myself, I like the technical stuff of course, but I’m only here because I can use it as an instrument. And it’s fab.

I have an inkling that the DAW I’ve been using (Reaper) does have some kind of ‘time safe’ model (if I’m allowed to call it that…) as I’ve experienced it dropping out but coming back in time. Whereas VCV Rack does more what I’d expect - after a hiccup comes back in tempo but out of sync. That said, Rack is so flexible and fun that it’s worth putting up with these foibles.

@samaaron - please can I just ask one thing to test my understanding, then I’m done on this. That the vt Virtual Time function genuinely is the ‘wall clock’ time (relative to start time of the run, of course).

Hi @soxsa

I was looking at getting some hardware to act as a primary clock, like a BeatstepPro or similar,

I don’t think that you can make SP listen to some external clock at the current state.

I have made some attempts to actually go the other way round and use SP as primary clock to sync a Fates (a small music machine based on Raspberry Pi and Norns software).

My setup actually works quite well with some lines of code:

use_bpm 60

live_loop :clock do
  midi_start if tick == 0
  4.times do
    midi_clock_beat port: "midi_usb-usb_midi_1"
    sleep 1

midi_usb-usb_midi_1 is refering to the 1 port of this little device I am using to connect to usb hosts (my laptop and the Raspberry Pi Fates device).

1 Like

…thanks Martin, yes I’m doing a similar thing with SP sending a midi or osc pulse to VCV Rack - and that’s working very nicely. As long as I can rely on SP to hold a solid beat (see my question above about the vt function. If that is what I think it is then all is good).

1 Like

Not quite, vt is the “virtual time” or “logical time”. It’s the time the code should be in a declarative sense rather than the actual wall clock time.

Sonic Pi never explicitly exposes the wall clock time to the user. However, you can access it via the unsupported (and therefore could vanish or break in a future version without warning) function Time.now. However, you’re definitely advised not to use it and if you do happen to find a very compelling use case for it, please do let me know.

Ok, thanks. So I guess vt and wall-clock time are kept close enough through thick and thin that you can’t hear the difference?

I’ll just use Time.now to run some tests while stressing the PC and RPi to convince myself! Otherwise, no I can’t see a musical use for it.

Yes, that’s the general idea, although vt never drifts. The worst that can happen is that it gets “too far behind” wall clock time and in that case, Sonic Pi kills that particular thread.

One way of seeing the difference between the two is to call them in quick succession:

10.times do
  puts "vt:", vt
  puts "now:", Time.now

This produces the following output. Notice that vt always returns the same value between calls to sleep or sync (as there’s none in this code, it will always return the same value). Also notice that Time.now drifts slowly as the wall clock advances normally. Consider how this drift may also vary on faster and slower machines…

{run: 1, time: 0.0}
 ├─ "vt:" 0.0
 ├─ "now:" 2020-10-16 22:58:39.3482036 +0000
 ├─ "vt:" 0.0
 ├─ "now:" 2020-10-16 22:58:39.3482193 +0000
 ├─ "vt:" 0.0
 ├─ "now:" 2020-10-16 22:58:39.3482264 +0000
 ├─ "vt:" 0.0
 ├─ "now:" 2020-10-16 22:58:39.3482324 +0000
 ├─ "vt:" 0.0
 ├─ "now:" 2020-10-16 22:58:39.3482377 +0000
 ├─ "vt:" 0.0
 ├─ "now:" 2020-10-16 22:58:39.3482431 +0000
 ├─ "vt:" 0.0
 ├─ "now:" 2020-10-16 22:58:39.3482488 +0000
 ├─ "vt:" 0.0
 ├─ "now:" 2020-10-16 22:58:39.3482545 +0000
 ├─ "vt:" 0.0
 ├─ "now:" 2020-10-16 22:58:39.34826 +0000
 ├─ "vt:" 0.0
 └─ "now:" 2020-10-16 22:58:39.3482661 +0000

Yes that makes sense, with vt being the ‘scheduled time’ of some event and Time.now being the time seen by the interpreter.

For my test I’ve got something along the lines of this, with ‘bar’ cueing lots of other loops. The absolute value of t isn’t important, but I’m looking for the error in bar length (dt) to stay small, and also the cumulative error (sumDt) which would indicate no drift from wall clock time. I appreciate that dt is subject to the jitter of the interpreter so isn’t a perfect measure, but it’s an indication.

Pleased to report that both these values seem to be sub-millisecond at the moment! That’s what I mean by virtual time being ‘locked’ to wall clock time. Even though I understand they are not the same.

use_bpm 60
dt       =0.0
sumDt    =0.0
live_loop "bar" do
  if !t0 then
    t0 = Time.now.to_f
    t1 = t0
  sleep 4
  t         = Time.now.to_f-t0
  if tick > 0 then
    barLength = t-t1
    dt        = barLength-4.0
    sumDt     = sumDt+dt
  puts "bar " + tick.to_s +
    ", " + (4.0*(look)).to_s +
    ", vt=" + vt.to_s +
    ", barlength=" + barLength.to_s +
    ", dt=" + dt.to_s +
    ", sumDt=" + sumDt.to_s
  t1 = t

I’ve had plenty of laggy calls on land-lines.

MIDI musicians in the 80s always had to consider lag when chaining MIDI equipment. Generally you could expect it to be a fixed lag; and it could even add groove to an arrangement.

I’ve read interviews with acid house pioneers who talk about manually syncing drum machines, so they could fine-tune the push/pull of the “drummer” on the fly. It’s a common DAW trick to do something similar by adjusting “channel delay”.

Some MIDI devices have “issues” with midi clock - I have a Zoom RT-223 which appears to occasionally ignore a clock beat, to continue playing offset by one beat. My solution is to abandon its sequencer and just trigger notes on it.

Modern equipment should be more reliable. I would trust Sonic Pi to emit a reliable clock (and you have enough control over the clock in Sonic Pi that you can do exotic things with it if you want to).


Thanks! I feel much better now reading that. I’ve bypassed several decades of electronic music kit development, and it looks like there were plenty of frustrations I’ve missed out on as a result. Yes, interesting how imperfections can be turned to creative use.

I’m also feeling particularly positive about how SPi handles time and latency. What a fantastic thing it is.

Re landlines, yes well everything uses some kind of network stuff these days. There was a time when you had essentially had just a long bit of copper cable between caller A and caller B.

1 Like

I can add to the thread too.

Fell in love with sonic pi and making music (that way). It worked great, to a certain complexity.

After a while playing with sonic pi, I had several synthesizers. And a Sequencer. thats when the trouble started.

SonicPi would not sync with them. Well, it did at first glance it would, but not reliably, and not without hiccups. It ran out of time/sync in Sonic pi alot, to start with. clock was always jerky. It had lag. It was not funny, because clock is key, and it was not able to either serv the clock reliably, or act on it reliably.

Dont get me wrong, its a perfect tool for me. I love it, I use it every day, - But sonic pi can not be the heart of the/my midi setup. I really wish it could :frowning:

My solution was to use a ‘hardware’ clock for the music. and sonic pi lives on the laptop.

1 Like

So sorry to hear this.

I’m aware that Sonic Pi can currently struggle to reliably react with low latency and low jitter to incoming events as this wasn’t part of its core design. It’s something I’m working very diligently to fix and I’m making some good progress in this area.

However, I’m very surprised to hear you weren’t able to generate a clock reliably. Could I ask what code you were using to do this, which version of Sonic Pi and on which platform? I would love to understand more about what’s going wrong for you as I treat this as a serious bug.

@ninn thanks for this. I’d like to hear more detail about your setup, get some ideas.

For me SPi is working reliably as a clock - but we might be talking about slightly different things. I’m not using it to send actual midi clock beats, but a pulse every bar (or few bars) to reset the clock on my secondary device (vCV Rack). This is pretty robust in practice.

I don’t have a hardware synth, but at some point I’ll try SPi as a real midi clock into VCV Rack and see how that goes too.

@soxsa, if you try this and do have any issues, please could you attempt to do the following:

  • Use midi_clock_beat not midi_clock_tick
  • Don’t use sync in the live_loop that sends the MIDI clock
  • Don’t put the live loop that sends the MIDI clock into “real time mode” with use_realtime.
  • Don’t send the MIDI clock to all connected ports, rather specify which port to send to explicitly

The following should be all you need:

live_loop :midi_clock do
  midi_clock_beat port: "name_of_your_midi_port_listed_in_the_prefs"
  sleep 1

Thanks Sam. I just finished trying it actually - and I did read the help (can you believe it ?) so that’s the code I used. Having midi_clock_beat is neat. But then I remembered reading that VCV Rack has problems syncing off a midi clock, and indeed I found it hunting around. So it’s not a good test. I think they might be fixing that in the next release.

In the meantime, my DIY method is solid. The only issue is that I have to manually set the clock tempo the same as the SPi, but that’s no hardship. I’m thinking I might go OSC with this and write a Rack module that picks up the tempo as a OSC parameter and translates it to a CV - much easier than this clock business.

I wish now I had a hardware synth to try this with.

@samaaron I am so sorry!!

My computer is old, - and my sonic pi code became too complex for it. This is 90% of the problem right there, on my (pc hardware) side. Imagine a non-professional, cheap midi setup ontop of that. The issue was getting it in sync with hardware sequencer. This issue did not feel like a sonic pi problem - more like a hardware/software/interface-problem in general with lag and so on… sonic Pi is awesome! But old pc with code too complex and jitter = not the masterclock I wanted it to be.

I tried a simple setup / experiment before I kinda gave up on that idea (because I needed another more stable solution):
Korg Monologue got a built-in 16 step sequencer. In theory - When a note hits - the sequencer starts to step thru the 16-step sequence / or restart/transpose it, when it’s already running. So, I wanted Sonic Pi to send a random note at step 1. to just hit on step 1. I was not able to to this in sync with sonic pi serving the clock. reliably. without adjusting delays and so on… The flashy step sequencer on the monologue would show jerky motion and/or overstep a lot. Got similar experiences with other hardware, especially chea midiinterfaces (?) and volcas. but will test that for sure again - with other midi interfaces, just to get an update on that issue for myself - and so I dont spread mis-information about sonic Pi!

It kinda worked. But was not ‘reliable’ as the clock. And thats more of a general PC-problem, than a specific sonic pi problem. I would expect every other midi program / daw to give similar experiences on similar hardware.

SonicPi is like a swiss knife: It reads and does everything i want, when want it. Its like the hidden dagger to stab somone with. But I needed a rock to lean on. And my PC was not able to deliver.

Hey @ninn,

no need to apologise :slight_smile:

| completely agree that a simple dedicated hardware microcontroller generating MIDI clock ticks is always going to be more reliable than a sophisticated CPU which is not just generating MIDI clock ticks, but also managing an operating system kernel, multiplexing many, many simultaneous threads of execution. This is before we even start to discuss the design decisions behind CPUs which recently have all focussed on increasing throughput and not on reducing latency and jitter.

However, all that said Sonic Pi should be a pretty rock solid clock even on old hardware.

I don’t fully get what you were trying to do though. Could you share the code? Were you able to reproduce the exact experiment with hardware MIDI signals and get it working as expected?

I’m imagining your code might have been something like this:

live_loop :foo do
  midi_note_on (scale :e1, :minor_pentatonic).choose
  16.times do
    sleep 1

However, I might have completely misunderstood what you were trying to do :slight_smile: