Smoothly fading samples and synths

Great technique, Martin.
Can use it for rate, pan, amp, release, … Opens up a whole new load of barely-music experiments :rofl:

#dueling blips

live_loop :panner do
  pann = (line -1, 1, inclusive: true, steps: 64).mirror
  vol = (line 0.1, 1, inclusive: true, steps: 32).mirror
  rat = (line 2, 4, inclusive: true, steps: 64).mirror
  
  2.times do
    if one_in(2) then
      sample :elec_blip, rate: rat.tick, pan: pann.tick, amp: 1 - vol.tick
    else
      sample :elec_blip2, rate: 6 - rat.tick, pan: pann.tick * -1, amp: vol.tick
    end
    sleep 0.2
  end
  
  with_fx :reverb, room: rrand(0.2, 0.75) do
    sample :bd_klub, amp: vol.look - 0.1 if one_in(4)
  end
  sample :bd_ada, amp: 0.2
end

Be careful about using tick in multiple places. tick is local to the thread not the ring. Rings do not have state and therefore don’t have any notion of the current index.

Hi,

I’d appreciate very much if someone could run my first to code examples (see top of this thread) and check if it does what I said it would (fade in/out smoothly). I did use this several times in other context and it worked (otherwise I would not have posted it :wink: ). Since a few weeks it doesn’t anymore and I wonder if there is something wrong with my installation (SP 3.1).

Cheers
Martin

A while ago, I found a way to fade individual tracks(live_loops) in and out.
I use a ramped line, with minimal maths involved:

I usually use individual live_loops for ‘tracks’, so I can just use the local tick.

PS:
@Martin Both code snippets work as expected.
I run SP 3.1 on Windows.

Hi @Davids-Music-Lab,

thanks very much for testing!

And yes, I also do use line or range (using the syntax (range 0,1,0.1).ramp). The thing is: I was looking for a solution which is not dependant on the time a live_loop runs: So in case I have a loop which has a sleep of 4 (see the :loop_amen example) I wanted a smooth fading and not a fade where the volume increases stepwise only every four beats. That’s why I choose this approach. So now I will have to find out why my SP doesn’t do it anymore … :frowning:

Nevertheless I will check out your tutorial (which I have not yet). So thanks for sharing!

So many great ideas here for fading! Thanks!

Has anyone experimented with something akin to fade groups? Particularly in a live-code context for quicker modifications of loops in progress. One thing I’d like to do is be able to target a given live_loop and apply a fade (or perhaps toggle a mute?) to it. For example:


# Apply a fade to only loops a and b, letting c play at full volume
apply_fade([:a, :b])

# Or alternately - only c and a are fading in and b is full volume.
# apply_fade([:c, :a])

live_loop :a do
  # play stuff
end

live_loop :b do
  # play stuff
end

live_loop :c do
  # play stuff
end

Here is a sample progarm which can control three separate loops in this way.
The code is almost identical for each loop section.
The function apply_fade lets you control each loop with one command. setting data to 1 fades loop on
setting data to 0 fades loop off.
You could put more than one loop inside each fx :level and they would be controlled together.
Fade time can be changed by changing amp_slide times
I used sample loops for teh loops but you can put anything you want in them. You could also use a metronome loop to sync them if required.

#Program controls fade up/down of three live_loops
#fade time 1 beat, but can be adjusted using slide times
#can be different for each loop if required

define :apply_fade do |flist,fvalue|
  flist.zip(fvalue).each do |f,v| # allows lists to be linked and corresponding values to be processed
    set f,v
  end
end

apply_fade([:La,:Lb,:Lc],[0,1,0]) #set fade values 1 on, 0 off

with_fx :level,amp: 1 do |va|
  in_thread do
    loop do
      control va,amp: get(:La),amp_slide: 1
      sleep 1
    end
  end
  
  live_loop :a do
    sample :loop_amen,beat_stretch:2
    sleep 2
  end
end

