1 /*
2 * Copyright (c) 2015-2016 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 <orbit.h>
23 #include <timely.h>
24 #include <props.h>
25
26 #define MAX_NPROPS 7
27
28 #define BUF_SIZE 0x1000000 // 16 MB
29 #define BUF_PERCENT (100.f / (BUF_SIZE - sizeof(LV2_Atom)))
30
31 typedef enum _punchmode_t punchmode_t;
32 typedef struct _plugstate_t plugstate_t;
33 typedef struct _plughandle_t plughandle_t;
34
35 enum _punchmode_t {
36 PUNCH_BEAT = 0,
37 PUNCH_BAR = 1
38 };
39
40 struct _plugstate_t {
41 int32_t punch;
42 int32_t width;
43 int32_t mute;
44 int32_t switsch;
45
46 int32_t play_capacity;
47 int32_t rec_capacity;
48 int32_t position;
49 };
50
51 struct _plughandle_t {
52 LV2_URID_Map *map;
53 LV2_Atom_Forge forge;
54 LV2_Atom_Forge_Ref ref;
55
56 struct {
57 LV2_URID play_capacity;
58 LV2_URID rec_capacity;
59 LV2_URID position;
60 LV2_URID beat_time;
61 } urid;
62
63 timely_t timely;
64
65 plugstate_t state;
66 plugstate_t stash;
67
68 const LV2_Atom_Sequence *event_in;
69 LV2_Atom_Sequence *event_out;
70
71 float window;
72 int64_t offset;
73 int64_t last;
74
75 PROPS_T(props, MAX_NPROPS);
76
77 unsigned play;
78 bool rolling;
79 bool mute;
80 uint8_t buf [2][BUF_SIZE];
81
82 LV2_Atom_Event *play_ev_next;
83 LV2_Atom_Event *play_ev_prev;
_seq_spin_lock(plughandle_t * handle)84
85 LV2_Atom_Event *rec_ev_next;
86 LV2_Atom_Event *rec_ev_prev;
87 };
88
89 static inline void
90 _window_refresh(plughandle_t *handle)
91 {
92 timely_t *timely = &handle->timely;
_seq_try_lock(plughandle_t * handle)93
94 if(handle->state.punch == PUNCH_BEAT)
95 handle->window = 100.f / (handle->state.width * TIMELY_FRAMES_PER_BEAT(timely));
96 else if(handle->state.punch == PUNCH_BAR)
97 handle->window = 100.f / (handle->state.width * TIMELY_FRAMES_PER_BAR(timely));
98 }
_seq_unlock(plughandle_t * handle)99
100 static void
101 _intercept(void *data, int64_t frames, props_impl_t *impl)
102 {
103 plughandle_t *handle = data;
104
105 _window_refresh(handle);
106 }
107
108 static const props_def_t defs [MAX_NPROPS] = {
109 {
110 .property = ORBIT_URI"#looper_punch",
111 .offset = offsetof(plugstate_t, punch),
112 .type = LV2_ATOM__Int,
113 .event_cb = _intercept
114 },
115 {
116 .property = ORBIT_URI"#looper_width",
117 .offset = offsetof(plugstate_t, width),
118 .type = LV2_ATOM__Int,
119 .event_cb = _intercept
120 },
121 {
122 .property = ORBIT_URI"#looper_mute",
123 .offset = offsetof(plugstate_t, mute),
_request(plughandle_t * handle,int32_t size)124 .type = LV2_ATOM__Bool,
125 },
126 {
127 .property = ORBIT_URI"#looper_switch",
128 .offset = offsetof(plugstate_t, switsch),
129 .type = LV2_ATOM__Bool,
130 },
131 {
132 .property = ORBIT_URI"#looper_play_capacity",
133 .offset = offsetof(plugstate_t, play_capacity),
134 .access = LV2_PATCH__readable,
135 .type = LV2_ATOM__Int,
136 },
137 {
138 .property = ORBIT_URI"#looper_rec_capacity",
139 .offset = offsetof(plugstate_t, rec_capacity),
140 .access = LV2_PATCH__readable,
_play(plughandle_t * handle,int64_t to,uint32_t capacity)141 .type = LV2_ATOM__Int,
142 },
143 {
144 .property = ORBIT_URI"#looper_position",
145 .offset = offsetof(plugstate_t, position),
146 .access = LV2_PATCH__readable,
147 .type = LV2_ATOM__Int,
148 }
149 };
150
151 #define LV2_ATOM_SEQUENCE_FOREACH_FROM(seq, from, iter) \
152 for (LV2_Atom_Event* (iter) = (from); \
153 !lv2_atom_sequence_is_end(&(seq)->body, (seq)->atom.size, (iter)); \
154 (iter) = lv2_atom_sequence_next(iter))
155
156 static inline void
157 _play(plughandle_t *handle, int64_t to, uint32_t capacity)
158 {
159 const LV2_Atom_Sequence *play_seq = (LV2_Atom_Sequence *)handle->buf[handle->play];
160
161 const int64_t rel = handle->offset - to; // beginning of current period
162
163 if(handle->play_ev_next)
164 {
165 LV2_ATOM_SEQUENCE_FOREACH_FROM(play_seq, handle->play_ev_next, ev)
166 {
167 const int64_t beat_frames = ev->time.beats * TIMELY_FRAMES_PER_BEAT(&handle->timely);
168
169 if(beat_frames >= handle->offset)
170 break; // event not part of this period
171
172 const int64_t frames = beat_frames - rel;
173
174 // check for time jump! skip out-of-order event, as it probably has already been forged...
175 if(frames >= handle->last) //TODO can this be solved more elegantly?
_rec(plughandle_t * handle,const LV2_Atom_Event * ev)176 {
177 // append event
178 const LV2_Atom *atom = &ev->body;
179 if(handle->ref)
180 handle->ref = lv2_atom_forge_frame_time(&handle->forge, frames);
181 if(handle->ref)
182 handle->ref = lv2_atom_forge_write(&handle->forge, atom, lv2_atom_total_size(atom));
183
184 handle->last = frames; // advance frame time head
185 }
186
187 handle->play_ev_prev = ev;
188 handle->play_ev_next = lv2_atom_sequence_next(ev);
189 }
190 }
191 }
192
193 static inline void
194 _rec(plughandle_t *handle, const LV2_Atom_Event *ev)
195 {
196 LV2_Atom_Sequence *rec_seq = (LV2_Atom_Sequence *)handle->buf[!handle->play];
197
198 LV2_Atom_Event *e = lv2_atom_sequence_append_event(rec_seq, BUF_SIZE, ev);
199 if(e)
200 {
201 e->time.beats = handle->offset / TIMELY_FRAMES_PER_BEAT(&handle->timely);
202
203 handle->rec_ev_prev = handle->rec_ev_next;
204 handle->rec_ev_next = e;
205 }
206 else
207 {
208 // overflow
209 }
210 }
211
212 static inline void
213 _reposition_play(plughandle_t *handle)
214 {
215 LV2_Atom_Sequence *play_seq = (LV2_Atom_Sequence *)handle->buf[handle->play];
216 LV2_Atom_Event *from = lv2_atom_sequence_begin(&play_seq->body);
217
218 if(handle->play_ev_prev && handle->play_ev_next)
_reposition_play(plughandle_t * handle)219 {
220 const int64_t beat_frames = handle->play_ev_prev->time.beats * TIMELY_FRAMES_PER_BEAT(&handle->timely);
221
222 if(beat_frames < handle->offset)
223 from = handle->play_ev_prev; // search from here, not beginning
224 else
225 ;//printf("play not positioned\n");
226 }
227
228 LV2_ATOM_SEQUENCE_FOREACH_FROM(play_seq, from, ev)
229 {
230 const int64_t beat_frames = ev->time.beats * TIMELY_FRAMES_PER_BEAT(&handle->timely);
231
232 if(beat_frames >= handle->offset)
233 {
234 // reposition here
235 handle->play_ev_prev = handle->play_ev_next;
236 handle->play_ev_next = ev;
237
238 return;
_reposition_rec(plughandle_t * handle)239 }
240 }
241
242 //printf("play null\n");
243 handle->play_ev_prev = NULL;
244 handle->play_ev_next = NULL;
245 }
246
247 static inline void
248 _reposition_rec(plughandle_t *handle)
249 {
250 LV2_Atom_Sequence *rec_seq = (LV2_Atom_Sequence *)handle->buf[!handle->play];
251 LV2_Atom_Event *from = lv2_atom_sequence_begin(&rec_seq->body);
252
253 if(handle->rec_ev_prev && handle->rec_ev_next)
254 {
255 const int64_t beat_frames = handle->rec_ev_prev->time.beats * TIMELY_FRAMES_PER_BEAT(&handle->timely);
256
_cb(timely_t * timely,int64_t frames,LV2_URID type,void * data)257 if(beat_frames < handle->offset)
258 from = handle->rec_ev_prev; // search from here, not beginning
259 else
260 ;//printf("rec not positioned\n");
261 }
262
263 LV2_ATOM_SEQUENCE_FOREACH_FROM(rec_seq, from, ev)
264 {
265 const int64_t beat_frames = ev->time.beats * TIMELY_FRAMES_PER_BEAT(&handle->timely);
266
267 if(beat_frames >= handle->offset)
268 {
269 handle->rec_ev_prev = handle->rec_ev_next; //FIXME check
270 handle->rec_ev_next = ev; //FIXME check
271
272 // truncate sequence here
273 rec_seq->atom.size = (uintptr_t)ev - (uintptr_t)&rec_seq->body;
274
275 return;
276 }
277 }
278
279 //printf("rec null\n");
instantiate(const LV2_Descriptor * descriptor,double rate,const char * bundle_path,const LV2_Feature * const * features)280 handle->rec_ev_prev = NULL;
281 handle->rec_ev_next = NULL;
282 }
283
284 static void
285 _cb(timely_t *timely, int64_t frames, LV2_URID type, void *data)
286 {
287 plughandle_t *handle = data;
288
289 if(type == TIMELY_URI_SPEED(timely))
290 {
291 handle->rolling = TIMELY_SPEED(timely) > 0.f ? true : false;
292 }
293 else if(type == TIMELY_URI_BAR_BEAT(timely))
294 {
295 double beats = (double)TIMELY_BAR(timely) * TIMELY_BEATS_PER_BAR(timely)
296 + TIMELY_BAR_BEAT_RAW(timely);
297
298 if(handle->state.punch == PUNCH_BEAT)
299 handle->offset = fmod(beats, handle->state.width) * TIMELY_FRAMES_PER_BEAT(timely);
300 else if(handle->state.punch == PUNCH_BAR)
301 handle->offset = fmod(beats, handle->state.width * TIMELY_BEATS_PER_BAR(timely))
302 * TIMELY_FRAMES_PER_BEAT(timely);
303
304 if(handle->offset == 0)
305 {
306 if(handle->state.switsch)
307 handle->play ^= 1;
308
309 handle->mute = handle->state.mute;
310 }
311
312 if(beats == 0.0) // clear sequence buffers when transport is rewound
313 {
314 LV2_Atom_Sequence *play_seq = (LV2_Atom_Sequence *)handle->buf[handle->play];
315 LV2_Atom_Sequence *rec_seq = (LV2_Atom_Sequence *)handle->buf[!handle->play];
316
317 lv2_atom_sequence_clear(play_seq);
318 lv2_atom_sequence_clear(rec_seq);
319 }
320
321 _reposition_rec(handle);
322 _reposition_play(handle);
323 }
324
325 _window_refresh(handle);
326 }
327
328 static LV2_Handle
329 instantiate(const LV2_Descriptor* descriptor, double rate,
330 const char *bundle_path, const LV2_Feature *const *features)
331 {
332 plughandle_t *handle = calloc(1, sizeof(plughandle_t));
333 if(!handle)
334 return NULL;
335 mlock(handle, sizeof(plughandle_t));
336
337 for(unsigned i=0; features[i]; i++)
338 {
connect_port(LV2_Handle instance,uint32_t port,void * data)339 if(!strcmp(features[i]->URI, LV2_URID__map))
340 handle->map = features[i]->data;
341 }
342
343 if(!handle->map)
344 {
345 fprintf(stderr,
346 "%s: Host does not support urid:map\n", descriptor->URI);
347 free(handle);
348 return NULL;
349 }
350
351 handle->urid.beat_time = handle->map->map(handle->map->handle, LV2_ATOM__beatTime);
352
353 timely_mask_t mask = TIMELY_MASK_BAR_BEAT
354 //| TIMELY_MASK_BAR
355 | TIMELY_MASK_BEAT_UNIT
356 | TIMELY_MASK_BEATS_PER_BAR
activate(LV2_Handle instance)357 | TIMELY_MASK_BEATS_PER_MINUTE
358 | TIMELY_MASK_FRAMES_PER_SECOND
359 | TIMELY_MASK_SPEED
360 | TIMELY_MASK_BAR_BEAT_WHOLE;
361 timely_init(&handle->timely, handle->map, rate, mask, _cb, handle);
362 lv2_atom_forge_init(&handle->forge, handle->map);
363
364 if(!props_init(&handle->props, descriptor->URI,
365 defs, MAX_NPROPS, &handle->state, &handle->stash,
366 handle->map, handle))
367 {
368 fprintf(stderr, "failed to initialize property structure\n");
369 free(handle);
370 return NULL;
371 }
372
373 handle->urid.play_capacity = props_map(&handle->props, ORBIT_URI"#looper_play_capacity");
374 handle->urid.rec_capacity = props_map(&handle->props, ORBIT_URI"#looper_rec_capacity");
375 handle->urid.position = props_map(&handle->props, ORBIT_URI"#looper_position");
376
377 return handle;
378 }
379
380 static void
381 connect_port(LV2_Handle instance, uint32_t port, void *data)
382 {
run(LV2_Handle instance,uint32_t nsamples)383 plughandle_t *handle = (plughandle_t *)instance;
384
385 switch(port)
386 {
387 case 0:
388 handle->event_in = (const LV2_Atom_Sequence *)data;
389 break;
390 case 1:
391 handle->event_out = (LV2_Atom_Sequence *)data;
392 break;
393 default:
394 break;
395 }
396 }
397
398 static void
399 activate(LV2_Handle instance)
400 {
401 plughandle_t *handle = instance;
402
403 handle->offset = 0.f;
404 handle->play = 0;
405 handle->mute = false;
406 handle->rolling = false;
407
408 LV2_Atom_Sequence *play_seq = (LV2_Atom_Sequence *)handle->buf[handle->play];
409 LV2_Atom_Sequence *rec_seq = (LV2_Atom_Sequence *)handle->buf[!handle->play];
410
411 play_seq->atom.type = handle->forge.Sequence;
412 play_seq->atom.size = sizeof(LV2_Atom_Sequence_Body);
413 play_seq->body.unit = handle->urid.beat_time;
414 play_seq->body.pad = 0;
415
416 rec_seq->atom.type = handle->forge.Sequence;
417 rec_seq->atom.size = sizeof(LV2_Atom_Sequence_Body);
418 rec_seq->body.unit = handle->urid.beat_time;
419 rec_seq->body.pad = 0;
420 }
421
422 static void
423 run(LV2_Handle instance, uint32_t nsamples)
424 {
425 plughandle_t *handle = instance;
426
427 handle->last = 0; // reset frame time head
428
429 const uint32_t capacity = handle->event_out->atom.size;
430 LV2_Atom_Forge_Frame frame;
431 lv2_atom_forge_set_buffer(&handle->forge, (uint8_t *)handle->event_out, capacity);
432 handle->ref = lv2_atom_forge_sequence_head(&handle->forge, &frame, 0);
433
434 props_idle(&handle->props, &handle->forge, 0, &handle->ref);
435
436 int64_t last_t = 0;
437 LV2_ATOM_SEQUENCE_FOREACH(handle->event_in, ev)
deactivate(LV2_Handle instance)438 {
439 if(handle->rolling)
440 handle->offset += ev->time.frames - last_t;
441
442 const LV2_Atom_Object *obj = (const LV2_Atom_Object *)&ev->body;
443 int handled = timely_advance(&handle->timely, obj, last_t, ev->time.frames);
444 if(!handled)
445 handled = props_advance(&handle->props, &handle->forge, ev->time.frames, obj, &handle->ref);
446
447 if(!handled && handle->rolling)
448 _rec(handle, ev); // dont' record time position signals and patch messages
449
450 if(!handle->mute && handle->rolling)
451 _play(handle, ev->time.frames, capacity);
452
453 last_t = ev->time.frames;
454 }
455
456 if(handle->rolling)
457 handle->offset += nsamples - last_t;
_state_save(LV2_Handle instance,LV2_State_Store_Function store,LV2_State_Handle state,uint32_t flags,const LV2_Feature * const * features)458 timely_advance(&handle->timely, NULL, last_t, nsamples);
459 if(!handle->mute && handle->rolling)
460 _play(handle, nsamples, capacity);
461
462 LV2_Atom_Sequence *play_seq = (LV2_Atom_Sequence *)handle->buf[handle->play];
463 LV2_Atom_Sequence *rec_seq = (LV2_Atom_Sequence *)handle->buf[!handle->play];
464
465 const int32_t play_capacity = BUF_PERCENT * play_seq->atom.size;
466 const int32_t rec_capacity = BUF_PERCENT * rec_seq->atom.size;
467 const int32_t position = handle->offset * handle->window;
468
469 if(handle->ref && (play_capacity != handle->state.play_capacity) )
470 {
471 handle->state.play_capacity = play_capacity;
472 props_set(&handle->props, &handle->forge, nsamples-1, handle->urid.play_capacity, &handle->ref);
_state_restore(LV2_Handle instance,LV2_State_Retrieve_Function retrieve,LV2_State_Handle state,uint32_t flags,const LV2_Feature * const * features)473 }
474 if(handle->ref && (rec_capacity != handle->state.rec_capacity) )
475 {
476 handle->state.rec_capacity = rec_capacity;
477 props_set(&handle->props, &handle->forge, nsamples-1, handle->urid.rec_capacity, &handle->ref);
478 }
479 if(handle->ref && (position != handle->state.position) )
480 {
481 handle->state.position = position;
482 props_set(&handle->props, &handle->forge, nsamples-1, handle->urid.position, &handle->ref);
483 }
484
485 if(handle->ref)
486 lv2_atom_forge_pop(&handle->forge, &frame);
487 else
488 lv2_atom_sequence_clear(handle->event_out);
489 }
490
491 static void
492 cleanup(LV2_Handle instance)
493 {
494 plughandle_t *handle = instance;
495
496 munlock(handle, sizeof(plughandle_t));
497 free(handle);
498 }
499
500 static LV2_State_Status
501 _state_save(LV2_Handle instance, LV2_State_Store_Function store,
502 LV2_State_Handle state, uint32_t flags,
503 const LV2_Feature *const *features)
504 {
505 plughandle_t *handle = instance;
506
507 return props_save(&handle->props, store, state, flags, features);
508 }
509
510 static LV2_State_Status
511 _state_restore(LV2_Handle instance, LV2_State_Retrieve_Function retrieve,
512 LV2_State_Handle state, uint32_t flags,
513 const LV2_Feature *const *features)
_work(LV2_Handle instance,LV2_Worker_Respond_Function respond,LV2_Worker_Respond_Handle worker,uint32_t size,const void * body)514 {
515 plughandle_t *handle = instance;
516
517 return props_restore(&handle->props, retrieve, state, flags, features);
518 }
519
520 static const LV2_State_Interface state_iface = {
521 .save = _state_save,
522 .restore = _state_restore
523 };
524
525 static const void*
526 extension_data(const char* uri)
527 {
528 if(!strcmp(uri, LV2_STATE__interface))
529 return &state_iface;
530
531 return NULL;
532 }
533
534 const LV2_Descriptor orbit_looper = {
535 .URI = ORBIT_LOOPER_URI,
536 .instantiate = instantiate,
537 .connect_port = connect_port,
538 .activate = activate,
539 .run = run,
540 .deactivate = NULL,
541 .cleanup = cleanup,
542 .extension_data = extension_data
543 };
544