1 /*
2 * Copyright (c) 2016-2017 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 voiceied 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 <inttypes.h>
20 #include <stdatomic.h>
21
22 #include <xpress.lv2/xpress.h>
23
24 #include <lv2/lv2plug.in/ns/ext/log/log.h>
25 #include <lv2/lv2plug.in/ns/ext/log/logger.h>
26
27 #define XPRESS_TEST_URI XPRESS_PREFIX"test"
28
29 #define MAX_NVOICES 32
30
31 typedef struct _targetI_t targetI_t;
32 typedef struct _targetO_t targetO_t;
33 typedef struct _plughandle_t plughandle_t;
34
35 struct _targetI_t {
36 xpress_uuid_t uuidO;
37 };
38
39 struct _targetO_t {
40 uint8_t dummy;
41 };
42
43 struct _plughandle_t {
44 LV2_URID_Map *map;
45 LV2_Log_Log *log;
46 LV2_Log_Logger logger;
47 LV2_Atom_Forge forge;
48 LV2_Atom_Forge_Ref ref;
49
50 const LV2_Atom_Sequence *event_in;
51 LV2_Atom_Sequence *event_out;
52
53 XPRESS_T(xpressI, MAX_NVOICES);
54 XPRESS_T(xpressO, MAX_NVOICES);
55 targetI_t targetI [MAX_NVOICES];
56 targetO_t targetO [MAX_NVOICES];
57 };
58
59 static void
_dump(plughandle_t * handle)60 _dump(plughandle_t *handle)
61 {
62 XPRESS_VOICE_FOREACH(&handle->xpressI, voice)
63 {
64 lv2_log_trace(&handle->logger, "\t%"PRIu32" (%"PRIu32")", voice->uuid, voice->source);
65 }
66 }
67
68 static void
_add(void * data,int64_t frames,const xpress_state_t * state,xpress_uuid_t uuid,void * target)69 _add(void *data, int64_t frames, const xpress_state_t *state,
70 xpress_uuid_t uuid, void *target)
71 {
72 plughandle_t *handle = data;
73 LV2_Atom_Forge *forge = &handle->forge;
74 targetI_t *src = target;
75
76 lv2_log_trace(&handle->logger, "ADD: %"PRIu32, uuid);
77
78 targetO_t *dst = xpress_create(&handle->xpressO, &src->uuidO);
79 (void)dst;
80
81 xpress_state_t new_state = *state;
82 new_state.pitch *= 2;
83
84 if(handle->ref)
85 handle->ref = xpress_token(&handle->xpressO, forge, frames, src->uuidO, &new_state);
86
87 _dump(handle);
88 }
89
90 static void
_set(void * data,int64_t frames,const xpress_state_t * state,xpress_uuid_t uuid,void * target)91 _set(void *data, int64_t frames, const xpress_state_t *state,
92 xpress_uuid_t uuid, void *target)
93 {
94 plughandle_t *handle = data;
95 LV2_Atom_Forge *forge = &handle->forge;
96 targetI_t *src = target;
97
98 lv2_log_trace(&handle->logger, "PUT: %"PRIu32, uuid);
99
100 targetO_t *dst = xpress_get(&handle->xpressO, src->uuidO);
101 (void)dst;
102
103 xpress_state_t new_state = *state;
104 new_state.pitch *= 2;
105
106 if(handle->ref)
107 handle->ref = xpress_token(&handle->xpressO, forge, frames, src->uuidO, &new_state);
108
109 _dump(handle);
110 }
111
112 static void
_del(void * data,int64_t frames,xpress_uuid_t uuid,void * target)113 _del(void *data, int64_t frames,
114 xpress_uuid_t uuid, void *target)
115 {
116 plughandle_t *handle = data;
117 LV2_Atom_Forge *forge = &handle->forge;
118 targetI_t *src = target;
119
120 lv2_log_trace(&handle->logger, "DEL: %"PRIu32, uuid);
121
122 xpress_free(&handle->xpressO, src->uuidO);
123
124 if(handle->ref)
125 handle->ref = xpress_alive(&handle->xpressO, forge, frames);
126
127 _dump(handle);
128 }
129
130 static const xpress_iface_t ifaceI = {
131 .size = sizeof(targetI_t),
132 .add = _add,
133 .set = _set,
134 .del = _del
135 };
136
137 static const xpress_iface_t ifaceO = {
138 .size = sizeof(targetO_t)
139 };
140
141 static LV2_Handle
instantiate(const LV2_Descriptor * descriptor,double rate,const char * bundle_path,const LV2_Feature * const * features)142 instantiate(const LV2_Descriptor* descriptor,
143 double rate __attribute__((unused)),
144 const char *bundle_path __attribute__((unused)),
145 const LV2_Feature *const *features)
146 {
147 plughandle_t *handle = calloc(1, sizeof(plughandle_t));
148 if(!handle)
149 return NULL;
150
151 xpress_map_t *voice_map = NULL;
152
153 for(unsigned i=0; features[i]; i++)
154 {
155 if(!strcmp(features[i]->URI, LV2_URID__map))
156 handle->map = features[i]->data;
157 else if(!strcmp(features[i]->URI, LV2_LOG__log))
158 handle->log = features[i]->data;
159 else if(!strcmp(features[i]->URI, XPRESS__voiceMap))
160 voice_map = features[i]->data;
161 }
162
163 if(!handle->map)
164 {
165 fprintf(stderr,
166 "%s: Host does not support urid:map\n", descriptor->URI);
167 free(handle);
168 return NULL;
169 }
170 if(!handle->log)
171 {
172 fprintf(stderr,
173 "%s: Host does not support log:log\n", descriptor->URI);
174 free(handle);
175 return NULL;
176 }
177
178 lv2_log_logger_init(&handle->logger, handle->map, handle->log);
179
180 lv2_atom_forge_init(&handle->forge, handle->map);
181
182 if(!xpress_init(&handle->xpressI, MAX_NVOICES, handle->map, voice_map,
183 XPRESS_EVENT_ALL, &ifaceI, handle->targetI, handle) )
184 {
185 fprintf(stderr, "failed to initialize xpressI structure\n");
186 free(handle);
187 return NULL;
188 }
189 if(!xpress_init(&handle->xpressO, MAX_NVOICES, handle->map, voice_map,
190 XPRESS_EVENT_NONE, &ifaceO, handle->targetO, handle) )
191 {
192 fprintf(stderr, "failed to initialize xpressO structure\n");
193 free(handle);
194 return NULL;
195 }
196
197 return handle;
198 }
199
200 static void
connect_port(LV2_Handle instance,uint32_t port,void * data)201 connect_port(LV2_Handle instance, uint32_t port, void *data)
202 {
203 plughandle_t *handle = (plughandle_t *)instance;
204
205 switch(port)
206 {
207 case 0:
208 handle->event_in = (const LV2_Atom_Sequence *)data;
209 break;
210 case 1:
211 handle->event_out = (LV2_Atom_Sequence *)data;
212 break;
213 default:
214 break;
215 }
216 }
217
218 static void
run(LV2_Handle instance,uint32_t nsamples)219 run(LV2_Handle instance, uint32_t nsamples)
220 {
221 plughandle_t *handle = instance;
222
223 uint32_t capacity = handle->event_out->atom.size;
224 LV2_Atom_Forge_Frame frame;
225 lv2_atom_forge_set_buffer(&handle->forge, (uint8_t *)handle->event_out, capacity);
226 handle->ref = lv2_atom_forge_sequence_head(&handle->forge, &frame, 0);
227
228 xpress_pre(&handle->xpressI);
229 xpress_rst(&handle->xpressO);
230
231 LV2_ATOM_SEQUENCE_FOREACH(handle->event_in, ev)
232 {
233 const LV2_Atom_Object *obj = (const LV2_Atom_Object *)&ev->body;
234
235 if(handle->ref)
236 xpress_advance(&handle->xpressI, &handle->forge, ev->time.frames, obj, &handle->ref); //TODO handle return
237 }
238
239 xpress_post(&handle->xpressI, nsamples-1);
240 if(handle->ref && !xpress_synced(&handle->xpressO))
241 handle->ref = xpress_alive(&handle->xpressO, &handle->forge, nsamples-1);
242
243 if(handle->ref)
244 lv2_atom_forge_pop(&handle->forge, &frame);
245 else
246 lv2_atom_sequence_clear(handle->event_out);
247 }
248
249 static void
cleanup(LV2_Handle instance)250 cleanup(LV2_Handle instance)
251 {
252 plughandle_t *handle = instance;
253
254 xpress_deinit(&handle->xpressI);
255 xpress_deinit(&handle->xpressO);
256 free(handle);
257 }
258
259 const LV2_Descriptor xpress_test = {
260 .URI = XPRESS_TEST_URI,
261 .instantiate = instantiate,
262 .connect_port = connect_port,
263 .activate = NULL,
264 .run = run,
265 .deactivate = NULL,
266 .cleanup = cleanup,
267 .extension_data = NULL
268 };
269
270 #ifdef _WIN32
271 __declspec(dllexport)
272 #else
273 __attribute__((visibility("default")))
274 #endif
275 const LV2_Descriptor*
lv2_descriptor(uint32_t index)276 lv2_descriptor(uint32_t index)
277 {
278 switch(index)
279 {
280 case 0:
281 return &xpress_test;
282 default:
283 return NULL;
284 }
285 }
286