with_fx :level,amp: 1 do |vb|
  in_thread do
    loop do
      control vb,amp: get(:Lb),amp_slide: 1
      sleep 1
    end
  end
  
  live_loop :b do
    sample :loop_tabla,beat_stretch:8
    sleep 8
  end
end

with_fx :level,amp: 1 do |vc|
  in_thread do
    loop do
      control vc,amp: get(:Lc),amp_slide: 1
      sleep 1
    end
  end
  
  live_loop :c do
    sample  :loop_garzul,beat_stretch:8
    sleep 8
  end
end

Hi Martin,

Wow, this threads covered a lot of ground… So, heres a little
drum beat, with 2 different ‘backgrounds’, fading into each other
and providing a composite background…

Initially you only get the beat… If you take the stop out of either
background, you will hear it fade in and out.

If you take out both stop 's you get the mixed background

This isn’t really meant to teach anything to anyone… I
was just pottering around and it sort of wrote itself… :slight_smile:

Eli…

#Spiralysis
#by Eli...

use_bpm 120

bg1 = (ring 0.0,0.1,0.2,0.3,0.4,0.5,0.0,0.0).mirror
bg2 = (ring 0.5,0.4,0.3,0.2,0.1,0.0,0.0,0.0).mirror

melody_1 = [:c4,:r,:ds4,:ds4,:g4,:c4,:r,:gs4,:r,:r,:gs3,:r,:g3,:r,:r,:r].ring

live_loop :beats do
  sleep 1
end

live_loop :bar do
  sleep 4
end

live_loop :mixer do
  tick
  set :bvol1, bg1.look
  set :bvol2, bg2.look
  sync :bar
end

live_loop :kick, sync: :beats  do
  sample :bd_haus
  sleep 1.5
  sample :bd_haus
  sleep 1
  sample :bd_haus
  sample :drum_cymbal_closed
  sleep 1
  sample :bd_haus
  sample :drum_cymbal_closed
  sleep 0.5
end

live_loop :kick1, sync: :bar do
  sample :bd_ada
  sleep 1
  sample :bd_ada
  sample :sn_dolf, sustain: 0, release: 0.08, hpf: 80
  sleep 1
end

live_loop :tom, sync: :beats  do
  sample :drum_tom_lo_soft
  sleep 0.5
end

live_loop :snap, sync: :beats  do
  sleep 1
  if rand(1) < 0.75 then
    sample :perc_snap, amp: 0.5
  else
    sample :perc_snap2, amp: 0.5
  end
  sleep 1
end

with_fx :reverb, mix: 0.5, room: 0.9 do
  with_fx :ixi_techno, mix: 0.75, room: 0.9 do
    
    live_loop :background1, sync: :beats do
      
      stop
      
      this_vol = get :bvol1
      use_synth_defaults amp: this_vol * 4
      use_synth (ring :supersaw, :prophet, :prophet, :tech_saws).tick
      3.times do
        play :C4, cutoff: rrand(60,80)
        sleep 0.5
        play :G3, cutoff: rrand(60,90)
        sleep 0.5
        play :C4, cutoff: rrand(60,70)
        sleep 0.5
        play :Gs3, cutoff: rrand(60,80)
        sleep 0.25
        play :Gs3, cutoff: rrand(70,100)
        sleep 0.25
      end
      1.times do
        play :c4, cutoff: rrand(60,80), release: 1
        sleep 0.5
        play :g4, cutoff: rrand(60,90), release: 1
        sleep 0.5
        play :c4, cutoff: rrand(60,70), release: 1
        sleep 0.5
        play :gs3 + 7, cutoff: rrand(80,100), release: 1
        sleep 0.5
      end
    end
    
    live_loop :background2 do
      
      stop
      
      sync :beats if rand(1) < 0.5
      this_vol = get :bvol2
      
      use_synth_defaults cutoff: 70, release: 1.2, amp: this_vol * 4, pulse_width: rrand(0,1)
      use_synth [:subpulse, :pulse].choose
      if rand(1) < 0.75 then
        with_fx :ixi_techno, mix: 0.55, room: 0.85, damp: 0.8 do
          16.times do
            play melody_1.look - 12
            sleep 0.25
            tick
          end
        end
      else
        use_synth :prophet
        16.times do
          play melody_1.look - 12
          sleep 0.25
          tick
        end
      end
    end
  end
