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