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 <time.h>
19 #include <math.h>
20 #include <inttypes.h>
21 
22 #include <sherlock.h>
23 #include <sherlock_nk.h>
24 #include <encoder.h>
25 
26 #include <osc.lv2/util.h>
27 
28 typedef struct _mem_t mem_t;
29 
30 struct _mem_t {
31 	size_t size;
32 	char *buf;
33 };
34 
35 static inline void
_shadow(struct nk_context * ctx,bool * shadow)36 _shadow(struct nk_context *ctx, bool *shadow)
37 {
38 	if(*shadow)
39 	{
40 		struct nk_style *style = &ctx->style;
41 		const struct nk_vec2 group_padding = style->window.group_padding;
42 		struct nk_command_buffer *canvas = nk_window_get_canvas(ctx);
43 
44 		struct nk_rect b = nk_widget_bounds(ctx);
45 		b.x -= group_padding.x;
46 		b.w *= 10;
47 		b.w += 5*group_padding.x;
48 		nk_fill_rect(canvas, b, 0.f, nk_rgb(0x28, 0x28, 0x28));
49 	}
50 
51 	*shadow = !*shadow;
52 }
53 
54 static inline void
_mem_printf(mem_t * mem,const char * fmt,...)55 _mem_printf(mem_t *mem, const char *fmt, ...)
56 {
57   va_list args;
58   va_start(args, fmt);
59 
60 	char *src;
61 	if(vasprintf(&src, fmt, args) != -1)
62 	{
63 		const size_t sz = strlen(src);
64 
65 		mem->buf = realloc(mem->buf, mem->size + sz);
66 		if(mem->buf)
67 		{
68 			char *dst = mem->buf + mem->size - 1;
69 
70 			strncpy(dst, src, sz + 1);
71 			mem->size += sz;
72 		}
73 		else
74 			mem->size = 0;
75 
76 		free(src);
77 	}
78 
79 	va_end(args);
80 }
81 
82 static void
_osc_timetag(mem_t * mem,LV2_OSC_Timetag * tt)83 _osc_timetag(mem_t *mem, LV2_OSC_Timetag *tt)
84 {
85 	if(tt->integral <= 1UL)
86 	{
87 		_mem_printf(mem, "t : %s", "immediate");
88 	}
89 	else
90 	{
91 		const uint32_t us = floor(tt->fraction * 0x1p-32 * 1e6);
92 		const time_t ttime = tt->integral - 0x83aa7e80;
93 		const struct tm *ltime = localtime(&ttime);
94 
95 		char tmp [32];
96 		if(strftime(tmp, 32, "%d-%b-%Y %T", ltime))
97 			_mem_printf(mem, "t : %s.%06"PRIu32, tmp, us);
98 	}
99 }
100 
101 static void
_osc_argument(plughandle_t * handle,struct nk_context * ctx,const LV2_Atom * arg,const char * path,bool first)102 _osc_argument(plughandle_t *handle, struct nk_context *ctx, const LV2_Atom *arg,
103 	const char *path, bool first)
104 {
105 	const LV2_OSC_Type type = lv2_osc_argument_type(&handle->osc_urid, arg);
106 
107 	mem_t mem = {
108 		.size = 1,
109 		.buf = calloc(1, sizeof(char))
110 	};
111 
112 	switch(type)
113 	{
114 		case LV2_OSC_INT32:
115 		{
116 			int32_t i;
117 			lv2_osc_int32_get(&handle->osc_urid, arg, &i);
118 			_mem_printf(&mem, "i : %"PRIi32, i);
119 		} break;
120 		case LV2_OSC_FLOAT:
121 		{
122 			float f;
123 			lv2_osc_float_get(&handle->osc_urid, arg, &f);
124 			_mem_printf(&mem, "f : %f", f);
125 		} break;
126 		case LV2_OSC_STRING:
127 		{
128 			const char *s;
129 			lv2_osc_string_get(&handle->osc_urid, arg, &s);
130 			_mem_printf(&mem, "s : %s", s);
131 		} break;
132 		case LV2_OSC_BLOB:
133 		{
134 			uint32_t sz;
135 			const uint8_t *b;
136 			lv2_osc_blob_get(&handle->osc_urid, arg, &sz, &b);
137 			_mem_printf(&mem, "b : (%"PRIu32") ", sz);
138 			for(unsigned i=0; i<sz; i++)
139 				_mem_printf(&mem, "%02"PRIx8, b[i]);
140 		} break;
141 
142 		case LV2_OSC_TRUE:
143 		{
144 			_mem_printf(&mem, "T : true");
145 		} break;
146 		case LV2_OSC_FALSE:
147 		{
148 			_mem_printf(&mem, "F : false");
149 		} break;
150 		case LV2_OSC_NIL:
151 		{
152 			_mem_printf(&mem, "N : nil");
153 		} break;
154 		case LV2_OSC_IMPULSE:
155 		{
156 			_mem_printf(&mem, "I : impulse");
157 		} break;
158 
159 		case LV2_OSC_INT64:
160 		{
161 			int64_t h;
162 			lv2_osc_int64_get(&handle->osc_urid, arg, &h);
163 			_mem_printf(&mem, "h : %"PRIi64, h);
164 		} break;
165 		case LV2_OSC_DOUBLE:
166 		{
167 			double d;
168 			lv2_osc_double_get(&handle->osc_urid, arg, &d);
169 			_mem_printf(&mem, "d : %lf", d);
170 		} break;
171 		case LV2_OSC_TIMETAG:
172 		{
173 			LV2_OSC_Timetag tt;
174 			lv2_osc_timetag_get(&handle->osc_urid, arg, &tt);
175 			_osc_timetag(&mem, &tt);
176 		} break;
177 
178 		case LV2_OSC_SYMBOL:
179 		{
180 			LV2_URID S;
181 			lv2_osc_symbol_get(&handle->osc_urid, arg, &S);
182 			_mem_printf(&mem, "S : %s", handle->unmap->unmap(handle->unmap->handle, S));
183 		} break;
184 		case LV2_OSC_CHAR:
185 		{
186 			char c;
187 			lv2_osc_char_get(&handle->osc_urid, arg, &c);
188 			_mem_printf(&mem, "c : %c", c);
189 		} break;
190 		case LV2_OSC_RGBA:
191 		{
192 			uint8_t r [4];
193 			lv2_osc_rgba_get(&handle->osc_urid, arg, r+0, r+1, r+2, r+3);
194 			_mem_printf(&mem, "r : ");
195 			for(unsigned i=0; i<4; i++)
196 				_mem_printf(&mem, "%02"PRIx8, r[i]);
197 		} break;
198 		case LV2_OSC_MIDI:
199 		{
200 			uint32_t sz;
201 			const uint8_t *m;
202 			lv2_osc_midi_get(&handle->osc_urid, arg, &sz, &m);
203 			_mem_printf(&mem, "m : (%"PRIu32") ", sz);
204 			for(unsigned i=0; i<sz; i++)
205 				_mem_printf(&mem, "%02"PRIx8, m[i]);
206 		} break;
207 	}
208 
209 	if(!first)
210 	{
211 		_shadow(ctx, &handle->shadow);
212 		_empty(ctx);
213 	}
214 
215 	if(first)
216 	{
217 		nk_label_colored(ctx, path, NK_TEXT_LEFT, magenta);
218 	}
219 	else
220 	{
221 		_empty(ctx);
222 	}
223 
224 	if(mem.buf)
225 	{
226 		nk_label_colored(ctx, mem.buf, NK_TEXT_LEFT, cwhite);
227 		free(mem.buf);
228 	}
229 	else
230 	{
231 		_empty(ctx);
232 	}
233 
234 	nk_labelf_colored(ctx, NK_TEXT_RIGHT, blue, "%"PRIu32, arg->size);
235 }
236 
237 static void
_osc_message(plughandle_t * handle,struct nk_context * ctx,const LV2_Atom_Object * obj)238 _osc_message(plughandle_t *handle, struct nk_context *ctx, const LV2_Atom_Object *obj)
239 {
240 	const LV2_Atom_String *path = NULL;
241 	const LV2_Atom_Tuple *args = NULL;
242 	lv2_osc_message_get(&handle->osc_urid, obj, &path, &args);
243 
244 	bool first = true;
245 	LV2_ATOM_TUPLE_FOREACH(args, arg)
246 	{
247 		_osc_argument(handle, ctx, arg, LV2_ATOM_BODY_CONST(path), first);
248 
249 		if(first)
250 			first = false;
251 	}
252 }
253 
254 static void
255 _osc_packet(plughandle_t *handle, struct nk_context *ctx, int64_t frames,
256 	const LV2_Atom_Object *obj, float offset);
257 
258 static void
_osc_bundle(plughandle_t * handle,struct nk_context * ctx,const LV2_Atom_Object * obj,float offset)259 _osc_bundle(plughandle_t *handle, struct nk_context *ctx, const LV2_Atom_Object *obj, float offset)
260 {
261 	const LV2_Atom_Object *timetag = NULL;
262 	const LV2_Atom_Tuple *items = NULL;
263 	lv2_osc_bundle_get(&handle->osc_urid, obj, &timetag, &items);
264 
265 	// format bundle timestamp
266 	LV2_OSC_Timetag tt;
267 	lv2_osc_timetag_get(&handle->osc_urid, &timetag->atom, &tt);
268 
269 	mem_t mem = {
270 		.size = 1,
271 		.buf = calloc(1, sizeof(char))
272 	};
273 
274 	nk_label_colored(ctx, "#bundle", NK_TEXT_LEFT, red);
275 
276 	_osc_timetag(&mem, &tt);
277 	if(mem.buf)
278 	{
279 		nk_label_colored(ctx, mem.buf, NK_TEXT_LEFT, cwhite);
280 		free(mem.buf);
281 	}
282 	else
283 	{
284 		_empty(ctx);
285 	}
286 
287 	nk_labelf_colored(ctx, NK_TEXT_RIGHT, blue, "%"PRIu32, obj->atom.size);
288 
289 	LV2_ATOM_TUPLE_FOREACH(items, item)
290 	{
291 		_osc_packet(handle, ctx, -1, (const LV2_Atom_Object *)item, offset + 0.025);
292 	}
293 }
294 
295 static void
_osc_packet(plughandle_t * handle,struct nk_context * ctx,int64_t frames,const LV2_Atom_Object * obj,float offset)296 _osc_packet(plughandle_t *handle, struct nk_context *ctx, int64_t frames,
297 	const LV2_Atom_Object *obj, float offset)
298 {
299 	const float widget_h = handle->dy;
300 
301 	const float ratios [4] = {offset, 0.3f - offset, 0.6f, 0.1f};
302 	nk_layout_row(ctx, NK_DYNAMIC, widget_h, 4, ratios);
303 
304 	_shadow(ctx, &handle->shadow);
305 	if(frames >= 0)
306 	{
307 		nk_labelf_colored(ctx, NK_TEXT_LEFT, yellow, "+%04"PRIi64, frames);
308 	}
309 	else
310 	{
311 		_empty(ctx);
312 	}
313 
314 	if(lv2_osc_is_message_type(&handle->osc_urid, obj->body.otype))
315 	{
316 		_osc_message(handle, ctx, obj);
317 	}
318 	else if(lv2_osc_is_bundle_type(&handle->osc_urid, obj->body.otype))
319 	{
320 		_osc_bundle(handle, ctx, obj, offset);
321 	}
322 }
323 
324 void
_osc_inspector_expose(struct nk_context * ctx,struct nk_rect wbounds,void * data)325 _osc_inspector_expose(struct nk_context *ctx, struct nk_rect wbounds, void *data)
326 {
327 	plughandle_t *handle = data;
328 
329 	handle->dy = 20.f * _get_scale(handle);
330 	const float widget_h = handle->dy;
331 	struct nk_style *style = &ctx->style;
332   const struct nk_vec2 window_padding = style->window.padding;
333   const struct nk_vec2 group_padding = style->window.group_padding;
334 
335 	const char *window_name = "Sherlock";
336 	if(nk_begin(ctx, window_name, wbounds, NK_WINDOW_NO_SCROLLBAR))
337 	{
338 		struct nk_panel *panel = nk_window_get_panel(ctx);
339 		struct nk_command_buffer *canvas = nk_window_get_canvas(ctx);
340 
341 		const float body_h = panel->bounds.h - 4*window_padding.y - 2*widget_h;
342 		nk_layout_row_dynamic(ctx, body_h, 1);
343 		nk_flags flags = NK_WINDOW_BORDER;
344 		if(handle->state.follow)
345 		{
346 			flags |= NK_WINDOW_NO_SCROLLBAR;
347 		}
348 		else
349 		{
350 			handle->shadow = false;
351 		}
352 		struct nk_list_view lview;
353 		if(nk_list_view_begin(ctx, &lview, "Events", flags, widget_h, NK_MIN(handle->n_item, MAX_LINES)))
354 		{
355 			if(handle->state.follow)
356 			{
357 				lview.end = NK_MAX(handle->n_item, 0);
358 				lview.begin = NK_MAX(lview.end - lview.count, 0);
359 			}
360 			for(int l = lview.begin; (l < lview.end) && (l < handle->n_item); l++)
361 			{
362 				item_t *itm = handle->items[l];
363 
364 				switch(itm->type)
365 				{
366 					case ITEM_TYPE_NONE:
367 					{
368 						// skip, was bundle payload
369 					} break;
370 					case ITEM_TYPE_FRAME:
371 					{
372 						nk_layout_row_dynamic(ctx, widget_h, 3);
373 						{
374 							struct nk_rect b = nk_widget_bounds(ctx);
375 							b.x -= group_padding.x;
376 							b.w *= 3;
377 							b.w += 4*group_padding.x;
378 							nk_fill_rect(canvas, b, 0.f, nk_rgb(0x18, 0x18, 0x18));
379 						}
380 
381 						nk_labelf_colored(ctx, NK_TEXT_LEFT, orange, "@%"PRIi64, itm->frame.offset);
382 						nk_labelf_colored(ctx, NK_TEXT_CENTERED, green, "-%"PRIu32"-", itm->frame.counter);
383 						nk_labelf_colored(ctx, NK_TEXT_RIGHT, violet, "%"PRIi32, itm->frame.nsamples);
384 
385 						handle->shadow = false;
386 					} break;
387 
388 					case ITEM_TYPE_EVENT:
389 					{
390 						LV2_Atom_Event *ev = &itm->event.ev;
391 						const LV2_Atom_Object *obj = (const LV2_Atom_Object *)&ev->body;
392 						const int64_t frames = ev->time.frames;
393 						const float offset = 0.1;
394 
395 						_osc_packet(handle, ctx, frames, obj, offset);
396 					} break;
397 				}
398 			}
399 
400 			nk_list_view_end(&lview);
401 		}
402 
403 		const float n = 3;
404 		const float r0 = 1.f / n;
405 		const float r1 = 0.1f / 3;
406 		const float r2 = r0 - r1;
407 		const float footer [6] = {r1, r2, r1, r2, r1, r2};
408 		nk_layout_row(ctx, NK_DYNAMIC, widget_h, 6, footer);
409 		{
410 			const int32_t state_overwrite = _check(ctx, handle->state.overwrite);
411 			if(state_overwrite != handle->state.overwrite)
412 			{
413 				handle->state.overwrite = state_overwrite;
414 				_set_bool(handle, handle->urid.overwrite, handle->state.overwrite);
415 			}
416 			nk_label(ctx, "overwrite", NK_TEXT_LEFT);
417 
418 			const int32_t state_block = _check(ctx, handle->state.block);
419 			if(state_block != handle->state.block)
420 			{
421 				handle->state.block = state_block;
422 				_set_bool(handle, handle->urid.block, handle->state.block);
423 			}
424 			nk_label(ctx, "block", NK_TEXT_LEFT);
425 
426 			const int32_t state_follow = _check(ctx, handle->state.follow);
427 			if(state_follow != handle->state.follow)
428 			{
429 				handle->state.follow = state_follow;
430 				_set_bool(handle, handle->urid.follow, handle->state.follow);
431 			}
432 			nk_label(ctx, "follow", NK_TEXT_LEFT);
433 		}
434 
435 		const bool max_reached = handle->n_item >= MAX_LINES;
436 		nk_layout_row_dynamic(ctx, widget_h, 2);
437 		if(nk_button_symbol_label(ctx,
438 			max_reached ? NK_SYMBOL_TRIANGLE_RIGHT: NK_SYMBOL_NONE,
439 			"clear", NK_TEXT_LEFT))
440 		{
441 			_clear(handle);
442 		}
443 		nk_label(ctx, "Sherlock.lv2: "SHERLOCK_VERSION, NK_TEXT_RIGHT);
444 	}
445 	nk_end(ctx);
446 }
447