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