1 /* 2 * Copyright (c) 2015-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 <sherlock.h> 22 23 typedef struct _handle_t handle_t; 24 25 struct _handle_t { 26 LV2_URID_Map *map; 27 LV2_URID_Unmap *unmap; 28 LV2_Log_Log *log; 29 LV2_Log_Logger logger; 30 31 const LV2_Atom_Sequence *control; 32 craft_t through; 33 craft_t notify; 34 35 LV2_URID time_position; 36 LV2_URID time_frame; 37 LV2_URID sherlock_matchAll; 38 39 int64_t frame; 40 41 PROPS_T(props, MAX_NPROPS); 42 state_t state; 43 state_t stash; 44 }; 45 46 static LV2_Handle 47 instantiate(const LV2_Descriptor* descriptor, double rate __attribute__((unused)), 48 const char *bundle_path __attribute__((unused)), 49 const LV2_Feature *const *features) 50 { 51 int i; 52 handle_t *handle = calloc(1, sizeof(handle_t)); 53 if(!handle) 54 return NULL; 55 56 for(i=0; features[i]; i++) 57 { 58 if(!strcmp(features[i]->URI, LV2_URID__map)) 59 handle->map = features[i]->data; 60 else if(!strcmp(features[i]->URI, LV2_URID__unmap)) 61 handle->unmap = features[i]->data; 62 else if(!strcmp(features[i]->URI, LV2_LOG__log)) 63 handle->log = features[i]->data; 64 } 65 66 if(!handle->map || !handle->unmap) 67 { 68 fprintf(stderr, "%s: Host does not support urid:(un)map\n", descriptor->URI); 69 free(handle); 70 return NULL; 71 } 72 73 if(handle->log) 74 lv2_log_logger_init(&handle->logger, handle->map, handle->log); 75 76 handle->time_position = handle->map->map(handle->map->handle, LV2_TIME__Position); 77 handle->time_frame = handle->map->map(handle->map->handle, LV2_TIME__frame); 78 handle->sherlock_matchAll = handle->map->map(handle->map->handle, SHERLOCK_URI"#matchAll"); 79 80 lv2_atom_forge_init(&handle->through.forge, handle->map); 81 lv2_atom_forge_init(&handle->notify.forge, handle->map); 82 83 if(!props_init(&handle->props, descriptor->URI, 84 defs, MAX_NPROPS, &handle->state, &handle->stash, 85 handle->map, handle)) 86 { 87 fprintf(stderr, "failed to allocate property structure\n"); 88 free(handle); 89 return NULL; 90 } 91 92 return handle; 93 } 94 95 static void 96 connect_port(LV2_Handle instance, uint32_t port, void *data) 97 { 98 handle_t *handle = (handle_t *)instance; 99 100 switch(port) 101 { 102 case 0: 103 handle->control = (const LV2_Atom_Sequence *)data; 104 break; 105 case 1: 106 handle->through.seq = (LV2_Atom_Sequence *)data; 107 break; 108 case 2: 109 handle->notify.seq = (LV2_Atom_Sequence *)data; 110 break; 111 default: 112 break; 113 } 114 } 115 116 static void 117 run(LV2_Handle instance, uint32_t nsamples) 118 { 119 handle_t *handle = (handle_t *)instance; 120 craft_t *through = &handle->through; 121 craft_t *notify = &handle->notify; 122 123 uint32_t capacity = through->seq->atom.size; 124 lv2_atom_forge_set_buffer(&through->forge, through->buf, capacity); 125 through->ref = lv2_atom_forge_sequence_head(&through->forge, &through->frame[0], 0); 126 127 capacity = notify->seq->atom.size; 128 lv2_atom_forge_set_buffer(¬ify->forge, notify->buf, capacity); 129 notify->ref = lv2_atom_forge_sequence_head(¬ify->forge, ¬ify->frame[0], 0); 130 131 props_idle(&handle->props, ¬ify->forge, 0, ¬ify->ref); 132 133 LV2_ATOM_SEQUENCE_FOREACH(handle->control, ev) 134 { 135 const LV2_Atom *atom = &ev->body; 136 const LV2_Atom_Object *obj = (const LV2_Atom_Object *)&ev->body; 137 const int64_t frames = ev->time.frames; 138 139 if(handle->state.trace && handle->log) 140 { 141 if(lv2_atom_forge_is_object_type(&through->forge, atom->type)) 142 { 143 lv2_log_trace(&handle->logger, "%4"PRIi64", object, %s\n", ev->time.frames, 144 handle->unmap->unmap(handle->unmap->handle, obj->body.otype)); 145 //FIXME introspect object? 146 } 147 else if(atom->type == through->forge.Bool) 148 { 149 lv2_log_trace(&handle->logger, "%4"PRIi64", bool , %s\n", ev->time.frames, 150 ((const LV2_Atom_Bool *)atom)->body ? "true" : "false"); 151 } 152 else if(atom->type == through->forge.Int) 153 { 154 lv2_log_trace(&handle->logger, "%4"PRIi64", int32 , %"PRIi32"\n", ev->time.frames, 155 ((const LV2_Atom_Int *)atom)->body); 156 } 157 else if(atom->type == through->forge.Long) 158 { 159 lv2_log_trace(&handle->logger, "%4"PRIi64", int64 , %"PRIi64"\n", ev->time.frames, 160 ((const LV2_Atom_Long *)atom)->body); 161 } 162 else if(atom->type == through->forge.Float) 163 { 164 lv2_log_trace(&handle->logger, "%4"PRIi64", flt32 , %f\n", ev->time.frames, 165 ((const LV2_Atom_Float *)atom)->body); 166 } 167 else if(atom->type == through->forge.Double) 168 { 169 lv2_log_trace(&handle->logger, "%4"PRIi64", flt64 , %lf\n", ev->time.frames, 170 ((const LV2_Atom_Double *)atom)->body); 171 } 172 else if(atom->type == through->forge.String) 173 { 174 lv2_log_trace(&handle->logger, "%4"PRIi64", urid , %s\n", ev->time.frames, 175 handle->unmap->unmap(handle->unmap->handle, ((const LV2_Atom_URID *)atom)->body)); 176 } 177 else if(atom->type == through->forge.String) 178 { 179 lv2_log_trace(&handle->logger, "%4"PRIi64", string, %s\n", ev->time.frames, 180 (const char *)LV2_ATOM_BODY_CONST(atom)); 181 } 182 else if(atom->type == through->forge.URI) 183 { 184 lv2_log_trace(&handle->logger, "%4"PRIi64", uri , %s\n", ev->time.frames, 185 (const char *)LV2_ATOM_BODY_CONST(atom)); 186 } 187 else if(atom->type == through->forge.Path) 188 { 189 lv2_log_trace(&handle->logger, "%4"PRIi64", path , %s\n", ev->time.frames, 190 (const char *)LV2_ATOM_BODY_CONST(atom)); 191 } 192 //FIXME more types 193 else 194 { 195 lv2_log_trace(&handle->logger, "%4"PRIi64", %s\n", ev->time.frames, 196 handle->unmap->unmap(handle->unmap->handle, atom->type)); 197 } 198 } 199 200 // copy all events to through port 201 if(through->ref) 202 through->ref = lv2_atom_forge_frame_time(&through->forge, frames); 203 if(through->ref) 204 through->ref = lv2_atom_forge_raw(&through->forge, &obj->atom, lv2_atom_total_size(&obj->atom)); 205 if(through->ref) 206 lv2_atom_forge_pad(&through->forge, obj->atom.size); 207 208 if( !props_advance(&handle->props, ¬ify->forge, frames, obj, ¬ify->ref) 209 && lv2_atom_forge_is_object_type(¬ify->forge, obj->atom.type) 210 && (obj->body.otype == handle->time_position) ) 211 { 212 const LV2_Atom_Long *time_frame = NULL; 213 lv2_atom_object_get(obj, handle->time_frame, &time_frame, NULL); 214 if(time_frame) 215 handle->frame = time_frame->body - frames; 216 } 217 } 218 219 if(through->ref) 220 lv2_atom_forge_pop(&through->forge, &through->frame[0]); 221 else 222 { 223 lv2_atom_sequence_clear(through->seq); 224 225 if(handle->log) 226 lv2_log_trace(&handle->logger, "through buffer overflow\n"); 227 } 228 229 bool has_event = notify->seq->atom.size > sizeof(LV2_Atom_Sequence_Body); 230 231 if(notify->ref) 232 notify->ref = lv2_atom_forge_frame_time(¬ify->forge, nsamples-1); 233 if(notify->ref) 234 notify->ref = lv2_atom_forge_tuple(¬ify->forge, ¬ify->frame[1]); 235 if(notify->ref) 236 notify->ref = lv2_atom_forge_long(¬ify->forge, handle->frame); 237 if(notify->ref) 238 notify->ref = lv2_atom_forge_int(¬ify->forge, nsamples); 239 if(notify->ref) 240 notify->ref = lv2_atom_forge_sequence_head(¬ify->forge, ¬ify->frame[2], 0); 241 242 // only serialize filtered events to UI 243 LV2_ATOM_SEQUENCE_FOREACH(handle->control, ev) 244 { 245 const LV2_Atom_Object *obj = (const LV2_Atom_Object *)&ev->body; 246 bool type_matches; 247 248 if(handle->state.filter == handle->sherlock_matchAll) 249 { 250 type_matches = true; 251 } 252 else 253 { 254 type_matches = lv2_atom_forge_is_object_type(¬ify->forge, obj->atom.type) 255 ? (obj->body.otype == handle->state.filter) 256 : (obj->atom.type == handle->state.filter); 257 } 258 259 if( (!handle->state.negate && type_matches) 260 || (handle->state.negate && !type_matches) ) 261 { 262 has_event = true; 263 if(notify->ref) 264 notify->ref = lv2_atom_forge_frame_time(¬ify->forge, ev->time.frames); 265 if(notify->ref) 266 notify->ref = lv2_atom_forge_write(¬ify->forge, &ev->body, sizeof(LV2_Atom) + ev->body.size); 267 } 268 } 269 270 if(notify->ref) 271 lv2_atom_forge_pop(¬ify->forge, ¬ify->frame[2]); 272 if(notify->ref) 273 lv2_atom_forge_pop(¬ify->forge, ¬ify->frame[1]); 274 if(notify->ref) 275 lv2_atom_forge_pop(¬ify->forge, ¬ify->frame[0]); 276 else 277 { 278 lv2_atom_sequence_clear(notify->seq); 279 280 if(handle->log) 281 lv2_log_trace(&handle->logger, "notify buffer overflow\n"); 282 } 283 284 if(!has_event) // don't send anything 285 lv2_atom_sequence_clear(notify->seq); 286 287 handle->frame += nsamples; 288 } 289 290 static void 291 cleanup(LV2_Handle instance) 292 { 293 handle_t *handle = (handle_t *)instance; 294 295 free(handle); 296 } 297 298 static LV2_State_Status 299 _state_save(LV2_Handle instance, LV2_State_Store_Function store, 300 LV2_State_Handle state, uint32_t flags, 301 const LV2_Feature *const *features) 302 { 303 handle_t *handle = instance; 304 305 return props_save(&handle->props, store, state, flags, features); 306 } 307 308 static LV2_State_Status 309 _state_restore(LV2_Handle instance, LV2_State_Retrieve_Function retrieve, 310 LV2_State_Handle state, uint32_t flags, 311 const LV2_Feature *const *features) 312 { 313 handle_t *handle = instance; 314 315 return props_restore(&handle->props, retrieve, state, flags, features); 316 } 317 318 static const LV2_State_Interface state_iface = { 319 .save = _state_save, 320 .restore = _state_restore 321 }; 322 323 static const void* 324 extension_data(const char* uri) 325 { 326 if(!strcmp(uri, LV2_STATE__interface)) 327 return &state_iface; 328 329 return NULL; 330 } 331 332 const LV2_Descriptor atom_inspector = { 333 .URI = SHERLOCK_ATOM_INSPECTOR_URI, 334 .instantiate = instantiate, 335 .connect_port = connect_port, 336 .activate = NULL, 337 .run = run, 338 .deactivate = NULL, 339 .cleanup = cleanup, 340 .extension_data = extension_data 341 }; 342