Thanks @chenghiz, please pardon that I have not yet looked at your code, as I’ve stumbled upon a different solution literally half an hour ago. In my solution the vibrato LFO runs freely, but I “burn” one of crow’s outputs for it. I wrote “burn” because I don’t actually patch something into that output, but just needed something I can attach an LFO action to.
My solution works like this:
• The vibrato LFO action is attached to the unused output 4, and
• the LFO voltage at output 4 is “sampled” at each stream event, and
• added to the pitch control voltage at input 1, which is also sampled at each stream event, and
• the sum of both samples is sent to output 2.
Although this works, it feels like “there must be a more elegant way, one that omits having to tie up a complete output for this”.
BTW: How do you make your code quote look so nicely formatted? Mine looks not as friendly, although I just pasted it over from Sublime Text editor, in which it looks nicely colorful.
--- adds vibrato to externally patched pitch CV
-- input 1: patch v/oct pitch CV into this
-- input 2: unused
-- output 1: pitch CV with vibrato
-- output 2: unused
-- output 3: unused
-- output 4: vibrato LFO
-- vibrato_freq: vibrato frequency in Hz
-- vibrato_level: vibrato level with 1 being ±5 Volt swing
function init()
input[1].mode( 'stream', 0.01 )
input[2].mode( 'stream', 0.01 )
end
vibrato_freq = 3.5
vibrato_level = 0.04
time = 1/vibrato_freq
level = vibrato_level/2
function mix_inputs_1_and_2_to_output_1()
output[1].volts = input[1].volts+output[4].volts
end
input[1].stream = mix_inputs_1_and_2_to_output_1
output[4].action = lfo( time, level, sine )
output[4]()
EDIT
@chenghiz, I believe the restart behavior you see is to be expected. According to the actions section in the script reference, this…
output[1](vibrato(.2, volts, .1))
…is a “shortcut to set the action and start immediately”, so each time a stream event happens, it will set the LFO action and (re)start it.
EDIT2: updated code formatting