How to Divide the Music in Sections Using live_loops and Keeping Everything Synchronized

Hello, everyone! As the title says, I’m trying to control different sections (intro, verse, chorus, etc.) of the music using live_loops. I’m using set in a specific live_loop to let different live_loops know through get what’s the section and what to play, but when I change the section in the main live_loop everything gets out of sync.
I created a minimal example to show what I’m trying to say. To reproduce my problem, first, let the code as it is, with the line set :_use_arrangement, _section_1, hit play and listen, the snare will match the note with the highest pitch. Now stop everything, change that line to set :_use_arrangement, _section_2 and hit play, again the snare will match the note with the highest pitch. Now stop everything and change that line back to set :_use_arrangement, _section_1 and hit play, but now, change the line to set :_use_arrangement, _section_2 and hit play again without stopping the code, when the beat return to 1 the music will update to the new section but it will be out of sync and the note with the highest pitch will not match the snare hit. Don’t know if I’m missing something here or my logic is incorrect, anyone have any ideas on how to make everything sync?

# Song Sections
_section_1 = 1
_section_2 = 2

# Tempo
use_bpm 120


live_loop :metronome do
  sleep 1
end

live_loop :maestro, sync: :metronome do
  # Time signature 4/4 (apply the changes every 4 beats)
  if "x---".ring.tick(:maestro) == "x" then
    set :_use_arrangement, _section_1
  end
  sleep 1
end

live_loop :melody, sync: :metronome do
  section = get(:_use_arrangement)
  use_synth :tech_saws
  
  melodies = {
    _section_1 => {
      "melody"  => (ring :C4, :D4, :Eb4, :F6),
      "timing"  => (ring 0.25, 0.25, 0.5, 1.0)
    },
    _section_2 => {
      "melody"  => (ring :D5, :Eb5, :F5, :G7),
      "timing"  => (ring 1.0, 0.25, 0.5, 0.25)
    }
  }
  
  
  melody  = melodies[section]
  
  note    = melody["melody"].tick(:melody_note)
  rhythm  = melody["timing"].tick(:melody_timing)
  
  play note
  sleep rhythm
end

live_loop :drums, sync: :metronome do
  section = get(:_use_arrangement)
  
  drum_patterns = {
    _section_1 => {
      "kick"    => "x-x-x-x--",
      "cymbal"  => "xxxxxxxxx",
      "snare"   => "--x---x--",
      "timing"  => (ring 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.25, 0.25)
    },
    _section_2 => {
      "kick"   => "xx--x-x-xx--",
      "cymbal" => "---x-x-x----",
      "snare"  => "x-----x-----",
      "timing" => (ring 0.25, 0.25, 0.25, 0.25, 0.5, 0.5, 0.5, 0.5, 0.25, 0.25, 0.25, 0.25)
    },
  }
  
  pattern = drum_patterns[section]
  rhythm  = pattern["timing"].tick(:drum_timing)
  
  
  sample :drum_heavy_kick if pattern["kick"].ring.tick(:drum_kick)       == "x"
  sample :drum_cymbal_closed if pattern["cymbal"].ring.tick(:drum_hihat) == "x"
  sample :drum_snare_hard if pattern["snare"].ring.tick(:drum_snare)     == "x"
  sleep rhythm
end

For the curious and if someone has similar problems, I’m still trying to figure everything out. When I change every timing to 0.25, for example, and rearrange the notes (to still match the note with the highest pitch with the snare in separate runs for every section), everything is synced the vast majority of the time when I change the sections without stopping the music, and when the note is out of sync, it is out of sync for a much small factor, maybe 0.25 in this case. So I’m thinking this can be something related mostly to finishing one live_loop with a sleep value different than the one in the other live_loop, but even when everything is using the same sleep time there’s still a small chance for the live_loops to be out of sync, still not sure why this happens, maybe it’s some weird edge case, but at least I had some progress haha

This topic has been discussed a lot in various ways in differnt threads over the years.
I would start looking at this thread Using :sync with live_loops, especially the post by @samaaron in the thread.

Also this thread may give some pointers.

Yeah, I know, I’m aware of all the discussions (I even learned about synchronization through Sam’s post, when I started using Sonic Pi a month ago), I asked for help because none of them helped me with this specific scenario. I don’t even see anyone dividing the songs the way I’m trying to divide them (which is, probably, the reason for the sync problem)

One different approach I considered was to have both versions running continuously and using a mechanism to switch the amp: setting so that only one played at a time.

I think I’ll give your approach a try. Thank you very much for your help!
The only other way I found for this to work it’s to normalize everything to 0.25 and control the time via sustain like in the code below, but that looks very hack to me and I don’t think it’s a good solution.

# Song Sections
_section_1 = 1
_section_2 = 2

# Tempo
use_bpm 120

live_loop :metronome do
  sleep 1
end

live_loop :maestro, sync: :metronome do
  
  # Time signature 4/4 (apply the changes every 4 beats)
  if "x---".ring.tick(:maestro) == "x" then
    set :_use_arrangement, _section_1
  end
  sleep 1
end

live_loop :melody, sync: :metronome do
  
  section = get(:_use_arrangement)
  
  use_synth :tech_saws
  
  melodies = {
    _section_1 => {
      "melody"  => (ring :C4, :D4, :Eb4, 0, :F6, 0, 0, 0),
      "timing"  => (ring 0.25, 0.25, 0.5, 0, 1.0, 0, 0, 0)
    },
    _section_2 => {
      "melody"  => (ring :D5, 0, 0, 0, :Eb5, :F5, 0, :G7),
      "timing"  => (ring 1.0, 0, 0, 0, 0.25, 0.5, 0, 0.25)
    }
  }
  
  
  melody  = melodies[section]
  
  note    = melody["melody"].tick(:melody_note)
  rhythm  = melody["timing"].tick(:melody_timing)
  
  
  play note, attack: 0, decay: 0, sustain: rhythm, release: 0 if note.class == Symbol
  sleep 0.25
  
end

live_loop :drums, sync: :metronome do
  section = get(:_use_arrangement)
  
  
  drum_patterns = {
    _section_1 => {
      "kick"    => "x---x---x---x---",
      "cymbal"  => "x-x-x-x-x-x-x-xx",
      "snare"   => "----x-------x---"
    },
    _section_2 => {
      "kick"   => "xx--x---x---xx--",
      "cymbal" => "---x--x---x-----",
      "snare"  => "x-------x-------"
    },
  }
  
  pattern = drum_patterns[section]
  
  sample :drum_heavy_kick if pattern["kick"].ring.tick(:drum_kick)       == "x"
  sample :drum_cymbal_closed if pattern["cymbal"].ring.tick(:drum_hihat) == "x"
  sample :drum_snare_hard if pattern["snare"].ring.tick(:drum_snare)     == "x"
  sleep 0.25
end