I’ve found what appears to be a limitation in the :fm synth. It seems that both the carrier and the modulator have the same envelope. This means that (e.g.) during the release, as the volume of the modulating oscillator decreases the timbre of the sound changes as the volume does.
I had a go at getting around this by having an FM synth that’s full volume for its whole length, and then the envelope is added by automation of the amplitude. I then tried to add a LFO controlling FM depth. Which I mostly did but it stops just before the end of the sound. (I could make the LFO continue right to the end, but didn’t as this is just an experiment.)
Are there more straightforward ways that I could implement LFOs? And is there a better way of making timbre and volume independent with the :fm synth?
I’m not sure how far I can take this approach, because automating both a LFO and an envelope at the same time would be … tricky.
I was thinking that it would be not too difficult to make the :fm synth do everything I’d do with, say, FM8, except that there are fewer modulation choices. But, with this difficulty, I think that :fm is more restricted than I thought. Please correct me if I’m wrong.
This is what I was doing:
use_debug false
define :strings do |note,args={}|
defaults = { :attack => 0.5, :decay => 0.3, :sustain_level => 0.6,
:sustain => 1, :release => 0.5, :divisor => 1, :depth => 6,
:detune => 0.1, :amp => 0.3,
:lfo_phase => 0.25, :lfo_depth => 0 }
ag = defaults.merge( args )
time = ag[ :attack ] + ag[ :decay ] + ag[ :sustain ] + ag[ :release ]
use_synth :fm
use_synth_defaults ag
detune = ag[ :detune ];
syn = play [ note, note-detune, note+detune ], amp: 0, attack: 0, decay: 0, sustain: time, release: 0
envelope( syn, ag[ :attack ], ag[ :decay ], ag[ :sustain ],
ag[ :release ], ag[ :sustain_level ], ag[ :amp ] )
if ag[ :lfo_depth ] > 0
print "phase: ", ag[ :lfo_phase ]
lfo( syn, ag[ :lfo_phase ], ag[ :lfo_depth ], ag[ :depth ], time )
end
end
define :lfo do |syn,phase,depth,initial_depth, time|
index = 0
direction = "up"
control syn, depth_slide: phase/4.0
while index + phase < time
index = index + phase / 2.0
if direction == "up"
at index do
control syn, depth_slide: phase/2.0, depth: initial_depth + depth/2.0
end
direction = "down"
else
at index do
control syn, depth_slide: phase/2.0, depth: initial_depth - depth/2.0
end
direction = "up"
end
end
end
define :envelope do |syn,a,d,s,r,sl, amp|
control syn, amp_slide: a, amp: amp
at a do
control syn, amp_slide: d, amp: amp * sl
end
at a + d + s do
control syn, amp_slide: r, amp: 0
end
end
notes = (chord, :a3, 'm7', invert: 2 )
notes.each do |n|
strings n, sustain: 4, attack: 1, decay: 1, release: 1, lfo_depth: 4, lfo_phase: 0.5
end
EDIT: Sorry that I’m posting so much. I had a look into adding a new synth, and it seems that while I may not have programmed in supercollider for well over a decade, that I’ll be able to re-learn it.
I modified the sample synthdef from the tutorial material here, to make an extremely simple FM synthdef:
(
SynthDef(\testsynth,
{|note = 60, pan=0, amp = 1, ratio1=1, ratio2=2, depth1=7, depth2=0
carrier_decay=1, modulator1_decay=1000, modulator2_decay=1000, out_bus = 0 |
var freq = note.midicps;
var out = SinOsc.ar( freq +
SinOsc.ar( freq * ratio1 +
SinOsc.ar( freq * ratio2, 0, depth2 * freq * Line.kr( 1, 0, modulator2_decay )),
0, depth1 * freq * Line.kr( 1, 0, modulator1_decay)), 0, 0.5 );
var left = 0.5 - pan * 0.5;
var right = 0.5 + pan * 0.5;
Out.ar(out_bus,
[out * left,out * right]* Line.kr(1, 0, carrier_decay, amp, doneAction: 2))}
).writeDefFile("/Users/pieater/Sonic Pi/synthdefs") ;
)
which I can run from Sonic Pi.
use_bpm 120
load_synthdefs "/Users/pieater/Sonic Pi/synthdefs"
use_synth :testsynth
notes = (chord, :c4, 'm7', num_octaves: 3)
live_loop :bongs do
note = notes.choose
p = rrand( -1, 1 )
play note, amp: 0.5, pan: p, carrier_decay: 1,
depth1: 7, ratio1: 3.7, modulator1_decay: 2/3.0,
depth2: 3, ratio2: 11.1, modulator2_decay: 1/3.0
play note + 0.1, amp: 0.5, pan: p, carrier_decay: 1,
depth1: 7, ratio1: 3.7, modulator1_decay: 2/3.0,
depth2: 3, ratio2: 11.1, modulator2_decay: 1/3.0
sleep 0.25
end
which I think is promising. I do like the idea of being able to create things solely in Sonic Pi, but I think that to do what I want to do, I will have to go outside of the standard synths.