Timing exception: Thread got too far behind time when using MIDI

This keeps crashing my SonicPi after ~32 seconds with “Timing exception: Thread got too far behind time” although I do not really have a slow computer.
Can anybody reproduce that?
I have version v3.2.2

You don’t have to connect a MIDI-device actually, this happens also without one connected.

use_bpm 120

use_midi_defaults channel: 1
midi_note_off :e3
midi_note_off :a3
midi_note_off :g3

ctr = [0,0,0,1,0,0,0,0]
running = 1

live_audio :in, input: 3, stereo: true

in_thread do
  sleep 32
  while(1) do
      if (running) then
        sample :bd_tek
        sleep 0.5
        if (ctr.tick == 1)
          sample :drum_snare_hard, hpf: 65, amp: 2
        end
        sleep 0.5
      end
    end
  end
  
  
  
  
  in_thread do
    while (1) do
        if (running) then
          midi_note_on :e3  #1
          sleep 0.2
          midi_note_off :e3
          sleep 1.35
          midi_note_on :e3  #2.55
          sleep 0.2
          midi_note_off :e3
          sleep 1.25
          midi_note_on :e3 #4
          sleep 0.2
          midi_note_off :e3
          sleep 0.8
          midi_note_on :g3  #1
          sleep 0.2
          midi_note_off :g3
          sleep 1.35
          midi_note_on :a3  #2.55
          sleep 0.25
          midi_note_off :a3
          sleep 1.2
          midi_note_on :a3 #4
          sleep 0.2
          midi_note_off :a3
          sleep 0.8
        end
      end
    end
    
    
    sleep 64
    
    running = false
    
    8.times do
      midi_note_on :e4
      sleep 1
      midi_note_off :e4
      sleep 1
    end
    
    running = true

Let me start by saying that I haven’t been this confused by SP code for a long time.
I see several issues:

There are two nested in_thread blocks without end. I think that is because of the do after while, removing them fixes the open blocks.
Nested threads should probably be avoided anyway, and for some reason SP doesn’t immediately throw an error because of the missing end.

There are two while true loops, you should definitely replace those with live_loops.
The thing I believe causes the crash is the if(running) in the while true loops. When running is set to false after the 64 beat sleep, these while loops repeat without delay.

I am still dissecting this, there is some other strange behaviour.

Unrelated:
If the snare is supposed to repeat, you want ctr to be a ring.

Edit 1:
I’m even more confused than 30 minutes ago.
I’ve tried to change it into something that should run fine and the loops still randomly stop.

Edit 2:
Relaunched SP and now my code works as expected.
As a general piece of advice: Use if and flags on the play/sample commands, not on the sleep. That way you keep the timing.

Here is the code that works for me:

#Code by gedobbles
#Modified by Davids Fiddle

use_bpm 120

use_midi_defaults channel: 1
midi_note_off :e3
midi_note_off :a3
midi_note_off :g3
running1 = false
running2 = true
running3 = false

ctr = (bools 0,0,0,1,0,0,0,0)

#live_audio :in, input: 3, stereo: true

in_thread do
  #Flags are set from this thread
  #I think this is close to what you wanted
  sleep 16
  running1 = true
  sleep 32
  running1 = false
  running2 = false
  running3 = true
  sleep 16
  running1 = true
  running2 = true
  running3 = false
end

live_loop :one, delay: 0.1 do  #Delay to avoid care conditionon the flag
  tick
  #puts 'loop 1'  #For debug
  sample :bd_tek if running1
  sleep 0.5
  sample :drum_snare_hard, hpf: 65,
    amp: 2 if ctr.look && running1
  sleep 0.5
end

notes = (ring :e3,:e3,:e3,:g3,:a3,:a3).stretch(2)
times = (ring 0.2,1.35,0.2,1.25,0.2,0.8,0.2,1.35,0.25,1.2,0.2,0.8)

live_loop :two, delay: 0.1 do
  #puts 'loop 2'
  tick
  midi_note_on notes.look if running2  #Message only sent when the flag is true
  sleep times.look
  tick
  midi_note_off notes.look  #Message is always sent
  sleep times.look
