How to code this


#1

Hi,

I suggest this little exercice :smile:

Let’s say we want this structure to loop. What do you propose as solution ?

2018-12-11%2018_44_46-Sans%20titre_%20-%20Live%20Intro

I give you some code basic poor music but it will do the stuff

use_bpm 120

### DRUMS ###

live_loop :_drums do
  sample :drum_bass_hard
  sleep 1
  sample :drum_snare_soft
  sleep 1
  sample :drum_bass_hard
  sleep 0.5
  sample :drum_bass_hard
  sleep 0.5
  sample :drum_snare_soft
  sleep 1
end


live_loop :_hits_hats do
  6.times do
    sample :drum_cymbal_soft, amp:0.5 , rate: 1, compress:1
    sleep 0.5
  end
  sample :drum_cymbal_open, amp:0.5 , rate: 2
  sleep 0.5
  sample :drum_cymbal_open, amp:0.5 , rate: 2
  sleep 0.5
end



### BASS ###

live_loop :_bass_ do
  use_synth :saw
  use_synth_defaults attack: 0.1, delay: 0.1, sustain: 0.1, cutoff: 90, amp: 0.5
  play_pattern_timed [:c2, :c3, :e3, :e3],[1,1,1,1]
  
end


### RIFF ###

riff_01 = (ring :c2,:e3,:e2,:e2,:r,:c3,:r,:c3,
           :c2,:e3,:e2,:e2,:r,:r,:g3, :f3)

# pluck riff
live_loop :_riff_ do
  #stop
  use_synth :pluck
  use_synth_defaults amp: 0.8, attack: 0.0, attack_level: 0.5, sustain: 0.8, release: 0.25, cutoff: 80
  play_pattern_timed riff_01, 0.5
  
end

What are your proposals ?
Thanks !


#2

have a look at my answer for a similar question here


#3

Good evening,

I try your piece of code and one more time i’m absolutely confused with the sync system… Non Jeff t’es pas tout seul.

Of course your piece of code is working but sorry to have to say this it looks like magical playing with the “good” values of sleep and cross fingers to work…

I change your code to try to illustrate my tellings…

#experimental live_loop sequencer by Robin Newman, December 2018
#The program controls 4 percussion and 4 tune liveloops.
#Each has a duration of 2 beats (or less)
#each group has a series of 2 flags. The first list contains an identifier for each loop
#the second lists the sequence playing order. An x signifies a silent loop
#an enable function for each group, enables the loop trigger flat
#matching the current sequence position, and then issues a start cue :goT or :goP.
#These enable functions are called in the conduct live loop.
#This is delayed to start last, and so only send cues when the liveloops are waiting.
#Inside each loop, the rleevant trigger flag enables the content, or else a short sleep
#The sleep is less than 2 beats, ensuring the live_loop is ready for the next cue
#The "playing" liveloops are carefully set up so that they are completed in time
#for the next :goT or :goP cue, so all loops remain in sync.

use_bpm 60
#percussion loop control
pl=[:p1,:p1,:p1,:p2] #play list for percussion liveloops :x is blank
p=[:p1,:p2] #available percussion liveloops triggers
p.each do |x| #disable all initially
  set x,:false
end
set :x,false #blank percussion loop flag

define :enablep do |t|
  cv=pl[t % (pl.length)] #next flag to be checked
  p.length.times do |i| #cycle through all available percussion triggers and enable match
    if cv==p[i]
      set p[i],true
    else
      set p[i],false
    end
  end
  cue :goP #cue percussion live_loops
end

#uncomment next loop to get metronome for debugging
##| live_loop :test do
##|   sync :goT
##|   play 84,release: 0.05
##| end

#tune loop control
tl=[:t1,:t1,:t1,:t2]#[:tx,:t1,:tx,:t2,:tx,:t1] #play list for tune liveloops :tx is blank
tn=[:t1,:t2,:t3,:t4] #available tune liveloop triggers
tn.each do |x| #disable all intially
  set x,false
end
set :tx,false #blank tune loop flag
define :enablet do |t|
  cv=tl[t % (tl.length)] #next flag to be checked
  tn.length.times do |i| #cycle through all available tune triggers and enable match
    if cv==tn[i]
      set tn[i],true
    else
      set tn[i],false
    end
  end
  cue :goT #cue tune live_loops
