1 /*
2  * Copyright (c) 2015 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 
20 #include <props.h>
21 
22 #include <lv2/lv2plug.in/ns/ext/log/log.h>
23 #include <lv2/lv2plug.in/ns/ext/log/logger.h>
24 
25 #define PROPS_PREFIX		"http://open-music-kontrollers.ch/lv2/props#"
26 #define PROPS_TEST_URI	PROPS_PREFIX"test"
27 
28 #define MAX_NPROPS 7
29 #define MAX_STRLEN 256
30 
31 typedef struct _plugstate_t plugstate_t;
32 typedef struct _plughandle_t plughandle_t;
33 
34 struct _plugstate_t {
35 	int32_t val1;
36 	int64_t val2;
37 	float val3;
38 	double val4;
39 	char val5 [MAX_STRLEN];
40 	char val6 [MAX_STRLEN];
41 	uint8_t val7 [MAX_STRLEN];
42 };
43 
44 struct _plughandle_t {
45 	LV2_URID_Map *map;
46 	LV2_Log_Log *log;
47 	LV2_Log_Logger logger;
48 	LV2_Atom_Forge forge;
49 	LV2_Atom_Forge_Ref ref;
50 
51 	PROPS_T(props, MAX_NPROPS);
52 	plugstate_t state;
53 	plugstate_t stash;
54 
55 	struct {
56 		LV2_URID val2;
57 		LV2_URID val4;
58 	} urid;
59 
60 	const LV2_Atom_Sequence *event_in;
61 	LV2_Atom_Sequence *event_out;
62 };
63 
64 static void
_intercept(void * data,int64_t frames,props_impl_t * impl)65 _intercept(void *data, int64_t frames __attribute__((unused)), props_impl_t *impl)
66 {
67 	plughandle_t *handle = data;
68 
69 	lv2_log_trace(&handle->logger, "SET     : %s\n", impl->def->property);
70 }
71 
72 static void
_intercept_stat1(void * data,int64_t frames,props_impl_t * impl)73 _intercept_stat1(void *data, int64_t frames, props_impl_t *impl)
74 {
75 	plughandle_t *handle = data;
76 
77 	_intercept(data, frames, impl);
78 
79 	handle->state.val2 = handle->state.val1 * 2;
80 
81 	props_set(&handle->props, &handle->forge, frames, handle->urid.val2, &handle->ref);
82 }
83 
84 static void
_intercept_stat3(void * data,int64_t frames,props_impl_t * impl)85 _intercept_stat3(void *data, int64_t frames, props_impl_t *impl)
86 {
87 	plughandle_t *handle = data;
88 
89 	_intercept(data, frames, impl);
90 
91 	handle->state.val4 = handle->state.val3 * 2;
92 
93 	props_set(&handle->props, &handle->forge, frames, handle->urid.val4, &handle->ref);
94 }
95 
96 static void
_intercept_stat6(void * data,int64_t frames,props_impl_t * impl)97 _intercept_stat6(void *data, int64_t frames, props_impl_t *impl)
98 {
99 	plughandle_t *handle = data;
100 
101 	_intercept(data, frames, impl);
102 
103 	const char *path = strstr(handle->state.val6, "file://")
104 		? handle->state.val6 + 7 // skip "file://"
105 		: handle->state.val6;
106 	FILE *f = fopen(path, "wb"); // create empty file
107 	if(f)
108 		fclose(f);
109 }
110 
111 static const props_def_t defs [MAX_NPROPS] = {
112 	{
113 		.property = PROPS_PREFIX"statInt",
114 		.offset = offsetof(plugstate_t, val1),
115 		.type = LV2_ATOM__Int,
116 		.event_cb = _intercept_stat1,
117 	},
118 	{
119 		.property = PROPS_PREFIX"statLong",
120 		.access = LV2_PATCH__readable,
121 		.offset = offsetof(plugstate_t, val2),
122 		.type = LV2_ATOM__Long,
123 		.event_cb = _intercept,
124 	},
125 	{
126 		.property = PROPS_PREFIX"statFloat",
127 		.offset = offsetof(plugstate_t, val3),
128 		.type = LV2_ATOM__Float,
129 		.event_cb = _intercept_stat3,
130 	},
131 	{
132 		.property = PROPS_PREFIX"statDouble",
133 		.access = LV2_PATCH__readable,
134 		.offset = offsetof(plugstate_t, val4),
135 		.type = LV2_ATOM__Double,
136 		.event_cb = _intercept,
137 	},
138 	{
139 		.property = PROPS_PREFIX"statString",
140 		.offset = offsetof(plugstate_t, val5),
141 		.type = LV2_ATOM__String,
142 		.event_cb = _intercept,
143 		.max_size = MAX_STRLEN // strlen
144 	},
145 	{
146 		.property = PROPS_PREFIX"statPath",
147 		.offset = offsetof(plugstate_t, val6),
148 		.type = LV2_ATOM__Path,
149 		.event_cb = _intercept_stat6,
150 		.max_size = MAX_STRLEN // strlen
151 	},
152 	{
153 		.property = PROPS_PREFIX"statChunk",
154 		.offset = offsetof(plugstate_t, val7),
155 		.type = LV2_ATOM__Chunk,
156 		.event_cb = _intercept,
157 		.max_size = MAX_STRLEN // strlen
158 	}
159 };
160 
161 static LV2_Handle
instantiate(const LV2_Descriptor * descriptor,double rate,const char * bundle_path,const LV2_Feature * const * features)162 instantiate(const LV2_Descriptor* descriptor,
163 	double rate __attribute__((unused)),
164 	const char *bundle_path __attribute__((unused)),
165 	const LV2_Feature *const *features)
166 {
167 	plughandle_t *handle = calloc(1, sizeof(plughandle_t));
168 	if(!handle)
169 		return NULL;
170 
171 	for(unsigned i=0; features[i]; i++)
172 	{
173 		if(!strcmp(features[i]->URI, LV2_URID__map))
174 			handle->map = features[i]->data;
175 		else if(!strcmp(features[i]->URI, LV2_LOG__log))
176 			handle->log = features[i]->data;
177 	}
178 
179 	if(!handle->map)
180 	{
181 		fprintf(stderr,
182 			"%s: Host does not support urid:map\n", descriptor->URI);
183 		free(handle);
184 		return NULL;
185 	}
186 	if(!handle->log)
187 	{
188 		fprintf(stderr,
189 			"%s: Host does not support log:log\n", descriptor->URI);
190 		free(handle);
191 		return NULL;
192 	}
193 
194 	lv2_log_logger_init(&handle->logger, handle->map, handle->log);
195 	lv2_atom_forge_init(&handle->forge, handle->map);
196 
197 	if(!props_init(&handle->props, descriptor->URI,
198 		defs, MAX_NPROPS, &handle->state, &handle->stash,
199 		handle->map, handle))
200 	{
201 		lv2_log_error(&handle->logger, "failed to initialize property structure\n");
202 		free(handle);
203 		return NULL;
204 	}
205 
206 	handle->urid.val2 = props_map(&handle->props, PROPS_PREFIX"statLong");
207 	handle->urid.val4 = props_map(&handle->props, PROPS_PREFIX"statDouble");
208 
209 	return handle;
210 }
211 
212 static void
connect_port(LV2_Handle instance,uint32_t port,void * data)213 connect_port(LV2_Handle instance, uint32_t port, void *data)
214 {
215 	plughandle_t *handle = (plughandle_t *)instance;
216 
217 	switch(port)
218 	{
219 		case 0:
220 			handle->event_in = (const LV2_Atom_Sequence *)data;
221 			break;
222 		case 1:
223 			handle->event_out = (LV2_Atom_Sequence *)data;
224 			break;
225 		default:
226 			break;
227 	}
228 }
229 
230 static void
run(LV2_Handle instance,uint32_t nsamples)231 run(LV2_Handle instance, uint32_t nsamples __attribute__((unused)))
232 {
233 	plughandle_t *handle = instance;
234 
235 	uint32_t capacity = handle->event_out->atom.size;
236 	LV2_Atom_Forge_Frame frame;
237 	lv2_atom_forge_set_buffer(&handle->forge, (uint8_t *)handle->event_out, capacity);
238 	handle->ref = lv2_atom_forge_sequence_head(&handle->forge, &frame, 0);
239 
240 	props_idle(&handle->props, &handle->forge, 0, &handle->ref);
241 
242 	LV2_ATOM_SEQUENCE_FOREACH(handle->event_in, ev)
243 	{
244 		const LV2_Atom_Object *obj = (const LV2_Atom_Object *)&ev->body;
245 
246 		if(handle->ref)
247 			props_advance(&handle->props, &handle->forge, ev->time.frames, obj, &handle->ref);
248 	}
249 
250 	if(handle->ref)
251 		lv2_atom_forge_pop(&handle->forge, &frame);
252 	else
253 		lv2_atom_sequence_clear(handle->event_out);
254 }
255 
256 static void
cleanup(LV2_Handle instance)257 cleanup(LV2_Handle instance)
258 {
259 	plughandle_t *handle = instance;
260 
261 	free(handle);
262 }
263 
264 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)265 _state_save(LV2_Handle instance, LV2_State_Store_Function store,
266 	LV2_State_Handle state, uint32_t flags,
267 	const LV2_Feature *const *features)
268 {
269 	plughandle_t *handle = (plughandle_t *)instance;
270 
271 	return props_save(&handle->props, store, state, flags, features);
272 }
273 
274 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)275 _state_restore(LV2_Handle instance, LV2_State_Retrieve_Function retrieve,
276 	LV2_State_Handle state, uint32_t flags,
277 	const LV2_Feature *const *features)
278 {
279 	plughandle_t *handle = (plughandle_t *)instance;
280 
281 	return props_restore(&handle->props, retrieve, state, flags, features);
282 }
283 
284 LV2_State_Interface state_iface = {
285 	.save = _state_save,
286 	.restore = _state_restore
287 };
288 
289 static const void *
extension_data(const char * uri)290 extension_data(const char *uri)
291 {
292 	if(!strcmp(uri, LV2_STATE__interface))
293 		return &state_iface;
294 	return NULL;
295 }
296 
297 const LV2_Descriptor props_test = {
298 	.URI						= PROPS_TEST_URI,
299 	.instantiate		= instantiate,
300 	.connect_port		= connect_port,
301 	.activate				= NULL,
302 	.run						= run,
303 	.deactivate			= NULL,
304 	.cleanup				= cleanup,
305 	.extension_data	= extension_data
306 };
307 
308 #ifdef _WIN32
309 __declspec(dllexport)
310 #else
311 __attribute__((visibility("default")))
312 #endif
313 const LV2_Descriptor*
lv2_descriptor(uint32_t index)314 lv2_descriptor(uint32_t index)
315 {
316 	switch(index)
317 	{
318 		case 0:
319 			return &props_test;
320 		default:
321 			return NULL;
322 	}
323 }
324