end

Hi @Eli,

yes, that’s an interesting idea to use a live_loop to set amp-values. The only thing which keeps me from doing that, is, that, to be applied, you depend on the runtime (the added sleeps) of the live_loop which calls these values. That is why I quite like the idea of amp_slide: It gives you more freedom to control your volume manipulation (or any other option) somehow independant from the time your life_loop needs to set another amp value:

Just imagine: you could have a synth melody changing every 0.25 compared to one with a release of 8. Actually you probably will not be able to fade the second one with ticking. It just takes to long. The other thing is, that using a ring and ticking through the values in most cases actually creates a stepwise fading. If you want to have it smoothly, you will need to apply amp_slide (either within the live_loops or by using set_mixer_control! for a whole buffer).

I updated my Sonic Pi Resources and tried to collect and comment different examples for fading (1,2).

here is my solution, I hope it helps for some.
greetings
www.rawlivecoding.com

fade_in = (line 0, 1, inclusive: true, steps: 10).ramp
fade_out = fade_in.reverse
isFading = true

live_loop :arpe do

  if(isFading)
    tick_reset
    isFading = false
  end

  arpe_gain = fade_out.tick
  print arpe_gain

  with_fx :reverb do |r|
    play 40, attack: 0.5, release: 3, amp: arpe_gain
    sleep 0.5
    control r, mix: 0.1
    play 35, attack:1, amp: arpe_gain
    sleep 0.5
    control r, mix: 0.9
    play 38, attack:1, amp: arpe_gain
    play 78, attack:1, amp: arpe_gain * 0.5, release:1, pan:-1
    sleep 1
    s = play 30, release:5, note_slide: 1, attack:1, amp: arpe_gain, pan:rrand(-1,1)
    sleep 0.5
  end
  sleep 1
end
3 Likes

Hi, I am looking forward to check this out later… Thanks!

Hi @selcukartut,

I like the idea of how you take care of reseting the tick to make sure you can switch between fading in an out.

Nevertheless, the fade time relies on the runtime of the live_loop and ultimately I would very much like to have a solution where I am independant of that (which probably involves a/the slide option). So my personal favorite in terms of functionality is Robin’s solution but in terms of simplicity I am still searching …

Very much thanks for sharing!

Hmmm… Hmmm…

I wonder… could you use the ‘at’ command to increment a
line.tick or a slide… it wouldnt then be worried about the loop
sleep time?

I’ll have to think on that some…

Eli…

EDIT: Ah! I wrote a whole song in at’s a long time back… it’s not what
you want, but could you mod it to fade in and out as part of the at’s ?

I even put in a fade_in line… but never really explored it further.

# Tune in a Box...
# Everything done in 1 Live_loop
# using 'at'.
# Eli...

use_bpm 90
bells = 0
clarinet = 0
clarinet_rhythm = (ring 0.25, 0.75, 1, 0.75, 0.25, 0.5, 0.5).shuffle
fade_in = (line 0, 3, inclusive: true, steps: 40).ramp




