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 <stdio.h>
19 #include <stdlib.h>
20 
21 #include <sherlock.h>
22 
23 typedef struct _handle_t handle_t;
24 
25 struct _handle_t {
26 	LV2_URID_Map *map;
27 	LV2_URID_Unmap *unmap;
28 	LV2_Log_Log *log;
29 	LV2_Log_Logger logger;
30 
31 	const LV2_Atom_Sequence *control;
32 	craft_t through;
33 	craft_t notify;
34 
35 	LV2_URID time_position;
36 	LV2_URID time_frame;
37 	LV2_URID sherlock_matchAll;
38 
39 	int64_t frame;
40 
41 	PROPS_T(props, MAX_NPROPS);
42 	state_t state;
43 	state_t stash;
44 };
45 
46 static LV2_Handle
47 instantiate(const LV2_Descriptor* descriptor, double rate __attribute__((unused)),
48 	const char *bundle_path __attribute__((unused)),
49 	const LV2_Feature *const *features)
50 {
51 	int i;
52 	handle_t *handle = calloc(1, sizeof(handle_t));
53 	if(!handle)
54 		return NULL;
55 
56 	for(i=0; features[i]; i++)
57 	{
58 		if(!strcmp(features[i]->URI, LV2_URID__map))
59 			handle->map = features[i]->data;
60 		else if(!strcmp(features[i]->URI, LV2_URID__unmap))
61 			handle->unmap = features[i]->data;
62 		else if(!strcmp(features[i]->URI, LV2_LOG__log))
63 			handle->log = features[i]->data;
64 	}
65 
66 	if(!handle->map || !handle->unmap)
67 	{
68 		fprintf(stderr, "%s: Host does not support urid:(un)map\n", descriptor->URI);
69 		free(handle);
70 		return NULL;
71 	}
72 
73 	if(handle->log)
74 		lv2_log_logger_init(&handle->logger, handle->map, handle->log);
75 
76 	handle->time_position = handle->map->map(handle->map->handle, LV2_TIME__Position);
77 	handle->time_frame = handle->map->map(handle->map->handle, LV2_TIME__frame);
78 	handle->sherlock_matchAll = handle->map->map(handle->map->handle, SHERLOCK_URI"#matchAll");
79 
80 	lv2_atom_forge_init(&handle->through.forge, handle->map);
81 	lv2_atom_forge_init(&handle->notify.forge, handle->map);
82 
83 	if(!props_init(&handle->props, descriptor->URI,
84 		defs, MAX_NPROPS, &handle->state, &handle->stash,
85 		handle->map, handle))
86 	{
87 		fprintf(stderr, "failed to allocate property structure\n");
88 		free(handle);
89 		return NULL;
90 	}
91 
92 	return handle;
93 }
94 
95 static void
96 connect_port(LV2_Handle instance, uint32_t port, void *data)
97 {
98 	handle_t *handle = (handle_t *)instance;
99 
100 	switch(port)
101 	{
102 		case 0:
103 			handle->control = (const LV2_Atom_Sequence *)data;
104 			break;
105 		case 1:
106 			handle->through.seq = (LV2_Atom_Sequence *)data;
107 			break;
108 		case 2:
109 			handle->notify.seq = (LV2_Atom_Sequence *)data;
110 			break;
111 		default:
112 			break;
113 	}
114 }
115 
116 static void
117 run(LV2_Handle instance, uint32_t nsamples)
118 {
119 	handle_t *handle = (handle_t *)instance;
120 	craft_t *through = &handle->through;
121 	craft_t *notify = &handle->notify;
122 
123 	uint32_t capacity = through->seq->atom.size;
124 	lv2_atom_forge_set_buffer(&through->forge, through->buf, capacity);
125 	through->ref = lv2_atom_forge_sequence_head(&through->forge, &through->frame[0], 0);
126 
127 	capacity = notify->seq->atom.size;
128 	lv2_atom_forge_set_buffer(&notify->forge, notify->buf, capacity);
129 	notify->ref = lv2_atom_forge_sequence_head(&notify->forge, &notify->frame[0], 0);
130 
131 	props_idle(&handle->props, &notify->forge, 0, &notify->ref);
132 
133 	LV2_ATOM_SEQUENCE_FOREACH(handle->control, ev)
134 	{
135 		const LV2_Atom *atom = &ev->body;
136 		const LV2_Atom_Object *obj = (const LV2_Atom_Object *)&ev->body;
137 		const int64_t frames = ev->time.frames;
138 
139 		if(handle->state.trace && handle->log)
140 		{
141 			if(lv2_atom_forge_is_object_type(&through->forge, atom->type))
142 			{
143 				lv2_log_trace(&handle->logger, "%4"PRIi64", object, %s\n", ev->time.frames,
144 					handle->unmap->unmap(handle->unmap->handle, obj->body.otype));
145 				//FIXME introspect object?
146 			}
147 			else if(atom->type == through->forge.Bool)
148 			{
149 				lv2_log_trace(&handle->logger, "%4"PRIi64", bool  , %s\n", ev->time.frames,
150 					((const LV2_Atom_Bool *)atom)->body ? "true" : "false");
151 			}
152 			else if(atom->type == through->forge.Int)
153 			{
154 				lv2_log_trace(&handle->logger, "%4"PRIi64", int32 , %"PRIi32"\n", ev->time.frames,
155 					((const LV2_Atom_Int *)atom)->body);
156 			}
157 			else if(atom->type == through->forge.Long)
158 			{
159 				lv2_log_trace(&handle->logger, "%4"PRIi64", int64 , %"PRIi64"\n", ev->time.frames,
160 					((const LV2_Atom_Long *)atom)->body);
161 			}
162 			else if(atom->type == through->forge.Float)
163 			{
164 				lv2_log_trace(&handle->logger, "%4"PRIi64", flt32 , %f\n", ev->time.frames,
165 					((const LV2_Atom_Float *)atom)->body);
166 			}
167 			else if(atom->type == through->forge.Double)
168 			{
169 				lv2_log_trace(&handle->logger, "%4"PRIi64", flt64 , %lf\n", ev->time.frames,
170 					((const LV2_Atom_Double *)atom)->body);
171 			}
172 			else if(atom->type == through->forge.String)
173 			{
174 				lv2_log_trace(&handle->logger, "%4"PRIi64", urid  , %s\n", ev->time.frames,
175 					handle->unmap->unmap(handle->unmap->handle, ((const LV2_Atom_URID *)atom)->body));
176 			}
177 			else if(atom->type == through->forge.String)
178 			{
179 				lv2_log_trace(&handle->logger, "%4"PRIi64", string, %s\n", ev->time.frames,
180 					(const char *)LV2_ATOM_BODY_CONST(atom));
181 			}
182 			else if(atom->type == through->forge.URI)
183 			{
184 				lv2_log_trace(&handle->logger, "%4"PRIi64", uri   , %s\n", ev->time.frames,
185 					(const char *)LV2_ATOM_BODY_CONST(atom));
186 			}
187 			else if(atom->type == through->forge.Path)
188 			{
189 				lv2_log_trace(&handle->logger, "%4"PRIi64", path  , %s\n", ev->time.frames,
190 					(const char *)LV2_ATOM_BODY_CONST(atom));
191 			}
192 			//FIXME more types
193 			else
194 			{
195 				lv2_log_trace(&handle->logger, "%4"PRIi64", %s\n", ev->time.frames,
196 					handle->unmap->unmap(handle->unmap->handle, atom->type));
197 			}
198 		}
199 
200 		// copy all events to through port
201 		if(through->ref)
202 			through->ref = lv2_atom_forge_frame_time(&through->forge, frames);
203 		if(through->ref)
204 			through->ref = lv2_atom_forge_raw(&through->forge, &obj->atom, lv2_atom_total_size(&obj->atom));
205 		if(through->ref)
206 			lv2_atom_forge_pad(&through->forge, obj->atom.size);
207 
208 		if(  !props_advance(&handle->props, &notify->forge, frames, obj, &notify->ref)
209 			&& lv2_atom_forge_is_object_type(&notify->forge, obj->atom.type)
210 			&& (obj->body.otype == handle->time_position) )
211 		{
212 			const LV2_Atom_Long *time_frame = NULL;
213 			lv2_atom_object_get(obj, handle->time_frame, &time_frame, NULL);
214 			if(time_frame)
215 				handle->frame = time_frame->body - frames;
216 		}
217 	}
218 
219 	if(through->ref)
220 		lv2_atom_forge_pop(&through->forge, &through->frame[0]);
221 	else
222 	{
223 		lv2_atom_sequence_clear(through->seq);
224 
225 		if(handle->log)
226 			lv2_log_trace(&handle->logger, "through buffer overflow\n");
227 	}
228 
229 	bool has_event = notify->seq->atom.size > sizeof(LV2_Atom_Sequence_Body);
230 
231 	if(notify->ref)
232 		notify->ref = lv2_atom_forge_frame_time(&notify->forge, nsamples-1);
233 	if(notify->ref)
234 		notify->ref = lv2_atom_forge_tuple(&notify->forge, &notify->frame[1]);
235 	if(notify->ref)
236 		notify->ref = lv2_atom_forge_long(&notify->forge, handle->frame);
237 	if(notify->ref)
238 		notify->ref = lv2_atom_forge_int(&notify->forge, nsamples);
239 	if(notify->ref)
240 		notify->ref = lv2_atom_forge_sequence_head(&notify->forge, &notify->frame[2], 0);
241 
242 	// only serialize filtered events to UI
243 	LV2_ATOM_SEQUENCE_FOREACH(handle->control, ev)
244 	{
245 		const LV2_Atom_Object *obj = (const LV2_Atom_Object *)&ev->body;
246 		bool type_matches;
247 
248 		if(handle->state.filter == handle->sherlock_matchAll)
249 		{
250 			type_matches = true;
251 		}
252 		else
253 		{
254 			type_matches = lv2_atom_forge_is_object_type(&notify->forge, obj->atom.type)
255 				? (obj->body.otype == handle->state.filter)
256 				: (obj->atom.type == handle->state.filter);
257 		}
258 
259 		if(  (!handle->state.negate && type_matches)
260 			|| (handle->state.negate && !type_matches) )
261 		{
262 			has_event = true;
263 			if(notify->ref)
264 				notify->ref = lv2_atom_forge_frame_time(&notify->forge, ev->time.frames);
265 			if(notify->ref)
266 				notify->ref = lv2_atom_forge_write(&notify->forge, &ev->body, sizeof(LV2_Atom) + ev->body.size);
267 		}
268 	}
269 
270 	if(notify->ref)
271 		lv2_atom_forge_pop(&notify->forge, &notify->frame[2]);
272 	if(notify->ref)
273 		lv2_atom_forge_pop(&notify->forge, &notify->frame[1]);
274 	if(notify->ref)
275 		lv2_atom_forge_pop(&notify->forge, &notify->frame[0]);
276 	else
277 	{
278 		lv2_atom_sequence_clear(notify->seq);
279 
280 		if(handle->log)
281 			lv2_log_trace(&handle->logger, "notify buffer overflow\n");
282 	}
283 
284 	if(!has_event) // don't send anything
285 		lv2_atom_sequence_clear(notify->seq);
286 
287 	handle->frame += nsamples;
288 }
289 
290 static void
291 cleanup(LV2_Handle instance)
292 {
293 	handle_t *handle = (handle_t *)instance;
294 
295 	free(handle);
296 }
297 
298 static LV2_State_Status
299 _state_save(LV2_Handle instance, LV2_State_Store_Function store,
300 	LV2_State_Handle state, uint32_t flags,
301 	const LV2_Feature *const *features)
302 {
303 	handle_t *handle = instance;
304 
305 	return props_save(&handle->props, store, state, flags, features);
306 }
307 
308 static LV2_State_Status
309 _state_restore(LV2_Handle instance, LV2_State_Retrieve_Function retrieve,
310 	LV2_State_Handle state, uint32_t flags,
311 	const LV2_Feature *const *features)
312 {
313 	handle_t *handle = instance;
314 
315 	return props_restore(&handle->props, retrieve, state, flags, features);
316 }
317 
318 static const LV2_State_Interface state_iface = {
319 	.save = _state_save,
320 	.restore = _state_restore
321 };
322 
323 static const void*
324 extension_data(const char* uri)
325 {
326 	if(!strcmp(uri, LV2_STATE__interface))
327 		return &state_iface;
328 
329 	return NULL;
330 }
331 
332 const LV2_Descriptor atom_inspector = {
333 	.URI						= SHERLOCK_ATOM_INSPECTOR_URI,
334 	.instantiate		= instantiate,
335 	.connect_port		= connect_port,
336 	.activate				= NULL,
337 	.run						= run,
338 	.deactivate			= NULL,
339 	.cleanup				= cleanup,
340 	.extension_data	= extension_data
341 };
342