G-Funk Whistle Synth

Hi Guys!
Lately I’ve been trying to create this whistle sound used a lot in 90’s West Coast Hip Hop, as explained in this video: https://www.youtube.com/watch?v=_oiIwEWEFcs
Here the portamento part seems to be essential, but I don’t know how to reproduce this in sonic pi.
I thought note_slide could work, but it doesn’t seem to have an effect.
It would be great if someone could help me out!

use_bpm 90
use_synth :saw
use_synth_defaults note_slide: 5
play 90, sustain: 0.4
sleep 0.4
play 100, sustain: 0.4
sleep 0.4
play 90, sustain: 0.4

Hi @thehiddenfortress,

I don’t think that you will get a real portamento with this (or currently any) synth in Sonic Pi. The thing is that you will have to control a running synth, so actually you are not playing separate synths with separate notes but just one where you control the note (and/or other) values. I hope that’s an understandable (and technically at least sufficiently correct) explanation.

Having said that, you can do something like that:

use_bpm 90

live_loop :beat do
  sleep 1
end

live_loop :whistle, sync: :beat do
  s = synth :saw, note: :c4, release: 6
  sleep 1
  control s, note: :c5, note_slide: 0.25
  sleep 7
end

live_loop :drums, sync: :beat do
  sample :loop_amen, beat_stretch: 4
  sleep 4
end

Does this resemble to what you were planning to do?

Hi @Martin,
this is exactly what I was looking for!
Thank you so much!

1 Like

I also had a bit of a play around, and came up with a function to control it:

use_bpm 90

define :whistle do |notes, durs, slide=0.1|
  len = durs.reduce(:+)
  s = play notes[0], sustain: len, note_slide: slide
  sleep durs[0]
  notes.zip(durs)[1..-1].each do |n, d|
    control s, note: n
    sleep d
  end
end

use_synth :saw

whistle [:c6, :g6, :f6, :ds6, :d6, :ds6, :d6, :c6, :as5, :c6],
  [0.5, 0.5, 1, 0.5, 0.25, 0.25, 0.25, 0.25, 0.5, 1]

Note that it does use some Ruby things that are not strictly supported by Sonic Pi (like zip and reduce), so it might not continue working in future versions (but could be adapted in that case).

2 Likes

I was working towards this too, based on Martins example,
but Emlyn simply out-classed us both… I will be using his code
myself. :slight_smile:

Eli…

notes = (ring :d5, :e5, :d5,:a5, :d5, :g5, :a5, :e5)
durations = (ring 1)
slides = (ring, 0.5, 0.5, 0.5, 0.75, 0.5, 1, 0.5)
use_bpm 90

live_loop :beat do
  sleep 1
end

live_loop :whistle do
  s = synth :saw, note: notes.tick, release: 6
  sleep 1
  offset = [-12, 12].choose
  control s, note: notes.look+offset, note_slide: slides.look
  sleep durations.look
end

live_loop :drums, sync: :beat do
  sample :loop_amen, beat_stretch: 4
  sleep 4
end
1 Like

Ah yes, very elegant and exactly what was needed!

1 Like

Which reminds me of a solution I wrote a while ago (slightly ‘refactored’) when I needed a sliding sound. It does not slide each note into the next one but allows an optional sliding note for each note you want to play (each note creates a new synth):

use_synth :dsaw

# score => [note, sustain, release, amp, slide_note, slide_time]
define :play_synth do | score |
  score.each do | n |
    
    if n[0] != :r # if not a rest
      if n[4] == nil
        play n[0], sustain: n[1], release: n[2], amp: n[3]
      elsif n != :r
        control (play n[0], note_slide: n[5], sustain: n[1], release: n[2]), amp: n[3], note: n[4]
      end
    end
    
    sleep 1
  end
end

the_score = (ring
             [:c, 0.5, 0.5, 1, nil, nil],
             [:r],
             [:eb, 0.25, 0.5, 1.25, :g, 0.125],
             [:g, 0.25, 0.5, 1, :bb, 0.125],
             [:g, 0.25, 0.5, 1, :eb, 0.125])

live_loop :test do
  play_synth the_score
end
1 Like

Ok. I must admit, I could not stop thinking about this… mainly because I just checked my last code snippet first it does not rearly do what I want it do and secondly and quite franky: it is a mess :wink: I was lucky that it did something meaningful at all.

So here is another attempt to correct myself:

use_bpm 120
use_synth :saw

# score => [note, attack, sustain, release, amp, slide_note, slide_at (0..1)]
define :play_synth do | score |
  score.each do | n |
    
    dur = (n[1] + n[2] + n[3]) # sustain + release
    
    if n[0] != :r # if not a rest
      
      if n[5] == nil # no rest but also no slide_note
        play n[0], attack: n[1], sustain: n[2], release: n[3], amp: n[4]
        sleep dur
        
      else # slide note given
        
        slide_len = dur * n[6].round(2) # time to slide
        slide_at = dur - slide_len # time when to start sliding
        
        s = synth :saw, note: n[0], attack: n[1], sustain: n[2], release: n[3], amp: n[4]
        sleep slide_at # sleep until it's time for sliding
        control s, note: n[5], note_slide: slide_len
        sleep slide_len
      end
    else # just rest
      sleep dur
    end
  end
end

the_score = (ring
             [:c, 0.1, 2, 0.1, 1, :g, 0.5],
             [:g, 0.1, 2, 0.1, 1, nil, nil],
             [:r, 0.1, 2, 0.1],
             [:bb, 0.1, 1, 0.1, 1, :a, 0.75],
             [:a, 0.1, 0.5, 0.1, 1, :c, 0.125]
             )

live_loop :test do
  play_synth the_score
end

I admit, the data to be fed into the function is quite complex but - on the other hand - you thus have a few options to be musically expressive.

Nice! One little thing that you might find useful: you can “destructure” the n array into individual variables to improve readability:

define :play_synth do | score |
  score.each do | note, attack, sustain, release, amp, note2, slide |
    
    dur = (attack + sustain + release)
    
    if note != :r # if not a rest
      
      if note2 == nil # no rest but also no slide_note
        play note, attack: attack, sustain: sustain, release: release, amp: amp
        sleep dur
        
      else # slide note given
        
        slide_len = dur * slide.round(2) # time to slide
        slide_at = dur - slide_len # time when to start sliding
        
        s = synth :saw, note: note, attack: attack, sustain: sustain, release: release, amp: amp
        sleep slide_at # sleep until it's time for sliding
        control s, note: note2, note_slide: slide_len
        sleep slide_len
      end
    else # just rest
      sleep dur
    end
  end
end
2 Likes

Yes. A very healthy thing to do!