live_loop :everything do
  at (ring 2,4) do
    sample :drum_heavy_kick
  end
  
  at (range 0, 4, step: (ring 0.75, 1,1.25).choose) do
    sample :elec_hollow_kick, finish: 0.125, amp: 0.5
  end
  
  at (range 0.5, 4.5, step: 2) do
    if rand(1) < 0.6 then
      sample :perc_snap, rate: 0.75, start: 0.0, finish: 0.5, amp: 0.2
    else
      sample :perc_snap2, rate: 0.75, start: 0.0, finish: 0.5, amp: 0.15
    end
  end
  
  at (range 0, 4, step: 1) do
    sample :drum_cymbal_pedal, rate: 1, start: 0.0, finish: 0.5, amp: 0.4
  end
  
  at (rrand_i(2, 3)) do
    sample :drum_bass_soft , amp: 0.4
  end
  
  at (0) do
    if bells == 0 then
      256.times do
        bells += 1
        melody = [62,57,50,60,50,55,50,50,53,50,55,50,52,50,57,48,
                  62,57,48,60,48,55,48,48,53,48,55,48,52,48,57,45,
                  62,57,45,60,45,55,45,45,53,45,55,45,52,45,57,48,
                  62,57,48,60,48,55,48,48,53,48,55,48,52,48,57,50].ring
        
        melody_octave = [0,0,1,0,1,0,1,0,1,2,0,1,0,1,0,1,
                         0,0,1,0,1,0,1,0,1,2,0,1,0,1,0,1,
                         0,0,1,0,1,0,1,0,1,2,0,1,0,1,0,1,
                         0,0,1,0,1,0,1,0,1,2,0,1,0,1,0,1].ring
        use_synth :pretty_bell
        play melody.look-(12*melody_octave.look), amp: 0.1#, release: [0.75, 0.5, 0.25].choose
        sleep 0.25
        tick
      end
    else
      if bells == 256 then
        bells = 0
      end
    end
  end
  
  at (3) do
    if clarinet == 0 then
      8.times do
        clarinet += 1
        use_synth :fm
        if rand(1) < 0.8 then
          clarinet_line = (ring :d3, :e3, :d3,:a3, :d3, :g3, :a3, :e4)
        else
          clarinet_line = (ring :d3, :e3, :d3,:a3, :d3, :g3, :a3, :e4).shuffle
        end
        use_synth_defaults divisor: 0.5, depth: 4, attack: 0.05, sustain: 0.3, release: 0.2, amp: 0.2
        with_fx :reverb, room: 0.75, damp: 0.25 do
          play clarinet_line.look
        end
        sleep 2 * clarinet_rhythm.look
        tick
      end
      if clarinet == 8 then
        clarinet = 0
      end
    end
  end
  sleep 1
end
1 Like

That’s so cool! I was looking for a way to do this!

Weird question what is the purpose of the .tick(:V)?

Ruby syntax / sonic pi continues to surprise me.

I’m going to make a noob post at some point to share everything I’ve learned for new people to sonic pi.

@din: See chapter 9.4 of the tutorial, particularly the last section titled ‘Naming Ticks’ :slight_smile:
https://sonic-pi.net/tutorial#section-9-4

:bowing_man:

You can name ticks!!!

test = (ring 1,2,3,4)
test1 = (ring 1,2,3,4)
test2 = (ring 1,2,3,4)

live_loop :test do
  puts test.tick
  puts test1.tick
  puts test2.tick
  sleep 1
end

test = (ring 1,2,3,4)
test1 = (ring 1,2,3,4)
test2 = (ring 1,2,3,4)

live_loop :test2 do
  puts test.tick(:a)
  puts test1.tick(:b)
  puts test2.tick(:c)
  sleep 1
end

This whole time I’ve thought that tick was some how associated as a function of a ring not determined on some magical property of the live_loop. Explains why tick for me acted weird sometimes. Thank you for the reply.

1 Like

You’re welcome :grinning_face_with_smiling_eyes:
(Also, looking forward to reading about your Sonic Pi newcomer learnings! :+1: )

I used to use “tick” as a reference to get a position in time (?) - but, realised that you set the “tick” in a live_loop, by just stating “tick” - then, if you want to know what the value for tick is, you use “look”. If you use “tick” again, it counts up the tick value (If I understand correctly!)

So, I use something like :


myArray = [1,2,3,4].ring

live_loop :test do
  tick
  puts myArray.look  
  sleep 1
end

I’m loving the naming ticks though… that’s quite amazing (I tried replacing it with “look” and it doesn’t work)

myRing = (ring 1,2,3,4)

live_loop :foo do
  
  tick
  puts myRing.look
  sleep 1
  
end

another way to write the same thing :slight_smile: