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