end



live_loop :conduct,delay: 0.01 do
  tick
  enablep(look) #can comment out to stop all percussion loops
  enablet(look) #can comment out to stop all tune loops
  sleep 2
  
end

live_loop :percussion1 do
  sync :goP
  puts "p1"
  if get(:p1)==true
    # sample :loop_amen, beat_stretch: 2 # this "2 beats" works
    
    # no sleep so will be ready for next cue
    
    # THIS "2 beats" DO NOT WORK >>>> How can we explain to children that 2 beats is not equal to 2 beats with sleep...
    sample :drum_bass_soft
    sleep 1
    sample :drum_snare_soft
    sleep 1
    
  end
  sleep 1 #ready for next cue in plenty of time
end

live_loop :percussion2 do
  sync :goP
  #puts"p2"
  if get(:p2)==true
    sample :drum_cymbal_soft
    sleep 0.5
    sample :drum_cymbal_soft
    sleep 0.5
    sample :drum_cymbal_open
    sleep 1
  end
  sleep 1 #ready for next cue in plenty of time
end

live_loop :tune1 do
  use_synth :piano
  sync :goT
  if get(:t1)==true
    4.times do
      play scale(:c4,:major,num_octaves: 2).tick
      sleep 0.25
      # sleep 0.5 won't work because of sync system...
    end
  else
    sleep 1 #ready for next cue in plenty of time
  end
end


live_loop :tune2 do
  use_synth :piano
  sync :goT
  if get(:t2)==true
    4.times do
      play :c3, release: 0.5
      sleep 0.5
    end
    #no sleep so will be ready for next cue
  else
    sleep 1 #readn for nexrt cue in plenty of time
  end
end

Everybody is lost with this sync system even @Martin who tries hard to explain how it may work but has doubt with his drawing and nobody to confirm or answer his question.

Best regards.


#4

Hey @nlb

I’ve hopefully cleared up Martin’s question in the other thread.

With regard to your code illustration above, I’ll try to clarify it: the reason the sample :loop_amen, beat_stretch: 2 works is because beat_stretch: 2 does not actually cause the thread to sleep for that amount of time. (Rather, it tells the sample function to play sound for that long).
Thus, the :percussion1 thread, when using the beat_stretch example, only sleeps for 1 beat in total, since there is only a single sleep 1 in that instance.

One the other hand, when using the :drum_bass_soft/:drum_snare_soft example, if we count the two sleeps after each sample, plus the one at the very end of the live_loop, that’s a total of 3 beats that the thread’s loop duration lasts - so naturally it’s still doing things while the next :cue from :goP is sent - but it doesn’t ‘hear’ that because it’s still busy, not waiting at a sync.

Note, as I understand it, even if we reduce the sleep durations so that this loop’s duration is the same as the one we want to trigger it with (the conductor loop) - so that they are both 2 beats long, we will have the same problem. This I believe is what Sam’s explanation a while back refers to:

“…for threads syncing and queueing on the same logical time, their thread ID will determine whether they are before or after.”

(@samaaron can correct me if I’m wrong).

Does that make things any clearer? let us know. It’s really valuable to understand where the documentation can be improved etc.


#5

Hi @nlb

There are many ways to code things. Some are more
intuitive than others, some appeal to us more than others.

Example: I have problems thinking about rings… so, if you
are having difficulties with sync and cue… why not try using
the ‘at’ command… or perhaps ‘tick’ and ‘look’… these might
provide control structures you feel more relaxed with.

Eli…

Example of ‘at’

use_bpm 60
riff_01 = (ring :c2,:e3,:e2,:e2,:r,:c3,:r,:c3,
           :c2,:e3,:e2,:e2,:r,:r,:g3, :f3)

live_loop :beat4 do
  sleep 4
end


define :drums do
  at [0,8,12],
  [{:amp=> 1}] do |p|
    sample :drum_bass_hard
    sleep 1
    sample :drum_snare_soft
    sleep 1
    sample :drum_bass_hard
    sleep 0.5
    sample :drum_bass_hard
    sleep 0.5
    sample :drum_snare_soft
    sleep 1
  end
end

