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