end

live_loop :three, delay: 0.1 do
  #puts 'loop 3'
  midi_note_on :e4 if running3
  sleep 1
  midi_note_off :e4 if running3
  sleep 1
end

Hello @Davids-Music-Lab,
thanks for your work on getting things working.
Actually the in_thread statements are not nested, it only looks like that, because the code indentation doesn’t work too well for that in SonicPi.
I didn’t stick to live_loop, because these have timing issues when using midi.
Also ctr does not necessarily have to be a ring, the snare repeats in my code when adding an else sleep 0.1 to each if (running).
I should have seen the issue with the zero time loops, however I was kindof blind because not these threads died but the one where actually something happened.

This does work and is more readable in my opinion:

use_bpm 120

use_midi_defaults channel: 1, port: "midi_through_port-0"
midi_note_off :e3
midi_note_off :a3
midi_note_off :g3

ctr = [0,0,0,1,0,0,0,0]
running = 1

live_audio :in, input: 3, stereo: true, amp: 1.5

in_thread do
  sleep 32
  while(1) do
      if (running) then
        sample :bd_tek
        sleep 0.5
        if (ctr.tick == 1)
          sample :drum_snare_hard, hpf: 65, amp: 2
        end
        sleep 0.5
      else
        sleep 0.1
      end
    end
  end
  
  
  
  
  in_thread do
    while (1) do
        cue :a
        if (running) then
          midi_note_on :e3  #1
          sleep 0.2
          midi_note_off :e3
          sleep 1.35
          midi_note_on :e3  #2.55
          sleep 0.2
          midi_note_off :e3
          sleep 1.25
          midi_note_on :e3 #4
          sleep 0.2
          midi_note_off :e3
          sleep 0.8
          midi_note_on :g3  #1
          sleep 0.2
          midi_note_off :g3
          sleep 1.35
          midi_note_on :a3  #2.55
          sleep 0.25
          midi_note_off :a3
          sleep 1.2
          midi_note_on :a3 #4
          sleep 0.2
          midi_note_off :a3
          sleep 0.8
        else
          sleep 0.1
        end
      end
    end
    
    
    sleep 63.95
    
    
    4.times do
      sync :a
      running = false
      
      midi_note_on :e4, port: "midi_through_port-1"
      sleep 1.9
      midi_note_off :e4, port: "midi_through_port-1"
      sleep 0.1
      midi_note_on :e4, port: "midi_through_port-1"
      sleep 1.9
      midi_note_off :e4, port: "midi_through_port-1"
      sleep 0.1
      midi_note_on :g4, port: "midi_through_port-1"
      sleep 1.9
      midi_note_off :g4, port: "midi_through_port-1"
      sleep 0.1
      midi_note_on :a4, port: "midi_through_port-1"
      sleep 1.9
      midi_note_off :a4, port: "midi_through_port-1"
      sleep 0.1
      midi_note_on :e4, port: "midi_through_port-1"
      sleep 7.9
      midi_note_off :e4, port: "midi_through_port-1"
      sleep 0.05
      running = true
      
      sleep 16
    end
    
    running = false
    stop

EDIT: Updated the code
EDIT2: This is what it sounds like https://www.dropbox.com/s/8oz3ihl01gm9tjw/sp2.ogg?dl=0

Is this a known issue? I find it surprising as I’m sure I’ve seen @samaaron using live_loops many times with midi in his performances.

@emlyn I found that you can use live_loops with midi, however you have to use_realtime and the latency is still better with in_thread; while(1) do at least the last time i have tried, that was like this. Just try it but I remember to have had a terrible latency with this sort of thing (half a second or so):

live_loop :a do
  note, vel = sync "/midi:midi_through_port-0:0:1/note_on"
  play note, amp: vel/127.0
end

I just tried it, you just have to use_realtime.

For more background check out the section on reducing latency in section 11.1 of the tutorial:

https://sonic-pi.net/tutorial.html#section-11-1

1 Like