# Combining oscillators with trigger\_synth

**URL:** <https://in-thread.sonic-pi.net/t/combining-oscillators-with-trigger-synth/4300>\
**Category:** Creations & Ideas\
**Created:** [August 16, 2020, 8:54pm UTC](https://in-thread.sonic-pi.net/t/combining-oscillators-with-trigger-synth/4300 "2020-08-16T20:54:33Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![theibbster](https://avatars.discourse-cdn.com/v4/letter/t/da6949/32.png) [@theibbster](https://in-thread.sonic-pi.net/u/theibbster)\
**Post date:** [August 16, 2020, 8:54pm UTC](https://in-thread.sonic-pi.net/t/combining-oscillators-with-trigger-synth/4300/1 "2020-08-16T20:54:33Z")

</div>

I’ve been learning music theory and got to a section on harmonics. I wanted to play around with this in sonic pi. My first solution was based on [some codes by martin](https://github.com/mbutz/sonicpi-leuphana-ws1718/blob/master/session-02/additive-synthesis.rb):

```ruby
def play_harmonic(fundamental, amps, *args)
  use_synth :sine
  partials = [fundamental,
              fundamental * 2,
              fundamental * 3,
              fundamental * 4,
              fundamental * 5,
              fundamental * 6,
              fundamental * 7,
              fundamental * 8].map{|hz| hz_to_midi(hz) }
  
  partials.each_with_index do |note, index|
    play note, *args
  end
end

sine = [1,0,0,0,0,0,0,0]
violin = [1,0.8,0.5,0.25,0.35,0.35,0.5,0.5]
flute = [1,0.5,0.8,0.35,0.25,0.1525,0.125,0.255]
trumpet = [0.96,0.94,1,0.75,0.73,0.75,0.48,0.5]
guitar = [1,0.75,0.65,0.65,0.7,0.75,0.6,0.8]

play_harmonic 100, flute, release: 3

```

However after hearing some delay with this implementation, and because I fancied playing around with some code, I decided to try to implement it in a similar way to how sonic-pi implements `play_chord` (you can find this [here](https://github.com/sonic-pi-net/sonic-pi/blob/5dd2edd25e141ed6a076f2430bdecc71c44f3f25/app/server/ruby/lib/sonicpi/lang/sound.rb)). Here’s what I’ve ended up with:

```ruby
def play_harmonic(fundamental, amps, *args)
  sn = :sine.to_sym
  info = Synths::SynthInfo.get_info(sn)
  args_h = resolve_synth_opts_hash_or_array(args)
  args_h = normalise_and_resolve_synth_args(args_h, info, true)
  
  partials = [fundamental,
              fundamental * 2,
              fundamental * 3,
              fundamental * 4,
              fundamental * 5,
              fundamental * 6,
              fundamental * 7,
              fundamental * 8].map{|hz| hz_to_midi(hz) }
  
  unless __system_thread_locals.get(:sonic_pi_spider_real_time_mode)
    if __thread_locals.get(:sonic_pi_mod_sound_timing_guarantees)
      unless in_good_time?
        __delayed_message "!! Out of time, skipping: synth #{sn.inspect}, #{arg_h_pp({note: notes}.merge(args_h))}"
        return BlankNode.new(args_h)
      end
    end
  end
  
  unless __thread_locals.get(:sonic_pi_mod_sound_synth_silent)
    __delayed_message "synth #{sn.inspect}, #{arg_h_pp({note: partials}.merge(args_h))}"
  end
  
  chord_group = @mod_sound_studio.new_group(:tail, current_group, "CHORD")
  cg = ChordGroup.new(chord_group, partials, info)
  
  nodes = []
  amp = args_h[:amp] || 1.0
  partials.each_with_index do |note, index|
    if note
      args_h[:note] = note
      args_h[:amp] = amp * amps[index]
      nodes << trigger_synth(:sine, args_h.dup, cg, info)
    end
  end
  cg.sub_nodes = nodes
  __thread_locals.set(:sonic_pi_local_last_triggered_node, cg)
  cg
end

sine = [1,0,0,0,0,0,0,0]
violin = [1,0.8,0.5,0.25,0.35,0.35,0.5,0.5]
flute = [1,0.5,0.8,0.35,0.25,0.1525,0.125,0.255]
trumpet = [0.96,0.94,1,0.75,0.73,0.75,0.48,0.5]
guitar = [1,0.75,0.65,0.65,0.7,0.75,0.6,0.8]

play_harmonic 100, flute, release: 3

```

There is a potential here for a bit of further abstraction here too. Maybe a `trigger_amped_chords` that allows the playing of midi/notes simultaneously but at different volumes.

Figured I’d share, could be interesting for others 🙂

---

<div class="post-metadata">

**Author:** ![ethancrawford](https://avatars.discourse-cdn.com/v4/letter/e/a88e4f/32.png) [@ethancrawford](https://in-thread.sonic-pi.net/u/ethancrawford)\
**Post date:** [August 17, 2020, 1:32am UTC](https://in-thread.sonic-pi.net/t/combining-oscillators-with-trigger-synth/4300/2 "2020-08-17T01:32:56Z")

</div>

Interesting that you mention you hear some delay. How so? Does it happen when using the first example above exactly?

(Btw, it doesn’t seem to be using `amps` - in order to do so I needed to change the implementation of `play_harmonic` slightly) -

```ruby
def play_harmonic(fundamental, amps, args)
  use_synth :sine
  partials = [fundamental,
              fundamental * 2,
              fundamental * 3,
              fundamental * 4,
              fundamental * 5,
              fundamental * 6,
              fundamental * 7,
              fundamental * 8].map{|hz| hz_to_midi(hz) }
  
  partials.each_with_index do |note, index|
    play note, args.merge(amp: amps[index])
  end
end

```

---

<div class="post-metadata">

**Author:** ![samaaron](https://dub1.discourse-cdn.com/flex017/user_avatar/in-thread.sonic-pi.net/samaaron/32/7_2.png) [@samaaron](https://in-thread.sonic-pi.net/u/samaaron)\
**Post date:** [August 17, 2020, 9:42am UTC](https://in-thread.sonic-pi.net/t/combining-oscillators-with-trigger-synth/4300/3 "2020-08-17T09:42:48Z")

</div>

Hi there @theibbster, welcome to our forums!

I’m also like @ethancrawford and am curious as to what delays you were hearing. Everything is scheduled ahead of time with the same timestamp so I’d be very surprised to hear that there were delays. Could you explain a little more about the differences between what you expected and what you observed?

Also, whilst playing with and copying the internal code is definitely to be encouraged for learning, it’s not code that we would normally want to write directly in Sonic Pi for compositions or performances.

The main issue is that it’s not supported, and all those internal calls may (and likely will) completely change without warning. This includes calls to `def` - instead prefer `define`.

Glad you’re experimenting and having fun though 🙂

---

<div class="post-metadata">

**Author:** ![theibbster](https://avatars.discourse-cdn.com/v4/letter/t/da6949/32.png) [@theibbster](https://in-thread.sonic-pi.net/u/theibbster)\
**Post date:** [August 17, 2020, 12:16pm UTC](https://in-thread.sonic-pi.net/t/combining-oscillators-with-trigger-synth/4300/4 "2020-08-17T12:16:41Z")

</div>

I must’ve made a mistake with the code, when I went through now and made sure everything was right they both sounded the same.

@samaaron Thanks for the welcome! I wouldn’t use it for compositions or anything was just enjoying messing around. I was happily surprised it didn’t all go over my head! Maybe after some time I’d even be able to contribute to the code 😃

@ethancrawford yeah, I made a mistake with the code I posted ~~, however it seems your merge doesn’t work either, as `args` is an array with a hash inside like this `[{:sustain=>1, :release=>3}]`.~~

**Edit:** my bad, code has been updated

So keeping that in mind, and in case anyone wants to use similar code for additive synthesis or playing chords with each note having it’s own amplitude, here’s what I ended up with:

```ruby
define :play_harmonic do |fundamental, amps, args|
  use_synth :sine
  partials = [fundamental,
              fundamental * 2,
              fundamental * 3,
              fundamental * 4,
              fundamental * 5,
              fundamental * 6,
              fundamental * 7,
              fundamental * 8].map{|hz| hz_to_midi(hz) }
  
  partials.each_with_index do |note, index|
    play note, args.merge(amp: amps[index])
  end
end

```

I switched to `define` as well in keeping with what sam said so it could be used for composition or performance.

---

<div class="post-metadata">

**Author:** ![ethancrawford](https://avatars.discourse-cdn.com/v4/letter/e/a88e4f/32.png) [@ethancrawford](https://in-thread.sonic-pi.net/u/ethancrawford)\
**Post date:** [August 17, 2020, 12:31pm UTC](https://in-thread.sonic-pi.net/t/combining-oscillators-with-trigger-synth/4300/5 "2020-08-17T12:31:09Z")

</div>

Sure 🙂 that works too.  
(I’d gone for not using splats for `args` in mine).
