atom_inspector.c 9.29 KB
Newer Older
Hanspeter Portner's avatar
Hanspeter Portner committed
1
/*
2
 * Copyright (c) 2015-2016 Hanspeter Portner (dev@open-music-kontrollers.ch)
Hanspeter Portner's avatar
Hanspeter Portner committed
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
 *
 * This is free software: you can redistribute it and/or modify
 * it under the terms of the Artistic License 2.0 as published by
 * The Perl Foundation.
 *
 * This source is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * Artistic License 2.0 for more details.
 *
 * You should have received a copy of the Artistic License 2.0
 * along the source as a COPYING file. If not, obtain it from
 * http://www.perlfoundation.org/artistic_license_2_0.
 */

#include <stdio.h>
#include <stdlib.h>

#include <sherlock.h>

typedef struct _handle_t handle_t;

struct _handle_t {
	LV2_URID_Map *map;
27 28 29 30
	LV2_URID_Unmap *unmap;
	LV2_Log_Log *log;
	LV2_Log_Logger logger;

Hanspeter Portner's avatar
Hanspeter Portner committed
31 32 33
	const LV2_Atom_Sequence *control;
	craft_t through;
	craft_t notify;
34

35 36 37 38
	LV2_URID time_position;
	LV2_URID time_frame;

	int64_t frame;
39 40 41 42

	PROPS_T(props, MAX_NPROPS);
	state_t state;
	state_t stash;
Hanspeter Portner's avatar
Hanspeter Portner committed
43 44 45 46 47 48 49 50 51 52 53 54
};

static LV2_Handle
instantiate(const LV2_Descriptor* descriptor, double rate,
	const char *bundle_path, const LV2_Feature *const *features)
{
	int i;
	handle_t *handle = calloc(1, sizeof(handle_t));
	if(!handle)
		return NULL;

	for(i=0; features[i]; i++)
55
	{
Hanspeter Portner's avatar
Hanspeter Portner committed
56
		if(!strcmp(features[i]->URI, LV2_URID__map))
57 58 59 60 61 62
			handle->map = features[i]->data;
		else if(!strcmp(features[i]->URI, LV2_URID__unmap))
			handle->unmap = features[i]->data;
		else if(!strcmp(features[i]->URI, LV2_LOG__log))
			handle->log = features[i]->data;
	}
Hanspeter Portner's avatar
Hanspeter Portner committed
63

64
	if(!handle->map || !handle->unmap)
Hanspeter Portner's avatar
Hanspeter Portner committed
65
	{
66
		fprintf(stderr, "%s: Host does not support urid:(un)map\n", descriptor->URI);
Hanspeter Portner's avatar
Hanspeter Portner committed
67 68 69 70
		free(handle);
		return NULL;
	}

71 72 73
	if(handle->log)
		lv2_log_logger_init(&handle->logger, handle->map, handle->log);

74 75 76
	handle->time_position = handle->map->map(handle->map->handle, LV2_TIME__Position);
	handle->time_frame = handle->map->map(handle->map->handle, LV2_TIME__frame);

Hanspeter Portner's avatar
Hanspeter Portner committed
77 78
	lv2_atom_forge_init(&handle->through.forge, handle->map);
	lv2_atom_forge_init(&handle->notify.forge, handle->map);
79

80
	if(!props_init(&handle->props, descriptor->URI,
Hanspeter Portner's avatar
Hanspeter Portner committed
81
		defs, MAX_NPROPS, &handle->state, &handle->stash,
82
		handle->map, handle))
83 84 85 86 87 88
	{
		fprintf(stderr, "failed to allocate property structure\n");
		free(handle);
		return NULL;
	}

Hanspeter Portner's avatar
Hanspeter Portner committed
89 90 91 92 93 94 95 96 97 98 99
	return handle;
}

static void
connect_port(LV2_Handle instance, uint32_t port, void *data)
{
	handle_t *handle = (handle_t *)instance;

	switch(port)
	{
		case 0:
Hanspeter Portner's avatar
Hanspeter Portner committed
100
			handle->control = (const LV2_Atom_Sequence *)data;
Hanspeter Portner's avatar
Hanspeter Portner committed
101
			break;
102
		case 1:
Hanspeter Portner's avatar
Hanspeter Portner committed
103
			handle->through.seq = (LV2_Atom_Sequence *)data;
104
			break;
105
		case 2:
Hanspeter Portner's avatar
Hanspeter Portner committed
106
			handle->notify.seq = (LV2_Atom_Sequence *)data;
107
			break;
Hanspeter Portner's avatar
Hanspeter Portner committed
108 109 110 111 112 113 114 115 116
		default:
			break;
	}
}

