Herbie Hancock's Chameleon bass sound

Hello everyone, how are you doing?

I just started with Sonic Pi about a week ago, and the first thing I did was to start coding the classic Herbie Hancock’s Chameleon song to play around with it. So I wrote the bassline, the first part of the melody and structured them in live_loops and learned how to get it all synced.
But before I continued with the song, I wanted to try to get as close as possible do that delicious synth bass sound from the original song. With zero knowledge in sound synthesis practice and new to SP, I did a lot of grinding through the tutorial, some videos showing how to create the sound on physical synths and videos explaining how synths actually work to try to understand and put it all together.

So this is where I got, I think I’m in the right direction here, but kinda hit a wall and have no further idea how to get closer to the original sound. Also, the log is giving warnings about running behind time, although I can’t really hear any timing problem.
But the biggest problem is that once the code is running, if I hit run again, I loose part of the effect and only get the heavy filter effect, so I can’t do any live coding with it, only let it run…

I hope you enjoy the sound and if anyone can give me some tips on how to improve it further and also to solve these problems I mentioned, I will highly appreciate.

# Herie Hancock Chameleon bass synth experiment
# Pedro Cambier

use_bpm 95

# main bass line
bassline = [:r, :g1, :ab1, :a1, :bb1, :ab2, :bb2, :c2, :db2, :d2, :eb2, :bb2, :db3].ring
# bass sleep time between notes
basstimes = [0.5, 0.5,0.5,0.5,0.75,0.75,1,0.5,0.5,0.5,0.75,0.75,0.5].ring
# bass notes durations
bassdurs = [0.5, 0.5, 0.5, 0.5, 0.3, 0.3, 0.3, 0.5, 0.5, 0.5, 0.3,0.3, 0.3].ring

# min frequency hpf
min_cutoff = 35
# release time
rel = 0.1
# attack lpf value
attack_lpf = 95
# lpf value after filter slides in (initial value, updated with notes values)
base_lpf = 40
# main synth amp value
synth_amp = 0.125
# short attack value
attack = 0.05

with_fx :distortion, distort:0.7 do # Trying to add some dirt with distorion
  with_fx :hpf ,cutoff:min_cutoff do # hpf to cut out the really low freqs to try to dry it out a little
    with_fx :rlpf, res:0.3 do |fx| # Start with low res value, will rise when filter slides in
      live_loop :herbiebass do
        tick
        sleeptime = basstimes.look
        # Check if its a rest note
        if !rest? bassline.look
          # lpf frequency is the current note
          base_lpf = bassline.look
          # Update sleep time
          sleeptime = sleeptime - attack
          # Get filter back down before calling synth
          control fx, cutoff: base_lpf+ rrand(0,5)
          synth :saw, note:bassline.look, detune:0.05, attack:attack, release:rel, decay:bassdurs.look-rel, sustain:0, amp: synth_amp, cutoff:attack_lpf
          # Slide filter up with ressonance in short attack time
          control fx, cutoff: attack_lpf, cutoff_slide:attack, res: 0.7, res_slide:attack
          # After attack time, slide filter back down within note duration time
          sleep attack
          control fx, cutoff: base_lpf + rrand(0,5), cutoff_slide:bassdurs.look, res:0.3, res_slide:bassdurs.look
        end
        sleep sleeptime
      end
    end
  end
end
2 Likes

Hmmm… moving the fx inside the loop seems to be the answer…

Perhaps set: and get: might also be a working solution?

with_fx :distortion, distort:0.7 do # Trying to add some dirt with distorion
  with_fx :hpf ,cutoff:min_cutoff do # hpf to cut out the really low freqs to try to dry it out a little
    
    live_loop :herbiebass do
      with_fx :rlpf, res:0.3 do |fx| # Start with low res value, will rise when filter slides in
        tick
        sleeptime = basstimes.look
        # Check if its a rest note
        if !rest? bassline.look
          # lpf frequency is the current note
          base_lpf = bassline.look
          # Update sleep time
          sleeptime = sleeptime - attack
          # Get filter back down before calling synth
          control fx, cutoff: base_lpf+ rrand(0,5)
          synth :saw, note:bassline.look, detune:0.05, attack:attack, release:rel, decay:bassdurs.look-rel, sustain:0, amp: synth_amp, cutoff:attack_lpf
          # Slide filter up with ressonance in short attack time
          control fx, cutoff: attack_lpf, cutoff_slide:attack, res: 0.7, res_slide:attack
          # After attack time, slide filter back down within note duration time
          sleep attack
          control fx, cutoff: base_lpf + rrand(0,5), cutoff_slide:bassdurs.look, res:0.3, res_slide:bassdurs.look
        end
        sleep sleeptime
      end
    end
  end
end

Eli…

Hello Eli, thank you for your answer.
Indeed this worked, I still don’t really understand why, but this way solves the main problem! I’ll think about how to use set/get also…
I read on the tutorial that it wasn’t really good to put fx inside loops because of efficiency problems, do you think this could be an issue after having all the song parts looping together?

Its probably something to do with the control having to be inside the same loop as what it
is controlling. And yes, normally putting high-cost fx’s such as echo outside of loops is preferred,
especially when the loop has a low sleep time… but every rule has its exception I guess, and I
dont think a filter such as :rplf is really high cost.

Please continue to work on this, I was a huge Herbie fan back in the day. :slight_smile:

Eli…

Oh cool, I didn’t realize that some fx were less costly than others, good to know!

I will continue, I love this song too much =) but probably will take some time as I’m just starting out and taking this step by step and trying to learn as much as possible from each aspect =)

