Permalink
Newer
100644
305 lines (252 sloc)
8.13 KB
8
local clock_id_counter = 1
9
local function new_id()
10
local id = clock_id_counter
11
clock_id_counter = clock_id_counter + 1
12
return id
13
end
14
15
--- create a coroutine to run but do not immediately run it;
16
-- @tparam function f
17
-- @treturn integer : coroutine ID that can be used to resume/stop it later
18
clock.create = function(f)
19
local coro = coroutine.create(f)
20
local coro_id = new_id()
21
clock.threads[coro_id] = coro
22
return coro_id
23
end
24
25
--- create a coroutine from the given function and immediately run it;
26
-- the function parameter is a task that will suspend when clock.sleep and clock.sync are called inside it and will wake up again after specified time.
27
-- @tparam function f
28
-- @treturn integer : coroutine ID that can be used to stop it later
29
clock.run = function(f, ...)
31
clock.resume(coro_id, ...)
35
--- stop execution of a coroutine started using clock.run.
36
-- @tparam integer coro_id : coroutine ID
37
clock.cancel = function(coro_id)
38
_norns.clock_cancel(coro_id)
39
clock.threads[coro_id] = nil
40
end
41
45
46
--- yield and schedule waking up the coroutine in s seconds;
47
-- must be called from within a coroutine started with clock.run.
48
-- @tparam float s : seconds
49
clock.sleep = function(...)
50
return coroutine.yield(SLEEP, ...)
53
--- yield and schedule waking up the coroutine at beats beat;
54
-- the coroutine will suspend for the time required to reach the given fraction of a beat;
55
-- must be called from within a coroutine started with clock.run.
56
-- @tparam float beats : next fraction of a beat at which the coroutine will be resumed. may be larger than 1.
61
--- yield and do not schedule wake up, clock must be explicitly resumed
62
-- must be called from within a coroutine started with clock.run.
63
clock.suspend = function()
64
return coroutine.yield(SUSPEND)
65
end
66
72
print('clock: ignoring resumption of canceled clock (no coroutine)')
73
return
76
local result, mode, time = coroutine.resume(clock.threads[coro_id], ...)
78
if coroutine.status(coro) == "dead" then
79
if result then
80
clock.cancel(coro_id)
82
error(mode)
83
end
84
else
85
-- not dead
86
if result and mode ~= nil then
87
if mode == SLEEP then
88
_norns.clock_schedule_sleep(coro_id, time)
89
elseif mode == SYNC then
90
_norns.clock_schedule_sync(coro_id, time)
91
elseif mode == SUSPEND then
92
-- nothing needed for SUSPEND
93
end
100
for id, coro in pairs(clock.threads) do
106
clock.transport.start = nil
107
clock.transport.stop = nil
110
--- select the sync source
111
-- @tparam string source : "internal", "midi", or "link"
114
_norns.clock_set_source(util.clamp(source-1,0,3)) -- lua list is 1-indexed
116
_norns.clock_set_source(0)
117
elseif source == "midi" then
118
_norns.clock_set_source(1)
119
elseif source == "link" then
120
_norns.clock_set_source(2)
121
else
122
error("unknown clock source: "..source)
123
end
128
return _norns.clock_get_time_beats()
131
clock.get_tempo = function()
132
return _norns.clock_get_tempo()
133
end
134
135
clock.get_beat_sec = function(x)
136
x = x or 1
137
return 60.0 / clock.get_tempo() * x
138
end
139
143
clock.transport.start = nil
144
clock.transport.stop = nil
147
clock.internal = {}
148
149
clock.internal.set_tempo = function(bpm)
150
return _norns.clock_internal_set_tempo(bpm)
151
end
152
153
clock.internal.start = function(beat)
154
beat = beat or 0
155
return _norns.clock_internal_start(beat)
156
end
157
158
clock.internal.stop = function()
159
return _norns.clock_internal_stop()
160
end
161
162
clock.crow = {}
163
164
clock.crow.in_div = function(div)
165
_norns.clock_crow_in_div(div)
166
end
167
168
169
clock.midi = {}
170
171
172
clock.link = {}
173
174
clock.link.set_tempo = function(bpm)
175
return _norns.clock_link_set_tempo(bpm)
176
end
177
178
clock.link.set_quantum = function(quantum)
179
return _norns.clock_link_set_quantum(quantum)
180
end
181
182
184
if clock.transport.start ~= nil then
185
clock.transport.start()
187
end
188
189
_norns.clock.stop = function()
190
if clock.transport.stop ~= nil then
191
clock.transport.stop()
199
params:add_option("clock_source", "source", {"internal", "midi", "link", "crow"},
200
norns.state.clock.source)
205
crow.input[1].change = function() end
206
crow.input[1].mode("change",2,0.1,"rising")
208
norns.state.clock.source = x
209
if x==1 then clock.internal.set_tempo(params:get("clock_tempo"))
210
elseif x==3 then clock.link.set_tempo(params:get("clock_tempo")) end
212
params:set_save("clock_source", false)
213
params:add_number("clock_tempo", "tempo", 1, 300, norns.state.clock.tempo)
216
local source = params:string("clock_source")
217
if source == "internal" then clock.internal.set_tempo(bpm)
218
elseif source == "link" then clock.link.set_tempo(bpm) end
222
params:add_trigger("clock_reset", "reset")
223
params:set_action("clock_reset",
225
local source = params:string("clock_source")
226
if source == "internal" then clock.internal.start(bpm)
227
elseif source == "link" then print("link reset not supported") end
228
end)
229
params:add_number("link_quantum", "link quantum", 1, 32, norns.state.clock.link_quantum)
231
function(x)
232
clock.link.set_quantum(x)
233
norns.state.clock.link_quantum = x
234
end)
235
params:set_save("link_quantum", false)
238
{"off", "port 1", "port 2", "port 3", "port 4"}, norns.state.clock.midi_out)
239
params:set_action("clock_midi_out", function(x) norns.state.clock.midi_out = x end)
240
params:set_save("clock_midi_out", false)
242
{"off", "output 1", "output 2", "output 3", "output 4"}, norns.state.clock.crow_out)
243
params:set_action("clock_crow_out", function(x)
244
if x>1 then crow.output[x-1].action = "pulse(0.05,8)" end
247
params:set_save("clock_crow_out", false)
248
params:add_number("clock_crow_out_div", "crow out div", 1, 32,
249
norns.state.clock.crow_out_div)
250
params:set_action("clock_crow_out_div",
251
function(x) norns.state.clock.crow_out_div = x end)
252
params:set_save("clock_crow_out_div", false)
253
params:add_number("clock_crow_in_div", "crow in div", 1, 32,
254
norns.state.clock.crow_in_div)
255
params:set_action("clock_crow_in_div",
256
function(x)
257
clock.crow.in_div(x)
258
norns.state.clock.crow_in_div = x
259
end)
260
params:set_save("clock_crow_in_div", false)
261
--params:add_trigger("crow_clear", "crow clear")
262
--params:set_action("crow_clear",
263
--function() crow.reset() crow.clear() end)
266
267
-- executes crow sync
268
clock.run(function()
269
while true do
271
local crow_out = params:get("clock_crow_out")-1
272
if crow_out > 0 then crow.output[crow_out]() end
273
end
274
end)
275
276
-- executes midi out (needs a subtick)
277
-- FIXME: lots of if's every tick blah
278
clock.run(function()
279
while true do
280
clock.sync(1/24)
281
local midi_out = params:get("clock_midi_out")-1
283
if midi.vports[midi_out].name ~= "none" then
284
midi.vports[midi_out]:clock()
290
-- update tempo param value
291
clock.run(function()
292
while true do
293
if params:get("clock_source") ~= 1 then
294
local external_tempo = math.floor(clock.get_tempo() + 0.5)
295
params:set("clock_tempo", external_tempo, true)
296
end
297
298
clock.sleep(1)
299
end
300
end)
301