define :hat do
  at [0],
  [{:amp=>1}] do |p|
    6.times do
      sample :drum_cymbal_soft, amp:0.5 , rate: 1, compress:1
      sleep 0.5
    end
    sample :drum_cymbal_open, amp:0.5 , rate: 2
    sleep 0.5
    sample :drum_cymbal_open, amp:0.5 , rate: 2
    sleep 0.5
  end
end

define :bass do
  at [0],
  [{:amp=>1}] do |p|
    use_synth :saw
    use_synth_defaults attack: 0.1, delay: 0.1, sustain: 0.1, cutoff: 90, amp: 0.5
    play_pattern_timed [:c2, :c3, :e3, :e3],[1,1,1,1]
  end
end

define :riff do
  at [8],
  [{:amp=>1}] do |p|
    use_synth :pluck
    use_synth_defaults amp: 0.8, attack: 0.0, attack_level: 0.5, sustain: 0.8, release: 0.125, cutoff: 80
    play_pattern_timed riff_01, 0.125
  end
end



live_loop :do_song do
  # Either of these will produce the same sound. 
  #sync :beat4
  sleep 4
  hat
  drums
  bass
  riff
  
end

#6

Thanks for your explanation. it’s clearer even if it’s not “logical” if we compare to any audio software as Ableton.

Best regards.


#7

And the initial question is still alive :wink:

How to code the ableton simple scheme into sonic Pi ?


#8

Here is your orginal example with a conduct loop added. This uses cues to trigger each of your live loops. The trick is that this conduct loop is delayed from starting by a short time interval (I choose 0.01). Everything plays in time: the initial start of playing is just delayed by 0.01. This enrues that each sync is waiting for a cue BEFORE the cue is sent.

use_bpm 120

### DRUMS ###

live_loop :_drums do
  sync :cd
  sample :drum_bass_hard
  sleep 1
  sample :drum_snare_soft
  sleep 1
  sample :drum_bass_hard
  sleep 0.5
  sample :drum_bass_hard
  sleep 0.5
  sample :drum_snare_soft
  sleep 1
end


live_loop :_hits_hats do
  sync :cd #use same cue as for _drums
  6.times do
    sample :drum_cymbal_soft, amp:0.5 , rate: 1, compress:1
    sleep 0.5
  end
  sample :drum_cymbal_open, amp:0.5 , rate: 2
  sleep 0.5
  sample :drum_cymbal_open, amp:0.5 , rate: 2
  sleep 0.5
end



### BASS ###

live_loop :_bass_ do
  sync :cb
  use_synth :saw
  use_synth_defaults attack: 0.1, delay: 0.1, sustain: 0.1, cutoff: 90, amp: 0.5
  play_pattern_timed [:c2, :c3, :e3, :e3],[1,1,1,1]
  
end


### RIFF ###

riff_01 = (ring :c2,:e3,:e2,:e2,:r,:c3,:r,:c3,
           :c2,:e3,:e2,:e2,:r,:r,:g3, :f3)

# pluck riff
live_loop :_riff_ do
  sync :cr
  #stop
  use_synth :pluck
  use_synth_defaults amp: 0.8, attack: 0.0, attack_level: 0.5, sustain: 0.8, release: 0.25, cutoff: 80
  play_pattern_timed riff_01, 0.5
  
end

live_loop :conduct,delay: 0.01 do
  cue :cb #cue bass
  cue :cd #cue drums
  sleep 4
  cue :cb #cue bass
  sleep 4
  cue :cb #cue bass
  cue :cd #cue drums
  cue :cr #cue riff
  sleep 4
  cue :cb #cue bass
  cue :cd #cue drums
  sleep 4  
end

here is another example using the same logic

use_bpm 180
live_loop :drums do
  sync :cd
  sample :bd_haus,amp: 2
  sleep 2
  sample :drum_cymbal_closed,amp: 2
  sleep 1
  sample :drum_cymbal_closed,amp: 2
  sleep 1
end
live_loop :bass do
  sync :cb
  4.times do
    use_synth :blade
    n=(ring :c2, :e2, :f2,:g2).tick
    play n,release: 1
    sleep 1
  end
end
live_loop :riff do
  sync :cr
  use_synth :tb303
  n=(ring :c5,:g4,:a4,:e4,:d4,:e4,:g4,:f4,:e4,:d4)
  d=(ring 1,1,1,0.5,0.5,1,1,0.5,0.5,1)
  n.length.times do
    play n.tick,release: d.look
    sleep d.look
  end