Thanks a lot!

ACTUALLY THIS STILL HAS PROBLEMS. WILL WORK FURTHER ON IT.

EDIT amended code below to make sure that :flag has been initialised on first run
I think this does what you want whilst still keeping the fx outside the live_loop. The thing is to save the reference to the effect in the time-state but only on the first run. subsequent runs still use the initial fx wrapper. I use the defonce function to achieve this.
With this you can rerun without upsetting things.

# Herie Hancock Chameleon bass synth experiment
# Pedro Cambier

use_bpm 95

# main bass line
bassline = [:r, :g1, :ab1, :a1, :bb1, :ab2, :bb2, :c2, :db2, :d2, :eb2, :bb2, :db3].ring
# bass sleep time between notes
basstimes = [0.5, 0.5,0.5,0.5,0.75,0.75,1,0.5,0.5,0.5,0.75,0.75,0.5].ring
# bass notes durations
bassdurs = [0.5, 0.5, 0.5, 0.5, 0.3, 0.3, 0.3, 0.5, 0.5, 0.5, 0.3,0.3, 0.3].ring

# min frequency hpf
min_cutoff = 35
# release time
rel = 0.1
# attack lpf value
attack_lpf = 95
# lpf value after filter slides in (initial value, updated with notes values)
base_lpf = 40
# main synth amp value
synth_amp = 0.125
# short attack value
attack = 0.05

defonce :flagsetup do
  #:flag is only set to true the first time flagsetup is run
  #will be called again after stop is pressed followed by run again NOT TRUE NEEDS AMENDING
  puts "run defonce :flagsetup"
  set :flag,true
end

flagsetup
while get(:flag)==nil do #make sure that :flag has been initialised
   sleep 0.1
end
  
  
  
  with_fx :distortion, distort:0.7 do # Trying to add some dirt with distorion
    with_fx :hpf ,cutoff:min_cutoff do # hpf to cut out the really low freqs to try to dry it out a little
      with_fx :rlpf, res:0.3 do |fx| # Start with low res value, will rise when filter slides in
        set :fxp,fx if get(:flag) #store reference fx only once
        set :flag,false #set :flag to false so that :fxp is NOT set again
        live_loop :herbiebass do
          tick
          sleeptime = basstimes.look
          # Check if its a rest note
          if !rest? bassline.look
            # lpf frequency is the current note
            base_lpf = bassline.look
            # Update sleep time
            sleeptime = sleeptime - attack
            # Get filter back down before calling synth
            #use get(:fxp) to retrieve pointer fx
            control get(:fxp), cutoff: base_lpf+ rrand(0,5)
            synth :saw, note:bassline.look, detune:0.05, attack:attack, release:rel, decay:bassdurs.look-rel, sustain:0, amp: synth_amp, cutoff:attack_lpf
            # Slide filter up with ressonance in short attack time
            control get(:fxp), cutoff: attack_lpf, cutoff_slide:attack, res: 0.7, res_slide:attack
            # After attack time, slide filter back down within note duration time
            sleep attack
            control get(:fxp), cutoff: base_lpf + rrand(0,5), cutoff_slide:bassdurs.look, res:0.3, res_slide:bassdurs.look
          end
          sleep sleeptime
        end
      end
    end
  end
1 Like

Thanks a lot Robin, I’m gonna look into this defonce, hadn’t heard of it before…

It won’t help directly. What is needed is a similar function that will respond once when a fresh run is starting but not to subsequent runs until stop has first been pressed. Defonce responds first time ok but then not again until SP is rebooted

Yes, I just noted that, but it’s already one step forward and it’s always good to learn new functions.

Pedro…

I helped a little. but now you’ve caught the attention of
the real maestro’s on the forum. :slight_smile:

I’m sure it will get sorted out.

Regards,

Eli…

I think I finally found a way around this. After doing some searching I found out about the live_loop parameter, which can be initialized when the loop is created, but since the loops aren’t re-created on reruns, the parameter passed from the live_loop doesn’t get initialized on reruns, only when the run stops and starts again, so I did this, and it seems to be worrking now.

with_fx :distortion, distort:0.7 do # Trying to add some dirt with distorion
  with_fx :hpf ,cutoff:min_cutoff do # hpf to cut out the really low freqs to try to dry it out a little
    with_fx :rlpf, res:0.3 do |fx| # Start with low res value, will rise when filter slides in
      live_loop :herbiebass, init: true do |flag|
        set :fxp, fx if flag
        tick
        sleeptime = basstimes.look
        # Check if its a rest note
        if !rest? bassline.look
          # lpf frequency is the current note
          base_lpf = bassline.look
          # Update sleep time
          sleeptime = sleeptime - attack
          # Get filter back down before calling synth
          #use get(:fxp) to retrieve pointer fx
          control get(:fxp), cutoff: base_lpf + rrand(0,5)
          synth :saw, note:bassline.look, detune:0.05, attack:attack, release:rel, decay:bassdurs.look-rel, sustain:0, amp: synth_amp, cutoff:attack_lpf
          # Slide filter up with ressonance in short attack time
          control get(:fxp), cutoff: attack_lpf, cutoff_slide:attack, res: 0.7, res_slide:attack
          # After attack time, slide filter back down within note duration time
          sleep attack
          control get(:fxp), cutoff: base_lpf + rrand(0,5), cutoff_slide:bassdurs.look, res:0.3, res_slide:bassdurs.look
        end
        sleep sleeptime
        flag = false
      end
    end
  end
end
1 Like

Thanks a lot Eli, you did help a lot!
=)