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