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