What does time mean in Sonic Pi?

This may seem a silly question, but bear with me.

Can I check with someone exactly what ‘time’ is to Sonic Pi. Is it actual, real-world time or an internal virtual clock? So if a sound is scheduled for, say, 10s but there’s a PC glitch taking processing resource away for a very short time, then it will still try to sound at 10s real, wall clock time? Or is it pushed out by the length of the glitch?

I’m hoping from what I’ve seen that it’s the former. I’m thinking of other apps (VCV Rack in this case) where I think it’s the latter i.e. it’s ‘clock’ simply pauses during a glitch so when it picks up again it’s out of time. That’s what I observe anyway.

Thanks for any light. It’s important because I want to know what to rely on.

When I’m drumming and drop a stick, I don’t make everyone stop while I pick up another one. There’s a ‘glitch’ but then I slot back into the same groove. I’m hoping Sonic Pi is like that.

Hi there, this might help:

2 Likes

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.

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

Definitely!

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
  end
end

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
end

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
t0=nil
t1=nil
barLength=0.0
dt       =0.0
sumDt    =0.0
live_loop "bar" do
  
  if !t0 then
    t0 = Time.now.to_f
    t1 = t0
  end
  
  sleep 4
  
  t         = Time.now.to_f-t0
  
  if tick > 0 then
    barLength = t-t1
    dt        = barLength-4.0
    sumDt     = sumDt+dt
  end
  
  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
  
end

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).

2 Likes

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
end

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.