1 /*
2  * Copyright (c) 2016-2021 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 
21 #include <eteroj.h>
22 #include <osc.lv2/util.h>
23 #include <osc.lv2/forge.h>
24 #include <props.h>
25 
26 #define MAX_NPROPS 2
27 #define MAX_STRLEN 512
28 
29 typedef enum _pack_format_t pack_format_t;
30 typedef struct _plugstate_t plugstate_t;
31 typedef struct _plughandle_t plughandle_t;
32 
33 enum _pack_format_t {
34 	PACK_FORMAT_MIDI = 0,
35 	PACK_FORMAT_BLOB = 1
36 };
37 
38 struct _plugstate_t {
39 	char pack_path [MAX_STRLEN];
40 	int32_t pack_format;
41 };
42 
43 struct _plughandle_t {
44 	LV2_URID_Map *map;
45 	struct {
46 		LV2_URID midi_MidiEvent;
47 	} uris;
48 
49 	const LV2_Atom_Sequence *event_in;
50 	LV2_Atom_Sequence *event_out;
51 
52 	PROPS_T(props, MAX_NPROPS);
53 	LV2_Atom_Forge forge;
54 	LV2_OSC_URID osc_urid;
55 
56 	plugstate_t state;
57 	plugstate_t stash;
58 
59 	int64_t frames;
60 	LV2_Atom_Forge_Ref ref;
61 };
62 
63 static const props_def_t defs [MAX_NPROPS] = {
64 	{
65 		.property = ETEROJ_PACK_PATH_URI,
66 		.offset = offsetof(plugstate_t, pack_path),
67 		.type = LV2_ATOM__String,
68 		.max_size = MAX_STRLEN
69 	},
70 	{
71 		.property = ETEROJ_PACK_FORMAT_URI,
72 		.offset = offsetof(plugstate_t, pack_format),
73 		.type = LV2_ATOM__Int,
74 	}
75 };
76 
77 static LV2_Handle
instantiate(const LV2_Descriptor * descriptor,double rate,const char * bundle_path,const LV2_Feature * const * features)78 instantiate(const LV2_Descriptor* descriptor, double rate, const char *bundle_path, const LV2_Feature *const *features)
79 {
80 	plughandle_t *handle = calloc(1, sizeof(plughandle_t));
81 	if(!handle)
82 		return NULL;
83 	mlock(handle, sizeof(plughandle_t));
84 
85 	for(unsigned i=0; features[i]; i++)
86 	{
87 		if(!strcmp(features[i]->URI, LV2_URID__map))
88 			handle->map = (LV2_URID_Map *)features[i]->data;
89 	}
90 
91 	if(!handle->map)
92 	{
93 		fprintf(stderr, "%s: Host does not support urid:map\n", descriptor->URI);
94 		free(handle);
95 		return NULL;
96 	}
97 
98 	handle->uris.midi_MidiEvent = handle->map->map(handle->map->handle, LV2_MIDI__MidiEvent);
99 	lv2_atom_forge_init(&handle->forge, handle->map);
100 	lv2_osc_urid_init(&handle->osc_urid, handle->map);
101 
102 	if(!props_init(&handle->props, descriptor->URI,
103 		defs, MAX_NPROPS, &handle->state, &handle->stash,
104 		handle->map, handle))
105 	{
106 		free(handle);
107 		return NULL;
108 	}
109 
110 	return handle;
111 }
112 
113 static void
connect_port(LV2_Handle instance,uint32_t port,void * data)114 connect_port(LV2_Handle instance, uint32_t port, void *data)
115 {
116 	plughandle_t *handle = (plughandle_t *)instance;
117 
118 	switch(port)
119 	{
120 		case 0:
121 			handle->event_in = (const LV2_Atom_Sequence *)data;
122 			break;
123 		case 1:
124 			handle->event_out = (LV2_Atom_Sequence *)data;
125 			break;
126 		default:
127 			break;
128 	}
129 }
130 
131 static void
_unroll(const char * path,const LV2_Atom_Tuple * arguments,void * data)132 _unroll(const char *path, const LV2_Atom_Tuple *arguments, void *data)
133 {
134 	plughandle_t *handle = data;
135 	LV2_Atom_Forge *forge = &handle->forge;
136 	LV2_Atom_Forge_Ref ref = handle->ref;
137 
138 	LV2_ATOM_TUPLE_FOREACH(arguments, itr)
139 	{
140 		bool is_midi = false;
141 
142 		switch((pack_format_t)handle->state.pack_format)
143 		{
144 			case PACK_FORMAT_MIDI:
145 				if(lv2_osc_argument_type(&handle->osc_urid, itr) == LV2_OSC_MIDI)
146 					is_midi = true;
147 				break;
148 			case PACK_FORMAT_BLOB:
149 				if(lv2_osc_argument_type(&handle->osc_urid, itr) == LV2_OSC_BLOB)
150 					is_midi = true;
151 				break;
152 		}
153 
154 		if(is_midi)
155 		{
156 			if(ref)
157 				ref = lv2_atom_forge_frame_time(forge, handle->frames);
158 			if(ref)
159 				ref = lv2_atom_forge_atom(forge, itr->size, handle->uris.midi_MidiEvent);
160 			if(ref)
161 				ref = lv2_atom_forge_raw(forge, LV2_ATOM_BODY_CONST(itr), itr->size);
162 			if(ref)
163 				lv2_atom_forge_pad(forge, itr->size);
164 		}
165 	}
166 
167 	handle->ref = ref;
168 }
169 
170 static void
run(LV2_Handle instance,uint32_t nsamples)171 run(LV2_Handle instance, uint32_t nsamples)
172 {
173 	plughandle_t *handle = (plughandle_t *)instance;
174 
175 	// prepare osc atom forge
176 	const uint32_t capacity = handle->event_out->atom.size;
177 	LV2_Atom_Forge *forge = &handle->forge;
178 	lv2_atom_forge_set_buffer(forge, (uint8_t *)handle->event_out, capacity);
179 	LV2_Atom_Forge_Frame frame;
180 	handle->ref = lv2_atom_forge_sequence_head(forge, &frame, 0);
181 
182 	props_idle(&handle->props, &handle->forge, 0, &handle->ref);
183 
184 	LV2_ATOM_SEQUENCE_FOREACH(handle->event_in, ev)
185 	{
186 		const LV2_Atom_Object *obj = (const LV2_Atom_Object *)&ev->body;
187 		handle->frames = ev->time.frames;
188 
189 		if(obj->atom.type == handle->uris.midi_MidiEvent)
190 		{
191 			// pack MIDI into OSC
192 			switch((pack_format_t)handle->state.pack_format)
193 			{
194 				case PACK_FORMAT_MIDI:
195 				{
196 					if(obj->atom.size <= 3) // OSC 'm' type does not support more :(
197 					{
198 						LV2_Atom_Forge_Frame frames [2];
199 						if(handle->ref)
200 							handle->ref = lv2_atom_forge_frame_time(forge, ev->time.frames);
201 						if(handle->ref)
202 							handle->ref = lv2_osc_forge_message_head(forge, &handle->osc_urid, frames, handle->state.pack_path);
203 						if(handle->ref)
204 							handle->ref = lv2_osc_forge_midi(forge, &handle->osc_urid, LV2_ATOM_BODY_CONST(&obj->atom), obj->atom.size);
205 						if(handle->ref)
206 							lv2_osc_forge_pop(forge, frames);
207 					}
208 
209 					break;
210 				}
211 				case PACK_FORMAT_BLOB:
212 				{
213 					LV2_Atom_Forge_Frame frames [2];
214 					if(handle->ref)
215 						handle->ref = lv2_atom_forge_frame_time(forge, ev->time.frames);
216 					if(handle->ref)
217 						handle->ref = lv2_osc_forge_message_head(forge, &handle->osc_urid, frames, handle->state.pack_path);
218 					if(handle->ref)
219 						handle->ref = lv2_osc_forge_blob(forge, &handle->osc_urid, LV2_ATOM_BODY_CONST(&obj->atom), obj->atom.size);
220 					if(handle->ref)
221 						lv2_osc_forge_pop(forge, frames);
222 
223 					break;
224 				}
225 			}
226 		}
227 		else if(!props_advance(&handle->props, &handle->forge, ev->time.frames, obj, &handle->ref))
228 		{
229 			// unpack MIDI from OSC
230 			lv2_osc_unroll(&handle->osc_urid, obj, _unroll, handle);
231 		}
232 	}
233 
234 	if(handle->ref)
235 		lv2_atom_forge_pop(forge, &frame);
236 	else
237 		lv2_atom_sequence_clear(handle->event_out);
238 }
239 
240 static void
cleanup(LV2_Handle instance)241 cleanup(LV2_Handle instance)
242 {
243 	plughandle_t *handle = (plughandle_t *)instance;
244 
245 	munlock(handle, sizeof(plughandle_t));
246 	free(handle);
247 }
248 
249 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)250 _state_save(LV2_Handle instance, LV2_State_Store_Function store,
251 	LV2_State_Handle state, uint32_t flags,
252 	const LV2_Feature *const *features)
253 {
254 	plughandle_t *handle = (plughandle_t *)instance;
255 
256 	return props_save(&handle->props, store, state, flags, features);
257 }
258 
259 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)260 _state_restore(LV2_Handle instance, LV2_State_Retrieve_Function retrieve,
261 	LV2_State_Handle state, uint32_t flags,
262 	const LV2_Feature *const *features)
263 {
264 	plughandle_t *handle = (plughandle_t *)instance;
265 
266 	return props_restore(&handle->props, retrieve, state, flags, features);
267 }
268 
269 static const LV2_State_Interface state_iface = {
270 	.save = _state_save,
271 	.restore = _state_restore
272 };
273 
274 static const void *
extension_data(const char * uri)275 extension_data(const char *uri)
276 {
277 	if(!strcmp(uri, LV2_STATE__interface))
278 		return &state_iface;
279 
280 	return NULL;
281 }
282 
283 const LV2_Descriptor eteroj_pack = {
284 	.URI						= ETEROJ_PACK_URI,
285 	.instantiate		= instantiate,
286 	.connect_port		= connect_port,
287 	.activate				= NULL,
288 	.run						= run,
289 	.deactivate			= NULL,
290 	.cleanup				= cleanup,
291 	.extension_data	= extension_data
292 };
293