static void
run(LV2_Handle instance, uint32_t nsamples)
{
	handle_t *handle = (handle_t *)instance;
Hanspeter Portner's avatar
Hanspeter Portner committed
117 118
	craft_t *through = &handle->through;
	craft_t *notify = &handle->notify;
119

Hanspeter Portner's avatar
Hanspeter Portner committed
120 121 122
	uint32_t capacity = through->seq->atom.size;
	lv2_atom_forge_set_buffer(&through->forge, through->buf, capacity);
	through->ref = lv2_atom_forge_sequence_head(&through->forge, &through->frame[0], 0);
123

Hanspeter Portner's avatar
Hanspeter Portner committed
124 125 126
	capacity = notify->seq->atom.size;
	lv2_atom_forge_set_buffer(&notify->forge, notify->buf, capacity);
	notify->ref = lv2_atom_forge_sequence_head(&notify->forge, &notify->frame[0], 0);
127

Hanspeter Portner's avatar
Hanspeter Portner committed
128 129 130
	props_idle(&handle->props, &notify->forge, 0, &notify->ref);

	LV2_ATOM_SEQUENCE_FOREACH(handle->control, ev)
131
	{
132
		const LV2_Atom *atom = &ev->body;
133
		const LV2_Atom_Object *obj = (const LV2_Atom_Object *)&ev->body;
134 135
		const int64_t frames = ev->time.frames;

136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
		if(handle->state.trace && handle->log)
		{
			if(lv2_atom_forge_is_object_type(&through->forge, atom->type))
			{
				lv2_log_trace(&handle->logger, "%4"PRIi64", object, %s\n", ev->time.frames,
					handle->unmap->unmap(handle->unmap->handle, obj->body.otype));
				//FIXME introspect object?
			}
			else if(atom->type == through->forge.Bool)
			{
				lv2_log_trace(&handle->logger, "%4"PRIi64", bool  , %s\n", ev->time.frames,
					((const LV2_Atom_Bool *)atom)->body ? "true" : "false");
			}
			else if(atom->type == through->forge.Int)
			{
				lv2_log_trace(&handle->logger, "%4"PRIi64", int32 , %"PRIi32"\n", ev->time.frames,
					((const LV2_Atom_Int *)atom)->body);
			}
			else if(atom->type == through->forge.Long)
			{
				lv2_log_trace(&handle->logger, "%4"PRIi64", int64 , %"PRIi64"\n", ev->time.frames,
					((const LV2_Atom_Long *)atom)->body);
			}
			else if(atom->type == through->forge.Float)
			{
				lv2_log_trace(&handle->logger, "%4"PRIi64", flt32 , %f\n", ev->time.frames,
					((const LV2_Atom_Float *)atom)->body);
			}
			else if(atom->type == through->forge.Double)
			{
				lv2_log_trace(&handle->logger, "%4"PRIi64", flt64 , %lf\n", ev->time.frames,
					((const LV2_Atom_Double *)atom)->body);
			}
			else if(atom->type == through->forge.String)
			{
				lv2_log_trace(&handle->logger, "%4"PRIi64", urid  , %s\n", ev->time.frames,
					handle->unmap->unmap(handle->unmap->handle, ((const LV2_Atom_URID *)atom)->body));
			}
			else if(atom->type == through->forge.String)
			{
				lv2_log_trace(&handle->logger, "%4"PRIi64", string, %s\n", ev->time.frames,
177
					(const char *)LV2_ATOM_BODY_CONST(atom));
178 179 180 181
			}
			else if(atom->type == through->forge.URI)
			{
				lv2_log_trace(&handle->logger, "%4"PRIi64", uri   , %s\n", ev->time.frames,
182
					(const char *)LV2_ATOM_BODY_CONST(atom));
183 184 185 186
			}
			else if(atom->type == through->forge.Path)
			{
				lv2_log_trace(&handle->logger, "%4"PRIi64", path  , %s\n", ev->time.frames,
187
					(const char *)LV2_ATOM_BODY_CONST(atom));
188 189 190 191 192 193 194 195 196
			}
			//FIXME more types
			else
			{
				lv2_log_trace(&handle->logger, "%4"PRIi64", %s\n", ev->time.frames,
					handle->unmap->unmap(handle->unmap->handle, atom->type));
			}
		}

197
		// copy all events to through port
Hanspeter Portner's avatar
Hanspeter Portner committed
198 199 200 201 202 203 204 205 206
		if(through->ref)
			through->ref = lv2_atom_forge_frame_time(&through->forge, frames);
		if(through->ref)
			through->ref = lv2_atom_forge_raw(&through->forge, &obj->atom, lv2_atom_total_size(&obj->atom));
		if(through->ref)
			lv2_atom_forge_pad(&through->forge, obj->atom.size);

		if(  !props_advance(&handle->props, &notify->forge, frames, obj, &notify->ref)
			&& lv2_atom_forge_is_object_type(&notify->forge, obj->atom.type)
207
			&& (obj->body.otype == handle->time_position) )
208
		{
209 210 211 212
			const LV2_Atom_Long *time_frame = NULL;
			lv2_atom_object_get(obj, handle->time_frame, &time_frame, NULL);
			if(time_frame)
				handle->frame = time_frame->body - frames;
213 214 215
		}
	}

Hanspeter Portner's avatar
Hanspeter Portner committed
216 217
	if(through->ref)
		lv2_atom_forge_pop(&through->forge, &through->frame[0]);
218
	else
219
	{
Hanspeter Portner's avatar
Hanspeter Portner committed
220 221
		lv2_atom_sequence_clear(through->seq);

222 223 224 225
		if(handle->log)
			lv2_log_trace(&handle->logger, "through buffer overflow\n");
	}

Hanspeter Portner's avatar
Hanspeter Portner committed
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240
	bool has_event = notify->seq->atom.size > sizeof(LV2_Atom_Sequence_Body);

	if(notify->ref)
		notify->ref = lv2_atom_forge_frame_time(&notify->forge, nsamples-1);
	if(notify->ref)
		notify->ref = lv2_atom_forge_tuple(&notify->forge, &notify->frame[1]);
	if(notify->ref)
		notify->ref = lv2_atom_forge_long(&notify->forge, handle->frame);
	if(notify->ref)
		notify->ref = lv2_atom_forge_int(&notify->forge, nsamples);
	if(notify->ref)
		notify->ref = lv2_atom_forge_sequence_head(&notify->forge, &notify->frame[2], 0);

	// only serialize filtered events to UI
	LV2_ATOM_SEQUENCE_FOREACH(handle->control, ev)
241
	{
242 243
		const LV2_Atom_Object *obj = (const LV2_Atom_Object *)&ev->body;

244 245 246
		const bool type_matches = lv2_atom_forge_is_object_type(&notify->forge, obj->atom.type)
			? (obj->body.otype == handle->state.filter)
			: (obj->atom.type == handle->state.filter);
247

248 249
		if(  (!handle->state.negate && type_matches)
			|| (handle->state.negate && !type_matches) )
Hanspeter Portner's avatar
Hanspeter Portner committed
250 251 252 253 254 255 256
		{
			has_event = true;
			if(notify->ref)
				notify->ref = lv2_atom_forge_frame_time(&notify->forge, ev->time.frames);
			if(notify->ref)
				notify->ref = lv2_atom_forge_write(&notify->forge, &ev->body, sizeof(LV2_Atom) + ev->body.size);
		}
257
	}
258

Hanspeter Portner's avatar
Hanspeter Portner committed
259 260 261 262 263 264
	if(notify->ref)
		lv2_atom_forge_pop(&notify->forge, &notify->frame[2]);
	if(notify->ref)
		lv2_atom_forge_pop(&notify->forge, &notify->frame[1]);
	if(notify->ref)
		lv2_atom_forge_pop(&notify->forge, &notify->frame[0]);
265
	else
266
	{
Hanspeter Portner's avatar
Hanspeter Portner committed
267 268
		lv2_atom_sequence_clear(notify->seq);

269 270 271 272
		if(handle->log)
			lv2_log_trace(&handle->logger, "notify buffer overflow\n");
	}

Hanspeter Portner's avatar
Hanspeter Portner committed
273 274
	if(!has_event) // don't send anything
		lv2_atom_sequence_clear(notify->seq);
275

276
	handle->frame += nsamples;
Hanspeter Portner's avatar
Hanspeter Portner committed
277 278 279 280 281 282 283 284 285 286
}

