1 /*
2 * Copyright (c) 2015-2021 Hanspeter Portner (dev@open-music-kontrollers.ch)
3 *
4 * This is free software: you can redistribute it and/or modify
5 * it under the terms of the Artistic License 2.0 as published by
6 * The Perl Foundation.
7 *
8 * This source is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * Artistic License 2.0 for more details.
12 *
13 * You should have received a copy of the Artistic License 2.0
14 * along the source as a COPYING file. If not, obtain it from
15 * http://www.perlfoundation.org/artistic_license_2_0.
16 */
17
18 #include <api_time.h>
19 #include <api_atom.h>
20 #include <api_forge.h>
21
22 #include <timely.h>
23
24 __realtime static void
_ltimeresponder_cb(timely_t * timely,int64_t frames,LV2_URID type,void * data)25 _ltimeresponder_cb(timely_t *timely, int64_t frames, LV2_URID type,
26 void *data)
27 {
28 lua_State *L = data;
29
30 if(lua_geti(L, 5, type) != LUA_TNIL) // uservalue[type]
31 {
32 lua_pushvalue(L, 5); // uservalue
33 lua_pushinteger(L, frames); // frames
34 lua_pushvalue(L, 4); // data
35
36 if(type == TIMELY_URI_BAR_BEAT(timely))
37 {
38 lua_pushnumber(L, TIMELY_BAR_BEAT_RAW(timely));
39 }
40 else if(type == TIMELY_URI_BAR(timely))
41 {
42 lua_pushinteger(L, TIMELY_BAR(timely));
43 }
44 else if(type == TIMELY_URI_BEAT_UNIT(timely))
45 {
46 lua_pushinteger(L, TIMELY_BEAT_UNIT(timely));
47 }
48 else if(type == TIMELY_URI_BEATS_PER_BAR(timely))
49 {
50 lua_pushnumber(L, TIMELY_BEATS_PER_BAR(timely));
51 }
52 else if(type == TIMELY_URI_BEATS_PER_MINUTE(timely))
53 {
54 lua_pushnumber(L, TIMELY_BEATS_PER_MINUTE(timely));
55 }
56 else if(type == TIMELY_URI_FRAME(timely))
57 {
58 lua_pushinteger(L, TIMELY_FRAME(timely));
59 }
60 else if(type == TIMELY_URI_FRAMES_PER_SECOND(timely))
61 {
62 lua_pushnumber(L, TIMELY_FRAMES_PER_SECOND(timely));
63 }
64 else if(type == TIMELY_URI_SPEED(timely))
65 {
66 lua_pushnumber(L, TIMELY_SPEED(timely));
67 }
68 else
69 {
70 lua_pushnil(L);
71 }
72
73 lua_call(L, 4, 0);
74 }
75 else
76 lua_pop(L, 1); // nil
77 }
78
79 __realtime static int
_ltimeresponder__call(lua_State * L)80 _ltimeresponder__call(lua_State *L)
81 {
82 //moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
83
84 lua_settop(L, 5); // discard superfluous arguments
85 // 1: self
86 // 2: from
87 // 3: to
88 // 4: data aka forge
89 // 5: atom || nil
90
91 timely_t *timely = lua_touserdata(L, 1);
92 int64_t from = luaL_checkinteger(L, 2);
93 int64_t to = luaL_checkinteger(L, 3);
94 latom_t *latom = NULL;
95 if(luaL_testudata(L, 5, "latom"))
96 latom = lua_touserdata(L, 5);
97 lua_pop(L, 1); // atom
98
99 lua_getuservalue(L, 1); // 5: uservalue
100 const int handled = latom
101 ? timely_advance_body(timely, latom->atom->size, latom->atom->type, latom->body.obj, from, to)
102 : timely_advance_body(timely, 0, 0, NULL, from, to);
103
104 lua_pushboolean(L, handled); // handled vs not handled
105 return 1;
106 }
107
108 __realtime int
_ltimeresponder_apply(lua_State * L)109 _ltimeresponder_apply(lua_State *L)
110 {
111 // 1: self
112 // 2: atom
113
114 lua_pushinteger(L, 0);
115 lua_insert(L, 2); // from
116
117 lua_pushinteger(L, 0);
118 lua_insert(L, 3); // to
119
120 lua_pushnil(L);
121 lua_insert(L, 4); // data aka forge
122
123 return _ltimeresponder__call(L);
124 }
125
126 __realtime int
_ltimeresponder_stash(lua_State * L)127 _ltimeresponder_stash(lua_State *L)
128 {
129 //moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
130
131 lua_settop(L, 2); // discard superfluous arguments
132 // 1: self
133 // 2: lforge
134
135 timely_t *timely = lua_touserdata(L, 1);
136 lforge_t *lforge = luaL_checkudata(L, 2, "lforge");
137
138 const float multiplier_1 = 1.f / timely->multiplier;
139
140 // serialize full time state to stash
141 LV2_Atom_Forge_Frame frame;
142 if( !lv2_atom_forge_object(lforge->forge, &frame, 0, timely->urid.time_position)
143
144 || !lv2_atom_forge_key(lforge->forge, timely->urid.time_barBeat)
145 || !lv2_atom_forge_float(lforge->forge, TIMELY_BAR_BEAT(timely) * multiplier_1)
146
147 || !lv2_atom_forge_key(lforge->forge, timely->urid.time_bar)
148 || !lv2_atom_forge_long(lforge->forge, TIMELY_BAR(timely))
149
150 || !lv2_atom_forge_key(lforge->forge, timely->urid.time_beatUnit)
151 || !lv2_atom_forge_int(lforge->forge, TIMELY_BEAT_UNIT(timely) * multiplier_1)
152
153 || !lv2_atom_forge_key(lforge->forge, timely->urid.time_beatsPerBar)
154 || !lv2_atom_forge_float(lforge->forge, TIMELY_BEATS_PER_BAR(timely) * multiplier_1)
155
156 || !lv2_atom_forge_key(lforge->forge, timely->urid.time_beatsPerMinute)
157 || !lv2_atom_forge_float(lforge->forge, TIMELY_BEATS_PER_MINUTE(timely))
158
159 || !lv2_atom_forge_key(lforge->forge, timely->urid.time_frame)
160 || !lv2_atom_forge_long(lforge->forge, TIMELY_FRAME(timely))
161
162 || !lv2_atom_forge_key(lforge->forge, timely->urid.time_framesPerSecond)
163 || !lv2_atom_forge_float(lforge->forge, TIMELY_FRAMES_PER_SECOND(timely))
164
165 || !lv2_atom_forge_key(lforge->forge, timely->urid.time_speed)
166 || !lv2_atom_forge_float(lforge->forge, TIMELY_SPEED(timely)) )
167 luaL_error(L, forge_buffer_overflow);
168 lv2_atom_forge_pop(lforge->forge, &frame);
169
170 return 1; // forge
171 }
172
173 __realtime static int
_ltimeresponder__index(lua_State * L)174 _ltimeresponder__index(lua_State *L)
175 {
176 //moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
177
178 lua_settop(L, 2); // discard superfluous arguments
179 // 1: self
180 // 2: type
181
182 timely_t *timely = lua_touserdata(L, 1);
183
184 int ltype = lua_type(L, 2);
185 if(ltype != LUA_TNUMBER)
186 {
187 if(ltype == LUA_TSTRING)
188 {
189 const char *key = lua_tostring(L, 2);
190 if(!strcmp(key, "stash"))
191 lua_rawgetp(L, LUA_REGISTRYINDEX, _ltimeresponder_stash);
192 else if(!strcmp(key, "apply"))
193 lua_rawgetp(L, LUA_REGISTRYINDEX, _ltimeresponder_apply);
194 else if(!strcmp(key, "multiplier"))
195 lua_pushnumber(L, timely->multiplier);
196 else
197 lua_pushnil(L);
198 }
199 else
200 lua_pushnil(L);
201
202 return 1;
203 }
204
205 LV2_URID type = lua_tointeger(L, 2);
206
207 if(type == TIMELY_URI_BAR_BEAT(timely))
208 {
209 lua_pushnumber(L, TIMELY_BAR_BEAT(timely));
210 }
211 else if(type == TIMELY_URI_BAR(timely))
212 {
213 lua_pushinteger(L, TIMELY_BAR(timely));
214 }
215 else if(type == TIMELY_URI_BEAT_UNIT(timely))
216 {
217 lua_pushinteger(L, TIMELY_BEAT_UNIT(timely));
218 }
219 else if(type == TIMELY_URI_BEATS_PER_BAR(timely))
220 {
221 lua_pushnumber(L, TIMELY_BEATS_PER_BAR(timely));
222 }
223 else if(type == TIMELY_URI_BEATS_PER_MINUTE(timely))
224 {
225 lua_pushnumber(L, TIMELY_BEATS_PER_MINUTE(timely));
226 }
227 else if(type == TIMELY_URI_FRAME(timely))
228 {
229 lua_pushinteger(L, TIMELY_FRAME(timely));
230 }
231 else if(type == TIMELY_URI_FRAMES_PER_SECOND(timely))
232 {
233 lua_pushnumber(L, TIMELY_FRAMES_PER_SECOND(timely));
234 }
235 else if(type == TIMELY_URI_SPEED(timely))
236 {
237 lua_pushnumber(L, TIMELY_SPEED(timely));
238 }
239 else
240 {
241 lua_pushnil(L);
242 }
243
244 return 1;
245 }
246
247 __realtime static int
_ltimeresponder__newindex(lua_State * L)248 _ltimeresponder__newindex(lua_State *L)
249 {
250 //moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
251
252 lua_settop(L, 3); // discard superfluous arguments
253 // 1: self
254 // 2: key
255 // 3: value
256
257 timely_t *timely = lua_touserdata(L, 1);
258
259 if(lua_type(L, 2) == LUA_TSTRING)
260 {
261 if(!strcmp(lua_tostring(L, 2), "multiplier"))
262 {
263 const float multiplier = luaL_checknumber(L, 3);
264 if(multiplier <= 0.f)
265 luaL_error(L, "multiplier not > 0.0");
266 timely_set_multiplier(timely, multiplier);
267 }
268 }
269
270 return 0;
271 }
272
273 __realtime int
_ltimeresponder(lua_State * L)274 _ltimeresponder(lua_State *L)
275 {
276 moony_t *moony = lua_touserdata(L, lua_upvalueindex(1));
277
278 lua_settop(L, 2); // discard superfluous arguments
279
280 timely_mask_t mask = TIMELY_MASK_BAR_BEAT
281 | TIMELY_MASK_BAR
282 | TIMELY_MASK_BEAT_UNIT
283 | TIMELY_MASK_BEATS_PER_BAR
284 | TIMELY_MASK_BEATS_PER_MINUTE
285 | TIMELY_MASK_FRAME
286 | TIMELY_MASK_FRAMES_PER_SECOND
287 | TIMELY_MASK_SPEED
288 | TIMELY_MASK_BAR_BEAT_WHOLE
289 | TIMELY_MASK_BAR_WHOLE;
290
291 // o = o or {}
292 if(lua_isnil(L, 1))
293 {
294 lua_remove(L, 1);
295 lua_newtable(L);
296 lua_insert(L, 1);
297 }
298
299 const float multiplier = luaL_optnumber(L, 2, 1.f);
300 lua_pop(L, 1); // multiplier
301
302 if(multiplier <= 0.f)
303 luaL_error(L, "multiplier not > 0.0");
304
305 // TODO do we want to cache/reuse this, too?
306 timely_t *timely = lua_newuserdata(L, sizeof(timely_t)); // userdata
307 timely_init(timely, moony->map, moony->sample_rate.body, mask,
308 _ltimeresponder_cb, L);
309 timely_set_multiplier(timely, multiplier);
310
311 // userdata.uservalue = o
312 lua_insert(L, 1);
313 lua_setuservalue(L, -2);
314
315 // setmetatable(o, self)
316 luaL_getmetatable(L, "ltimeresponder");
317 lua_setmetatable(L, -2);
318
319 // return o
320 return 1;
321 }
322
323 const luaL_Reg ltimeresponder_mt [] = {
324 {"__index", _ltimeresponder__index},
325 {"__newindex", _ltimeresponder__newindex},
326 {"__call", _ltimeresponder__call},
327 {NULL, NULL}
328 };
329