-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcmss-tremolo.lua
65 lines (59 loc) · 2.87 KB
/
cmss-tremolo.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
--- cmss-tremolo - tremolo prototype/collaboration
-- tremolo as in amplitude modulation, not vibrato (pitch modulation)
-- pb. the colorado modular society, sine mountain and patchogram
-- https://github.com/pchuck/monome-crow
--
-- A configurable parametric tremolo effect/envelope generator
--
-- in1: gate
-- in2: v/o pitch
-- out1: tremolo oscillation/decay envelope 1
-- out2: tremolo oscillation/decay envelope 2 (phase shifted)
-- out3: tremolo oscillation/decay envelope 3
-- out4: tremolo oscillation/decay envelope 4 (phase shifted)
--
-- constants
local M_PERIOD = 0.001 -- time() interval in seconds
local V_THRESH = 1.0 -- trigger threshold in volts
local V_HYST = 0.1 -- hysteresis voltage
local TRIG = 'rising' -- trigger condition
local GATE = 1 ; PITCH = 2 -- inputs - logical ids of the inputs
local OUTS = { 1, 2, 3, 4 } -- outputs - logical ids of the lfo outputs
local V_MIN = 1.0 -- min output voltage
local V_MAX = 8.0 -- max output voltage
-- public/configurable parameters
public.add( 'rate', 50, { 1, 100}) -- osc rate (# div per tap/clock period)
public.add('accel', 1.1, { 1.0, 1.5}) -- time compression per tremolo interval
public.add('decay', -0.8, {-3.0, 1.0}) -- voltage decay per tremolo interval
public.add('shape', 'sin', -- envelope shape
{ 'lin', 'sin', 'log', 'exp', 'over', 'under', 'rebound'})
-- initialization - setup the callbacks for reacting to triggers/gates
function init()
input[GATE].mode('change', V_THRESH, V_HYST, TRIG) -- trig on clock edge
input[GATE].change = function() change() end -- the call-back function
end
-- tremolo lfo w/ support for +/- offsets, shapes and dynamic values
function tremolo(oid, pitch, period, offset, level)
local shape = public.shape -- envelope shape
local sub = period / public.rate -- time per oscillation, compressed by accel
local accel = public.accel -- time-based modulation accel/deceleration
local ld = public.decay -- amplitude decay per oscillation
local up = to( offset, dyn{time=sub}:mul(accel), shape )
local dn = to( dyn{height=level}:step(ld), dyn{time=sub}:mul(accel), shape )
-- invert odd/even output waveforms for a leslie speaker-like stereo effect
if oid % 2 == 0 then return loop { up, dn }
else return loop { dn, up } end
end
-- update the tremolo parameters and trigger the envelope
local time_last = 0 -- last time the envelope was triggered
function change()
local time_now = time()
local time_delta = (time_now - time_last) * M_PERIOD -- seconds (1/f)
time_last = time_now -- record the current time
-- capture pitch, create and trigger the tremolo envelope for specified out
local pitch = input[PITCH].volts -- unused; could affect tremolo depth
for _, v in pairs(OUTS) do
output[v].action = { tremolo(v, pitch, time_delta, V_MIN, V_MAX) }
output[v]() -- (re)trigger the envelope
end
end