-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlanes.lua
executable file
·106 lines (91 loc) · 3.7 KB
/
lanes.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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
--- lanes are breakpoint tables for creating DAW-like track automation.
-- implemenation heavily borrows from @tyleretters' sequins
local L = {}
--- Create a new lane.
-- @tparam table t table containing breakpoints that define the return value at given number of beats
-- ex: l { {0, 0}, {16, 100}, {32, 0}}
-- each point can have an optional 3rd element that defines the curve to the next point
-- ex: l { {0, 0, curves.ramp()}, {16, 100, curves.ease_in()}, {32, 0, curves.ease_out()}}}
-- see the curves library for more info
-- @tparam[opt] boolean loop wether or not the lane should loop, default: false
-- @tparam[opt] function callback a function to be called at the start of each breakpoint
-- the function will be called with 2 parameters, the breakpoint that triggered it and the Lanes object itself
-- @treturn table new Lane
function L.new(t, loop, callback)
-- wrap a table in a lane with defaults
local l = {data = t, loop = loop, ix=1, callback=callback}
setmetatable(l, L)
return l
end
--- Set an entirely new table of breakpoints.
-- @tparam table t
function L.setdata(self, t) self.data = t end
--- Set the the 0th beat to reset phase calculations.
-- @tparam number beats
function L.setstartbeats(self, beats) self.startbeats = beats end
--- Test whether a table is a Lane.
-- @tparam table t
-- @treturn boolean
function L.is_lanes(t) return getmetatable(t) == L end
--- Append an entire table of breakpoints to this lane.
-- @tparam table t table of breakpoints
-- @treturn table self
function L.extend(self, t)
for i = 1, #t do self.data[#self.data + 1] = v end
return self
end
-- Append a single breakpoint to this lane.
-- @tparam table t table containing a single break point
-- @treturn table self
function L.append(self, t) -- append a single breakpoint
self.data[#self.data + 1] = t
return self
end
--- Insert a single breakpoint at index ix.
-- @tparam table t table containg a single breakpoint
-- @tapram number ix index to insert at
-- @treturn table self
function L.insert(self, t, ix) -- insert a single breakpoint at ix
table.insert(self.data, ix, t)
return self
end
--- Interpolate between two breakpoints at the specified number of beats.
-- @tparam [opt] number at_beats point in time in beats to interpolate, default: clock.get_beats()
-- @treturn number value at specified beats
function L.interpolate(self, at_beats)
at_beats = at_beats or clock.get_beats()
self.startbeats = self.startbeats or at_beats
at_beats = at_beats - self.startbeats
local start_time, start_level, start_curve = table.unpack(self.data[self.ix])
local next_ix = (self.ix % #self.data) + 1
local target_time, target_level, target_curve = table.unpack(self.data[next_ix])
if at_beats >= target_time then
start_time, start_level, start_curve = target_time, target_level, target_curve
self.ix = next_ix
next_ix = (self.ix % #self.data) + 1
target_time, target_level = table.unpack(self.data[next_ix])
if self.callback then
self.callback(self.data[self.ix], self)
end
end
if self.ix == #self.data then
if self.loop then
self.startbeats = clock.get_beats()
else
return start_level
end
end
local phase = util.linlin(start_time, target_time, 0, 1, at_beats)
local y = start_curve and start_curve(phase) or phase -- default to linear
return util.linlin(0, 1, start_level, target_level, y)
end
--- metamethods
L.__call = function(self, ...)
return (self == L) and L.new(...) or L.interpolate(self, ...)
end
L.metaix = {settable = L.setdata, setstartbearts = L.setstartbeats}
L.__index = function(self, ix)
return L.metaix[ix]
end
setmetatable(L, L)
return L