Using 'at' block

Hi there! I’m a newcomer to Sonic Pi from Argentina. I’ve been playing around for two days and I’m now building up a cover from a Thom Yorke song, Impossible Knots.

I’m using rings to store the sequence of notes as well as the release time of each note and the time at which each note should be played (so I can use the ‘at’ block inside my live_loop).

I ended up with 3 rings with the same number of elements with the information of each note.

bass_notes = (knit :eb3, 3, :db3, 1, ...) # note sequence
bass_note_lengths = (knit 1.5, 1, 0.75, 1, 1.5, 2, ...) # release times
bass_timings = (ring 0, 1.5, 2, 3, ...) # position of note for 'at' block

My first idea was to use tick to get the correct index for every note information:

at bass_timings do
  tick(:note)
  play bass_notes.look(:note), release: bass_note_lengths.look(:note)
end

But when I do this I get the first element everytime. Shouldn’t the tick be incrementing?

Finally, the following code works well for me, but I thought using tick was the cleanest way to go:

index = 0
at bass_timings do
  play bass_notes[index], release: bass_note_lengths[index]
  index = index + 1
end

Is there a way to make the previous code work? Or can I pass more than one ring as parameters to the ‘at’ block, e. g.: ‘at bass_timings, bass_notes, bass_note_lengths do |note, length|’?

1 Like

Hi @jotaleal,

welcome to the Sonic Pi community - it’s lovely to have you here.

As a quick answer to your question, try time_warp instead of at. Take a look at the docstrings for each function (including the examples) and after that if you still have any questions please do ask them back on here and we’ll be very happy to answer them!

2 Likes

Great! I will check time_warp then. Thank you, @samaaron!

1 Like

How about…

bass_notes = (knit :eb3, 3, :db3, 1) # note sequence
bass_note_lengths = (knit 1.5, 1, 0.75, 1, 1.5, 2) # release times
bass_timings = (ring 1, 1.5, 2, 3) # position of note for 'at' block


use_synth :hollow

with_fx :echo do
  with_fx :ixi_techno do
    
    live_loop :loopy do
      tick
      puts bass_notes.look
      play bass_notes.look, release: bass_note_lengths.look
      sleep bass_timings.look
    end
    
  end
end

Eli…

Welcome to the community! Sounds like you’re getting some great advice from some of the ninjas here.

You are so right, @birv2! It’s great having these beautiful beasts helping me and greeting me as you also did.

So, I got from the documentation that as at runs in its own thread, I can’t depend on the tick from the live_loop incrementing correctly because it creates it own instance and gets reset every time inside that thread.

Thanks, Eli, for your answer! For some reason that code doesn’t act like I was expecting. I couldn’t get the timings to be set the way I wanted.

Reading the docs I found out that there is a way of retrieving the index of the at block passing an extra arg without the need of declaring and incrementing it myself:

at bass_timings do |t, idx| # idx will increment every time (0, 1, 2 ...)
  play bass_notes[idx], release: bass_note_lengths[idx]
end

Finally, as @samaaron pointed out, I could use time_warp with tick as it runs synchronously within the same thread as the live_loop:

time_warp bass_timings do
  tick(:note) # tick will be incremented ever time also
  play bass_notes.look(:note), release: bass_note_lengths.look(:note)
end

Also, with time_warp I can use the same index approach as with the at block:

time_warp bass_timings do |t, idx| # idx will increment every time (0, 1, 2 ...)
  play bass_notes[idx], release: bass_note_lengths[idx]
end

Sorry for the lengthy reply, but I thought I should leave this for anyone else having doubts about the usage of these blocks.

5 Likes

For anyone curious about what I was making with this code, I leave this link here with a preview: https://www.youtube.com/watch?v=h1HQqz-pIKg

1 Like