static void
cleanup(LV2_Handle instance)
{
	handle_t *handle = (handle_t *)instance;

	free(handle);
}

287 288 289 290 291 292 293
static LV2_State_Status
_state_save(LV2_Handle instance, LV2_State_Store_Function store,
	LV2_State_Handle state, uint32_t flags,
	const LV2_Feature *const *features)
{
	handle_t *handle = instance;

294
	return props_save(&handle->props, store, state, flags, features);
295 296 297 298 299 300 301 302 303
}

static LV2_State_Status
_state_restore(LV2_Handle instance, LV2_State_Retrieve_Function retrieve,
	LV2_State_Handle state, uint32_t flags,
	const LV2_Feature *const *features)
{
	handle_t *handle = instance;

304
	return props_restore(&handle->props, retrieve, state, flags, features);
305 306 307 308 309 310 311
}

static const LV2_State_Interface state_iface = {
	.save = _state_save,
	.restore = _state_restore
};

312 313
static const void*
extension_data(const char* uri)
314 315 316
{
	if(!strcmp(uri, LV2_STATE__interface))
		return &state_iface;
317

318 319 320
	return NULL;
}

Hanspeter Portner's avatar
Hanspeter Portner committed
321 322 323 324
const LV2_Descriptor atom_inspector = {
	.URI						= SHERLOCK_ATOM_INSPECTOR_URI,
	.instantiate		= instantiate,
	.connect_port		= connect_port,
325
	.activate				= NULL,
Hanspeter Portner's avatar
Hanspeter Portner committed
326
	.run						= run,
327
	.deactivate			= NULL,
Hanspeter Portner's avatar
Hanspeter Portner committed
328
	.cleanup				= cleanup,
329
	.extension_data	= extension_data
Hanspeter Portner's avatar
Hanspeter Portner committed
330
};