end
live_loop :conduct,delay: 0.01 do
  cue :cb
  cue :cd
  sleep 4
  cue :cb
  sleep 4
  cue :cb
  cue :cd
  cue :cr
  sleep 4
  cue :cb
  cue :cd
  sleep 4  
end

#9

Bonsoir @robin.newman

Thank you for this code. This is exactly what i try to get !!!
I can now use it with childre which is one of Sonic Pi’s goal.

Best regards


#10

Hi @nlb,

well, if you want a construction which somehow matches the timeline in Ableton I would think you could go with something like this and skip the live_loops alltogether:

use_bpm 120

define :drums do
  in_thread do
    sample :drum_bass_hard
    sleep 1
    sample :drum_snare_soft
    sleep 1
    sample :drum_bass_hard
    sleep 0.5
    sample :drum_bass_hard
    sleep 0.5
    sample :drum_snare_soft
    sleep 1
  end
end

define :hats do
  in_thread do
    6.times do
      sample :drum_cymbal_soft, amp:0.5 , rate: 1, compress:1
      sleep 0.5
    end
    sample :drum_cymbal_open, amp:0.5 , rate: 2
    sleep 0.5
    sample :drum_cymbal_open, amp:0.5 , rate: 2
    sleep 0.5
  end
end

define :bass do
  in_thread do
    use_synth :saw
    use_synth_defaults attack: 0.1, delay: 0.1, sustain: 0.1, cutoff: 90, amp: 0.5
    play_pattern_timed [:c2, :c3, :e3, :e3],[1,1,1,1]
  end
end

define :riff do
  riff_01 = (ring
             :c2,:e3,:e2,:e2,:r,:c3,:r,:c3,
             :c2,:e3,:e2,:e2,:r,:r,:g3,:f3)
  in_thread do
    use_synth :pluck
    use_synth_defaults amp: 0.8, attack_level: 0.5, sustain: 0.8, release: 0.25, cutoff: 80
    play_pattern_timed riff_01, 0.5
  end
end

# play the tune
#loop do # of course, you could loop this...
  bass
  drums
  sleep 4
  
  bass
  sleep 4
  
  bass
  drums
  riff
  sleep 4
  
  bass
  drums
  sleep 4
#end

For the purpose of teaching (thought I do teach students and not (yet) children) I think the in_thread construct much more resembles something which comes close to tracks and a timeline as you can find these in contemporary DAWs (and also Ableton). I do visualise live_loops more like a bunch of rotating circles (rather than tracks following a timeline while playing), which might be interconnected by cue/sync.

Nevertheless I see the challange of being able to control musical events while working with live_loops and I am still experimenting and looking for different ways to do that (e. g. with fading independant of the runtime of a loop.). I do think similar as @Eli : you will have to figure out what works best for you.

[spoken aside] I don’t know Ableton enougth to go into detail but it might be quite difficult to try and apply the concept of live_loops to it. So I think we do have a different paradigm here and I might be tempted to think it is a bit like comparing apples and pears, when you comparing Sonic Pi with Ableton. Neverthess I do admit both are fruits and you can eat them… :wink: But seriously: I do e. g. encourage my students to compare what they know (which often is Ableton) with Sonic Pi because being clear about the differences - I think - also might give you new insights about what music is, how it works and how it can be done in different ways. But that’s more of a philosophical than a practical question whith respect to your original question.[/spoken aside]


#11

Hi Eli

Yes it’s a good idea to trigger some parts at precise number of beats.

What does this syntax mean ? Do we really need p because you don’t use it after ?


#12

Yeah… I was editing an old example, and was a bit sloppy.

That line was used to set the amplitude of the sample when
it was played.

Eli…

use_bpm 120
live_loop :beat4 do
  sleep 4
end


define :hat do
  at [0, 2, 3],
  [{:amp=>0.3}, {:amp=> 1}, {:amp=>0.5}] do |p|
    sample :drum_cymbal_pedal, p
  end
end

define :drum do
  at [1, rrand_i(2, 3)],
  [{:amp=>1}] do |p|
    sample [:drum_bass_hard, :drum_bass_soft].choose, p
  end
end



live_loop :do_hat do
  sync :beat4
  hat
  sleep 1
end

live_loop :do_drum do
  sync :beat4
  drum
  sleep 1
end