1 /* B.Oops
2 * Glitch effect sequencer LV2 plugin
3 *
4 * Copyright (C) 2020 by Sven Jähnichen
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3, or (at your option)
9 * any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 */
20
21 #include <cstdio>
22 #include <string>
23 #include <stdexcept>
24 #include <algorithm>
25 #include "BOops.hpp"
26 #include "ControllerLimits.hpp"
27 #include "BUtilities/stof.hpp"
28 #include "BUtilities/Path.hpp"
29 #include "getURIs.hpp"
30 #include "to_shapes.hpp"
31 #include "bool2hstr.hpp"
32
33 #ifndef SF_FORMAT_MP3
34 #ifndef MINIMP3_IMPLEMENTATION
35 #define MINIMP3_IMPLEMENTATION
36 #endif
37 #endif
38 #include "Sample.hpp"
39
40 #ifndef LIMIT
41 #define LIMIT(g , min, max) ((g) > (max) ? (max) : ((g) < (min) ? (min) : (g)))
42 #endif /* LIMIT */
43
floorfrac(const double value)44 inline double floorfrac (const double value) {return value - floor (value);}
45
46
BOops(double samplerate,const char * bundle_path,const LV2_Feature * const * features)47 BOops::BOops (double samplerate, const char* bundle_path, const LV2_Feature* const* features) :
48 map(NULL), workerSchedule (NULL), pluginPath {0}, urids (),
49 host {samplerate, 120.0f, 1.0f, 0ul, 0.0f, 4.0f},
50 activated (false),
51 positions {{0.0, -1, 0.0, 0, {samplerate, 120.0f, 1.0f, 0ul, 0.0f, 4.0f}, 1.0, true}, {0.0, -1, 0.0, 0, {0.0, 120.0f, 1.0f, 0ul, 0.0f, 4.0f}, 0.0, true}},
52 transportGateKeys {false},
53 pages {},
54 pageNr (0),
55 pageMax (0),
56 midiLearn (false),
57 midiLearned {0},
58 editorPage (0),
59 editorSlot (0),
60 controlPort(NULL), notifyPort(NULL),
61 audioInput1(NULL), audioInput2(NULL), audioOutput1(NULL), audioOutput2(NULL),
62 new_controllers {NULL}, globalControllers {0},
63 forge (), notify_frame (),
64 sample (NULL), sampleAmp (1.0f),
65 waveform {0}, waveformCounter (0), lastWaveformCounter (0),
66 message (), ui_on(false), scheduleNotifyAllSlots (false),
67 scheduleNotifyPageControls {false},
68 scheduleNotifyStatus (false), scheduleResizeBuffers (false), scheduleSetFx {false},
69 scheduleNotifyWaveformToGui (false), scheduleNotifyTransportGateKeys (false),
70 scheduleNotifySamplePathToGui (false),
71 scheduleNotifyMidiLearnedToGui (false),
72 scheduleStateChanged (false),
73 scheduleInit (false)
74
75 {
76 if (bundle_path) strncpy (pluginPath, bundle_path, 1023);
77 if ((strlen (pluginPath) > 0) && (pluginPath[strlen (pluginPath) - 1] != BUTILITIES_PATH_SLASH[0])) strcat (pluginPath, BUTILITIES_PATH_SLASH);
78
79 //Scan host features for URID map
80 LV2_URID_Map* m = NULL;
81 for (int i = 0; features[i]; ++i)
82 {
83 if (strcmp(features[i]->URI, LV2_URID__map) == 0)
84 {
85 m = (LV2_URID_Map*) features[i]->data;
86 }
87
88 else if (!strcmp(features[i]->URI, LV2_WORKER__schedule))
89 {
90 workerSchedule = (LV2_Worker_Schedule*)features[i]->data;
91 }
92 }
93 if (!m) throw std::invalid_argument ("Host does not support urid:map");
94 if (!workerSchedule) throw std::invalid_argument ("BJumblr.lv2: Host does not support work:schedule.");
95
96 //Map URIS
97 map = m;
98 getURIs (m, &urids);
99
100 // Initialize forge
101 lv2_atom_forge_init (&forge, map);
102
103 // Initialize slots
104 slots.fill (Slot (this, FX_NONE, nullptr, nullptr, 16, 1.0f, 0.25 * samplerate));
105
106 // Init pages
107 for (Page& p : pages)
108 {
109 p.controls = {0, 0, 0, 0};
110 for (std::array<Pad, NR_STEPS>& pp : p.pads) pp.fill (Pad());
111 p.shapes.fill (Shape<SHAPE_MAXNODES>());
112 for (std::array<bool, NR_PIANO_KEYS + 1>& pk : p.keys) pk.fill (false);
113 }
114 }
115
~BOops()116 BOops::~BOops ()
117 {
118 if (sample) delete sample;
119 }
120
connect_port(uint32_t port,void * data)121 void BOops::connect_port(uint32_t port, void *data)
122 {
123 switch (port) {
124 case CONTROL:
125 controlPort = (LV2_Atom_Sequence*) data;
126 break;
127
128 case NOTIFY:
129 notifyPort = (LV2_Atom_Sequence*) data;
130 break;
131
132 case AUDIO_IN_1:
133 audioInput1 = (float*) data;
134 break;
135
136 case AUDIO_IN_2:
137 audioInput2 = (float*) data;
138 break;
139
140 case AUDIO_OUT_1:
141 audioOutput1 = (float*) data;
142 break;
143 case AUDIO_OUT_2:
144 audioOutput2 = (float*) data;
145 break;
146
147 default:
148 if ((port >= CONTROLLERS) && (port < CONTROLLERS + NR_CONTROLLERS)) new_controllers[port - CONTROLLERS] = (float*) data;
149 }
150 }
151
getPositionFromBeats(const Transport & transport,const double beats)152 double BOops::getPositionFromBeats (const Transport& transport, const double beats)
153 {
154 if (globalControllers[BASE_VALUE] == 0.0) return 0.0;
155
156 switch (int (globalControllers[BASE]))
157 {
158 case SECONDS: return (transport.bpm ? beats / (globalControllers[BASE_VALUE] * (transport.bpm / 60.0)) : 0.0);
159 case BEATS: return beats / globalControllers[BASE_VALUE];
160 case BARS: return (transport.beatsPerBar ? beats / (globalControllers[BASE_VALUE] * transport.beatsPerBar) : 0.0);
161 default: return 0.0;
162 }
163 }
164
getPositionFromFrames(const Transport & transport,const uint64_t frames)165 double BOops::getPositionFromFrames (const Transport& transport, const uint64_t frames)
166 {
167 if ((globalControllers[BASE_VALUE] == 0.0) || (transport.rate == 0)) return 0.0;
168
169 switch (int (globalControllers[BASE]))
170 {
171 case SECONDS: return frames * (1.0 / transport.rate) / globalControllers[BASE_VALUE] ;
172 case BEATS: return (transport.bpm ? frames * (transport.speed / (transport.rate / (transport.bpm / 60))) / globalControllers[BASE_VALUE] : 0.0);
173 case BARS: return
174 (
175 transport.bpm && transport.beatsPerBar ?
176 frames * (transport.speed / (transport.rate / (transport.bpm / 60))) / (globalControllers[BASE_VALUE] * transport.beatsPerBar) :
177 0.0
178 );
179 default: return 0.0;
180 }
181 }
182
getFramesFromPosition(const Transport & transport,const double position) const183 uint64_t BOops::getFramesFromPosition (const Transport& transport, const double position) const
184 {
185 if (transport.bpm < 1.0) return 0;
186
187 switch (int (globalControllers[BASE]))
188 {
189 case SECONDS : return position * transport.rate * globalControllers[BASE_VALUE];
190 case BEATS: return position * (60.0 / transport.bpm) * transport.rate * globalControllers[BASE_VALUE];
191 case BARS: return position * transport.beatsPerBar * (60.0 / transport.bpm) * transport.rate * globalControllers[BASE_VALUE];
192 default: return 0;
193 }
194 }
195
getPositionFromSeconds(const Transport & transport,const double seconds)196 double BOops::getPositionFromSeconds (const Transport& transport, const double seconds)
197 {
198 if (globalControllers[BASE_VALUE] == 0.0) return 0.0;
199
200 switch (int (globalControllers[BASE]))
201 {
202 case SECONDS : return seconds / globalControllers[BASE_VALUE];
203 case BEATS: return seconds * (transport.bpm / 60.0) / globalControllers[BASE_VALUE];
204 case BARS: return (transport.beatsPerBar ? seconds * (transport.bpm / 60.0 / transport.beatsPerBar) / globalControllers[BASE_VALUE] : 0.0);
205 default: return 0;
206 }
207 }
208
getFramesPerStep(const Transport & transport)209 double BOops::getFramesPerStep (const Transport& transport)
210 {
211 double bps = (globalControllers[PLAY_MODE] == 0 ? globalControllers[AUTOPLAY_BPM] : transport.bpm) / 60.0;
212 double bpB = (globalControllers[PLAY_MODE] == 0 ? globalControllers[AUTOPLAY_BPB] :transport. beatsPerBar);
213 double s =
214 (
215 globalControllers[BASE] == 0 ?
216 globalControllers[BASE_VALUE] :
217 (
218 globalControllers[BASE] == 1 ?
219 globalControllers[BASE_VALUE] / bps :
220 globalControllers[BASE_VALUE] * bpB / bps
221 )
222 );
223 return transport.rate * s / globalControllers[STEPS];
224 }
225
backPosition()226 Position& BOops::backPosition() {return (sizePosition() > 1 ? positions[1] : positions[0]);}
227
sizePosition()228 size_t BOops::sizePosition() {return 1 + (positions[1].transport.rate != 0.0);}
229
pushBackPosition(Position & p)230 void BOops::pushBackPosition (Position& p) {positions[1] = p;}
231
popBackPosition()232 void BOops::popBackPosition() {positions[1].transport.rate = 0.0;}
233
popFrontPosition()234 void BOops::popFrontPosition() {positions[0] = positions[1]; popBackPosition();}
235
activate()236 void BOops::activate() {activated = true;}
237
deactivate()238 void BOops::deactivate() {activated = false;}
239
run(uint32_t n_samples)240 void BOops::run (uint32_t n_samples)
241 {
242 // Check ports
243 if ((!controlPort) || (!notifyPort) || (!audioInput1) || (!audioInput2) || (!audioOutput1) || (!audioOutput2)) return;
244
245 for (int i = 0; i < NR_CONTROLLERS; ++i) if (!new_controllers[i]) return;
246
247 // Prepare forge buffer and initialize atom sequence
248 const uint32_t space = notifyPort->atom.size;
249 lv2_atom_forge_set_buffer(&forge, (uint8_t*) notifyPort, space);
250 lv2_atom_forge_sequence_head(&forge, ¬ify_frame, 0);
251
252 // Update controller values
253 for (int i = 0; i < SLOTS; ++i)
254 {
255 if (globalControllers[i] != *new_controllers[i])
256 {
257 float newValue = controllerLimits[i].validate (*new_controllers[i]);
258 float oldValue = globalControllers[i];
259 globalControllers[i] = newValue;
260
261 if (i == PLAY_MODE)
262 {
263 Position np = backPosition();
264
265 switch (int (newValue))
266 {
267 case AUTOPLAY:
268 np.playing = true;
269 np.transport.bpm = globalControllers[AUTOPLAY_BPM];
270 np.transport.speed = 1.0;
271 np.transport.beatsPerBar = globalControllers[AUTOPLAY_BPB];
272 break;
273
274 case HOST_CONTROLLED:
275 np.playing = true;
276 np.transport = host;
277 break;
278
279 default:
280 np.playing = false;
281 np.transport = host;
282 }
283
284 pushBackPosition (np);
285
286 resizeSteps ();
287 }
288
289 else if (i == STEPS)
290 {
291 for (Slot& s : slots) s.size = newValue;
292 resizeSteps ();
293 }
294
295 else if (i == AUTOPLAY_BPM)
296 {
297 if (globalControllers[PLAY_MODE] == AUTOPLAY) backPosition().transport.bpm = globalControllers[AUTOPLAY_BPM];
298 resizeSteps ();
299 }
300
301 else if (i == AUTOPLAY_BPB)
302 {
303 if (globalControllers[PLAY_MODE] == AUTOPLAY) backPosition().transport.beatsPerBar = globalControllers[AUTOPLAY_BPB];
304 resizeSteps ();
305 }
306
307 else if (i == AUTOPLAY_POSITION)
308 {
309 Position np = backPosition();
310 np.sequence = floorfrac (np.sequence + 1.0 + newValue - oldValue);
311 np.refFrame = 0;
312 pushBackPosition (np);
313 scheduleNotifyStatus = true;
314 }
315
316 else if
317 ((i == BASE) || (i == BASE_VALUE)) resizeSteps ();
318 }
319 }
320
321 for (int slotNr = 0; slotNr < NR_SLOTS; ++slotNr)
322 {
323 if (!scheduleSetFx[slotNr])
324 {
325 if (slots[slotNr].effect != *new_controllers[SLOTS + slotNr * (SLOTS_PARAMS + NR_PARAMS)])
326 {
327 LV2_Atom_Int msg = {{sizeof (int), urids.bOops_allocateFx}, slotNr};
328 scheduleSetFx[slotNr] = true;
329 workerSchedule->schedule_work (workerSchedule->handle, sizeof (msg), &msg);
330 continue;
331 }
332
333 for (int params = 0; params < NR_PARAMS; ++params)
334 {
335 int controllerNr = SLOTS + slotNr * (SLOTS_PARAMS + NR_PARAMS) + SLOTS_PARAMS + params;
336 if (slots[slotNr].params[params] != *new_controllers[controllerNr])
337 {
338 float newValue = controllerLimits[controllerNr].validate (*new_controllers[controllerNr]);
339 slots[slotNr].params[params] = newValue;
340 }
341 }
342 }
343 }
344
345
346
347 // Control and MIDI messages
348 uint32_t last_t = 0;
349 LV2_ATOM_SEQUENCE_FOREACH(controlPort, ev)
350 {
351 // Read host & GUI events
352 if ((ev->body.type == urids.atom_Object) || (ev->body.type == urids.atom_Blank))
353 {
354 const LV2_Atom_Object* obj = (const LV2_Atom_Object*)&ev->body;
355
356 // Process GUI on status data
357 if (obj->body.otype == urids.bOops_uiOn)
358 {
359 ui_on = true;
360 scheduleNotifyAllSlots = true;
361 std::fill (scheduleNotifyPageControls, scheduleNotifyPageControls + NR_PAGES, true);
362 std::fill (scheduleNotifyShape, scheduleNotifyShape + NR_SLOTS, true);
363 scheduleNotifyTransportGateKeys = true;
364 scheduleNotifySamplePathToGui = true;
365 scheduleNotifyStatus = true;
366 }
367
368 // Process GUI off status data
369 else if (obj->body.otype == urids.bOops_uiOff) ui_on = false;
370
371
372 // Process page properties
373 else if (obj->body.otype == urids.bOops_pagePropertiesEvent)
374 {
375 LV2_Atom *oId = NULL, *oStatus = NULL, *oChannel = NULL, *oMsg = NULL, *oValue = NULL;
376 int id = -1;
377 lv2_atom_object_get (obj,
378 urids.bOops_pageID, &oId,
379 urids.bOops_pageStatus, &oStatus,
380 urids.bOops_pageChannel, &oChannel,
381 urids.bOops_pageMessage, &oMsg,
382 urids.bOops_pageValue, &oValue,
383 NULL);
384
385 if (oId && (oId->type == urids.atom_Int) && (((LV2_Atom_Int*)oId)->body >= 0) && (((LV2_Atom_Int*)oId)->body < NR_PAGES))
386 {
387 id = ((LV2_Atom_Int*)oId)->body;
388
389 if (oStatus && (oStatus->type == urids.atom_Int)) pages[id].controls.status = LIMIT (((LV2_Atom_Int*)oStatus)->body, 0, 15);
390 if (oChannel && (oChannel->type == urids.atom_Int)) pages[id].controls.channel = LIMIT (((LV2_Atom_Int*)oChannel)->body, 0, 16);
391 if (oMsg && (oMsg->type == urids.atom_Int)) pages[id].controls.message = LIMIT (((LV2_Atom_Int*)oMsg)->body, 0, 128);
392 if (oValue && (oValue->type == urids.atom_Int)) pages[id].controls.value = LIMIT (((LV2_Atom_Int*)oValue)->body, 0, 128);
393 }
394
395 scheduleStateChanged = true;
396 }
397
398
399 // Process page selection
400 else if (obj->body.otype == urids.bOops_statusEvent)
401 {
402 LV2_Atom * oMl = NULL, *oId = NULL, *oMax = NULL, *oEdPg = NULL, *oEdSl = NULL;
403 lv2_atom_object_get (obj,
404 urids.bOops_requestMidiLearn, &oMl,
405 urids.bOops_pageID, &oId,
406 urids.bOops_pageMax, &oMax,
407 urids.bOops_editorPage, &oEdPg,
408 urids.bOops_editorSlot, &oEdSl,
409 NULL);
410
411 // Midi learn request notification
412 if (oMl && (oMl->type == urids.atom_Bool)) midiLearn = ((LV2_Atom_Bool*)oMl)->body;
413
414 if (oId && (oId->type == urids.atom_Int) && (((LV2_Atom_Int*)oId)->body >= 0) && (((LV2_Atom_Int*)oId)->body < NR_PAGES))
415 {
416 const int id = ((LV2_Atom_Int*)oId)->body;
417
418 // Schedule fader for page change
419 if (id != pageNr)
420 {
421 Position np = backPosition();
422 pushBackPosition (np);
423 pageNr = id;
424 }
425
426 if (pageMax < pageNr) pageMax = pageNr;
427 }
428
429 if (oMax && (oMax->type == urids.atom_Int) && (((LV2_Atom_Int*)oMax)->body >= 0) && (((LV2_Atom_Int*)oMax)->body < NR_PAGES))
430 {
431 pageMax = ((LV2_Atom_Int*)oMax)->body;
432
433 if (pageNr > pageMax)
434 {
435 // Limit pageNr to pageMax
436 pageNr = pageMax;
437
438 // Schedule fader for page change
439 Position np = backPosition();
440 pushBackPosition (np);
441 }
442 }
443
444 if (oEdPg && (oEdPg->type == urids.atom_Int)) editorPage = LIMIT (((LV2_Atom_Int*)oEdPg)->body, 0, NR_PAGES - 1);
445
446 if (oEdSl && (oEdSl->type == urids.atom_Int)) editorSlot = LIMIT (((LV2_Atom_Int*)oEdSl)->body, 0, NR_SLOTS - 1);
447
448 scheduleStateChanged = true;
449 }
450
451
452 // Process transportGateKey data
453 else if (obj->body.otype == urids.bOops_transportGateKeyEvent)
454 {
455 LV2_Atom *oKeys = NULL;
456 lv2_atom_object_get (obj,
457 urids.bOops_transportGateKeys, &oKeys,
458 NULL);
459
460 if (oKeys && (oKeys->type == urids.atom_Vector))
461 {
462 const LV2_Atom_Vector* vec = (const LV2_Atom_Vector*) oKeys;
463 if (vec->body.child_type == urids.atom_Int)
464 {
465 const int keysize = LIMIT ((int) ((oKeys->size - sizeof(LV2_Atom_Vector_Body)) / sizeof (int)), 0, NR_PIANO_KEYS);
466 const int* keys = (int*) (&vec->body + 1);
467 std::fill (transportGateKeys, transportGateKeys + NR_PIANO_KEYS, false);
468 for (int i = 0; i < keysize; ++i)
469 {
470 int keyNr = keys[i];
471 if ((keyNr >=0) && (keyNr < NR_PIANO_KEYS)) transportGateKeys[keyNr] = true;
472 }
473 scheduleNotifyTransportGateKeys = true;
474 scheduleStateChanged = true;
475 }
476 }
477 }
478
479 // Process slot pads or shape or keys data
480 else if (obj->body.otype == urids.bOops_slotEvent)
481 {
482 LV2_Atom *oPg = NULL, *oSl = NULL, *oPd = NULL, *oSh = NULL, *oKy = NULL;
483 int pg = 0;
484 int slot = -1;
485 lv2_atom_object_get (obj,
486 urids.bOops_pageID, &oPg,
487 urids.bOops_slot, &oSl,
488 urids.bOops_pads, &oPd,
489 urids.bOops_shapeData, &oSh,
490 urids.bOops_keysData, &oKy,
491 NULL);
492
493 // Page nr notification
494 if (oPg && (oPg->type == urids.atom_Int) && (((LV2_Atom_Int*)oPg)->body >= 0) && (((LV2_Atom_Int*)oPg)->body < NR_PAGES))
495 {
496 pg = ((LV2_Atom_Int*)oPg)->body;
497 }
498
499 // Slot nr notification
500 if (oSl && (oSl->type == urids.atom_Int) && (((LV2_Atom_Int*)oSl)->body >= 0) && (((LV2_Atom_Int*)oSl)->body < NR_SLOTS))
501 {
502 slot = ((LV2_Atom_Int*)oSl)->body;
503 }
504
505 // Pad notification
506 if (oPd && (oPd->type == urids.atom_Vector) && (slot >= 0))
507 {
508 const LV2_Atom_Vector* vec = (const LV2_Atom_Vector*) oPd;
509 if (vec->body.child_type == urids.atom_Float)
510 {
511 const uint32_t size = (uint32_t) ((oPd->size - sizeof(LV2_Atom_Vector_Body)) / sizeof (Pad));
512 Pad* pad = (Pad*) (&vec->body + 1);
513 for (unsigned int i = 0; (i < size) && (i < NR_STEPS); ++i) pages[pg].pads[slot][i] = pad[i];
514 if (pg == pageNr)
515 {
516 for (unsigned int i = 0; (i < size) && (i < NR_STEPS); ++i) slots[slot].setPad (i, pad[i]);
517 }
518
519 scheduleStateChanged = true;
520 }
521 }
522
523 // Shape notification
524 if (oSh && (oSh->type == urids.atom_Vector) && (slot >= 0))
525 {
526 const LV2_Atom_Vector* vec = (const LV2_Atom_Vector*) oSh;
527 if (vec->body.child_type == urids.atom_Float)
528 {
529 Shape<SHAPE_MAXNODES> shape = Shape<SHAPE_MAXNODES>();
530 const uint32_t vecSize = (uint32_t) ((oSh->size - sizeof(LV2_Atom_Vector_Body)) / (7 * sizeof (float)));
531 float* data = (float*) (&vec->body + 1);
532 for (unsigned int i = 0; (i < vecSize) && (i < SHAPE_MAXNODES); ++i)
533 {
534 Node node;
535 node.nodeType = NodeType (int (data[i * 7]));
536 node.point.x = data[i * 7 + 1];
537 node.point.y = data[i * 7 + 2];
538 node.handle1.x = data[i * 7 + 3];
539 node.handle1.y = data[i * 7 + 4];
540 node.handle2.x = data[i * 7 + 5];
541 node.handle2.y = data[i * 7 + 6];
542 shape.appendNode (node);
543 }
544 if (shape != Shape<SHAPE_MAXNODES>()) shape.validateShape();
545 pages[pg].shapes[slot] = shape;
546 if (pg == pageNr) slots[slot].setSlotShape (shape);
547
548 scheduleStateChanged = true;
549 }
550 }
551
552 // Keys notification
553 if (oKy && (oKy->type == urids.atom_String) && (slot >= 0))
554 {
555 const char* kstr = (const char*) (oKy + 1);
556 hstr2bool<std::array<bool, NR_PIANO_KEYS + 1>> (kstr, pages[pg].keys[slot]);
557 if (pg == pageNr) slots[slot].setSlotKeys (pages[pg].keys[slot]);
558 scheduleStateChanged = true;
559 }
560 }
561
562 // Process single pad data
563 else if (obj->body.otype == urids.bOops_padEvent)
564 {
565 LV2_Atom *oPg = NULL, *oSl = NULL, *oSt = NULL, *oPd = NULL;
566 int pg = 0;
567 int slot = -1;
568 int step = -1;
569 lv2_atom_object_get (obj,
570 urids.bOops_pageID, &oPg,
571 urids.bOops_slot, &oSl,
572 urids.bOops_step, &oSt,
573 urids.bOops_pads, &oPd,
574 NULL);
575
576 // Page nr notification
577 if (oPg && (oPg->type == urids.atom_Int) && (((LV2_Atom_Int*)oPg)->body >= 0) && (((LV2_Atom_Int*)oPg)->body < NR_PAGES))
578 {
579 pg = ((LV2_Atom_Int*)oPg)->body;
580 }
581
582 // Slot nr notification
583 if (oSl && (oSl->type == urids.atom_Int) && (((LV2_Atom_Int*)oSl)->body >= 0) && (((LV2_Atom_Int*)oSl)->body < NR_SLOTS))
584 {
585 slot = ((LV2_Atom_Int*)oSl)->body;
586 }
587
588 // Step nr notification
589 if (oSt && (oSt->type == urids.atom_Int) && (((LV2_Atom_Int*)oSt)->body >= 0) && (((LV2_Atom_Int*)oSt)->body < NR_STEPS))
590 {
591 step = ((LV2_Atom_Int*)oSt)->body;
592 }
593
594 // Pad notification
595 if (oPd && (oPd->type == urids.atom_Vector) && (slot >= 0) && (step >= 0))
596 {
597 const LV2_Atom_Vector* vec = (const LV2_Atom_Vector*) oPd;
598 if (vec->body.child_type == urids.atom_Float)
599 {
600 const uint32_t size = (uint32_t) ((oPd->size - sizeof(LV2_Atom_Vector_Body)) / sizeof (Pad));
601 if (size == 1)
602 {
603 Pad* pad = (Pad*) (&vec->body + 1);
604 pages[pg].pads[slot][step] = *pad;
605 if (pg == pageNr) slots[slot].setPad (step, *pad);
606 scheduleStateChanged = true;
607 }
608 }
609 }
610 }
611
612 // Process param shape data
613 else if (obj->body.otype == urids.bOops_shapeEvent)
614 {
615 LV2_Atom *oSl = NULL, *oSh = NULL;
616 int slot = -1;
617 lv2_atom_object_get (obj,
618 urids.bOops_slot, &oSl,
619 urids.bOops_shapeData, &oSh,
620 NULL);
621
622 // Slot nr notification
623 if (oSl && (oSl->type == urids.atom_Int) && (((LV2_Atom_Int*)oSl)->body >= 0) && (((LV2_Atom_Int*)oSl)->body < NR_SLOTS))
624 {
625 slot = ((LV2_Atom_Int*)oSl)->body;
626 }
627
628 // Shape notification
629 if (oSh && (oSh->type == urids.atom_Vector) && (slot >= 0))
630 {
631 const LV2_Atom_Vector* vec = (const LV2_Atom_Vector*) oSh;
632 if (vec->body.child_type == urids.atom_Float)
633 {
634 slots[slot].shape.clearShape ();
635 const uint32_t vecSize = (uint32_t) ((oSh->size - sizeof(LV2_Atom_Vector_Body)) / (7 * sizeof (float)));
636 float* data = (float*) (&vec->body + 1);
637 for (unsigned int i = 0; (i < vecSize) && (i < SHAPE_MAXNODES); ++i)
638 {
639 Node node;
640 node.nodeType = NodeType (int (data[i * 7]));
641 node.point.x = data[i * 7 + 1];
642 node.point.y = data[i * 7 + 2];
643 node.handle1.x = data[i * 7 + 3];
644 node.handle1.y = data[i * 7 + 4];
645 node.handle2.x = data[i * 7 + 5];
646 node.handle2.y = data[i * 7 + 6];
647 slots[slot].shape.appendNode (node);
648 }
649 slots[slot].shape.validateShape();
650 scheduleStateChanged = true;
651 }
652 }
653 }
654
655 // Sample path notification -> forward to worker
656 else if (obj->body.otype ==urids.bOops_samplePathEvent)
657 {
658 const LV2_Atom* oPath = NULL, *oStart = NULL, *oEnd = NULL, *oAmp = NULL, *oLoop = NULL;
659 lv2_atom_object_get
660 (
661 obj,
662 urids.bOops_samplePath, &oPath,
663 urids.bOops_sampleStart, &oStart,
664 urids.bOops_sampleEnd, &oEnd,
665 urids.bOops_sampleAmp, &oAmp,
666 urids.bOops_sampleLoop, &oLoop,
667 0
668 );
669
670 // New sample
671 if (oPath && (oPath->type == urids.atom_Path))
672 {
673 workerSchedule->schedule_work (workerSchedule->handle, lv2_atom_total_size((LV2_Atom*)obj), obj);
674 }
675
676 // Only start / end /amp / loop changed
677 else if (sample)
678 {
679 if (oStart && (oStart->type == urids.atom_Long)) sample->start = LIMIT (((LV2_Atom_Long*)oStart)->body, 0, sample->info.frames - 1);
680 if (oEnd && (oEnd->type == urids.atom_Long)) sample->end = LIMIT (((LV2_Atom_Long*)oEnd)->body, 0, sample->info.frames);
681 if (oAmp && (oAmp->type == urids.atom_Float)) sampleAmp = LIMIT (((LV2_Atom_Float*)oAmp)->body, 0.0f, 1.0f);
682 if (oLoop && (oLoop->type == urids.atom_Bool)) sample->loop = bool (((LV2_Atom_Bool*)oLoop)->body);
683 }
684 }
685
686 // Process time / position data
687 else if (obj->body.otype == urids.time_Position)
688 {
689 bool scheduleUpdatePosition = false;
690 bool scheduleResizeSteps = false;
691
692 // Update bpm, speed, position
693 LV2_Atom *oBbeat = NULL, *oBpm = NULL, *oSpeed = NULL, *oBpb = NULL, *oBu = NULL, *oBar = NULL;
694 const LV2_Atom_Object* obj = (const LV2_Atom_Object*)&ev->body;
695 lv2_atom_object_get
696 (
697 obj,
698 urids.time_bar, &oBar,
699 urids.time_barBeat, &oBbeat,
700 urids.time_beatsPerMinute, &oBpm,
701 urids.time_beatsPerBar, &oBpb,
702 urids.time_beatUnit, &oBu,
703 urids.time_speed, &oSpeed,
704 NULL
705 );
706
707 // BPM changed?
708 if (oBpm && (oBpm->type == urids.atom_Float))
709 {
710 float nbpm = ((LV2_Atom_Float*)oBpm)->body;
711
712 if (nbpm != host.bpm)
713 {
714 host.bpm = nbpm;
715 if (nbpm < 1.0) message.setMessage (JACK_STOP_MSG);
716 else message.deleteMessage (JACK_STOP_MSG);
717
718 scheduleResizeSteps = true;
719 }
720 }
721
722 // Beats per bar changed?
723 if (oBpb && (oBpb->type == urids.atom_Float) && (((LV2_Atom_Float*)oBpb)->body > 0) && (((LV2_Atom_Float*)oBpb)->body != host.beatsPerBar))
724 {
725 host.beatsPerBar = ((LV2_Atom_Float*)oBpb)->body;
726 scheduleNotifyStatus = true;
727 scheduleResizeSteps = true;
728 }
729
730 // Speed changed?
731 if (oSpeed && (oSpeed->type == urids.atom_Float))
732 {
733 float nspeed = ((LV2_Atom_Float*)oSpeed)->body;
734
735 if (nspeed != host.speed)
736 {
737
738 if (globalControllers[BASE] != SECONDS)
739 {
740
741 // Started ?
742 // TODO Clear all audio buffers
743 // if (speed == 0)
744 // {
745 //
746 // }
747
748 // Stopped ?
749 if (nspeed == 0)
750 {
751 message.setMessage (JACK_STOP_MSG);
752 }
753
754 // Not stopped ?
755 else message.deleteMessage (JACK_STOP_MSG);
756 }
757
758 host.speed = nspeed;
759 }
760 }
761
762 // Bar position changed
763 if (oBar && (oBar->type == urids.atom_Long) && (host.bar != ((uint64_t)((LV2_Atom_Long*)oBar)->body)))
764 {
765 host.bar = ((LV2_Atom_Long*)oBar)->body;
766 scheduleUpdatePosition = true;
767 }
768
769 // Beat position changed (during playing) ?
770 if (oBbeat && (oBbeat->type == urids.atom_Float) && (((LV2_Atom_Float*)oBbeat)->body != host.barBeat))
771 {
772 host.barBeat = ((LV2_Atom_Float*)oBbeat)->body;
773 scheduleUpdatePosition = true;
774 }
775
776 // Bar or beat position data changed in host or midi controlled mode: fade
777 if (scheduleUpdatePosition)
778 {
779 if (globalControllers[PLAY_MODE] != AUTOPLAY)
780 {
781 Position np = backPosition();
782 double pos = getPositionFromBeats (host, host.barBeat + host.beatsPerBar * host.bar);
783 double npos = floorfrac (pos - np.offset);
784
785 // Fade only if jump > 1 ms
786 if (fabs (npos - np.sequence) > getPositionFromSeconds (host, 0.001))
787 {
788 np.fader = 0.0;
789 np.transport = host;
790 np.sequence = npos;
791 // keep np.step
792 np.refFrame = ev->time.frames;
793 scheduleNotifyStatus = true;
794
795 pushBackPosition (np);
796 }
797
798 // Otherwise update at least the transport data
799 else backPosition().transport = host;
800 }
801 }
802
803 // Other data changed in host or midi controlled mode: copy
804 else if (globalControllers[PLAY_MODE] != AUTOPLAY) backPosition().transport = host;
805
806 if (scheduleResizeSteps && (globalControllers[PLAY_MODE] != AUTOPLAY)) resizeSteps();
807 }
808 }
809
810 // Read incoming MIDI events
811 if (ev->body.type == urids.midi_Event)
812 {
813 const uint8_t* const msg = (const uint8_t*)(ev + 1);
814 const uint8_t status = (msg[0] >> 4);
815 const uint8_t channel = msg[0] & 0x0F;
816 const uint8_t note = ((status == 8) || (status == 9) || (status == 11) ? msg[1] : 0);
817 const uint8_t value = ((status == 8) || (status == 9) || (status == 11) ? msg[2] : 0);
818
819 // MidiLearn
820 if (midiLearn)
821 {
822 midiLearned[0] = status;
823 midiLearned[1] = channel;
824 midiLearned[2] = note;
825 midiLearned[3] = value;
826 midiLearn = false;
827 scheduleNotifyMidiLearnedToGui = true;
828 }
829
830 // Analyze MIDI event
831 else
832 {
833 // Store into keys
834 // NOTE_ON and NOTE_OFF
835 if (((status == 8) || (status == 9)) && (note < NR_PIANO_KEYS))
836 {
837 // Note on
838 if (status == 9)
839 {
840 for (Slot& s : slots)
841 {
842 MidiKey old = s.findMidiKey (note);
843 const double v0 = old.value;
844 s.addMidiKey ({9, channel, note, value, 0.0, v0});
845 }
846 }
847
848 // Note off
849 else if (status == 8)
850 {
851 for (Slot& s : slots)
852 {
853 MidiKey old = s.findMidiKey (note);
854 if (old.status != 0) s.addMidiKey ({8, channel, note, value, 0.0, old.value});
855 }
856 }
857 }
858
859 // LV2_MIDI_CTL_ALL_NOTES_OFF, LV2_MIDI_CTL_ALL_SOUNDS_OFF
860 {
861 if ((status == 11) && ((note == LV2_MIDI_CTL_ALL_NOTES_OFF) || (note ==LV2_MIDI_CTL_ALL_SOUNDS_OFF)))
862 {
863 for (Slot& s : slots) s.allKeysOff();
864 }
865 }
866
867 // MIDI-controlled pages
868 for (int p = 0; p <= pageMax; ++p)
869 {
870 if
871 (
872 pages[p].controls.status &&
873 (pages[p].controls.status == status) &&
874 (
875 (pages[p].controls.channel == 0) ||
876 (pages[p].controls.channel - 1 == channel)
877 ) &&
878 (
879 (pages[p].controls.message == 128) ||
880 (pages[p].controls.message == note)
881 ) &&
882 (
883 (pages[p].controls.value == 128) ||
884 (pages[p].controls.value == value)
885 )
886 )
887 {
888 if (p != pageNr)
889 {
890 Position np = backPosition();
891 pushBackPosition (np);
892 pageNr = p;
893 scheduleNotifyStatus = true;
894 }
895
896 break;
897 }
898 }
899
900 // MIDI-controlled playback
901 if (globalControllers[PLAY_MODE] == MIDI_CONTROLLED)
902 {
903 const uint8_t typ = lv2_midi_message_type(msg);
904 // uint8_t chn = msg[0] & 0x0F;
905 const uint8_t note = msg[1];
906 const bool isTransportGateKey = ((note < NR_PIANO_KEYS) && transportGateKeys[note]);
907
908
909 switch (typ)
910 {
911 case LV2_MIDI_MSG_NOTE_ON:
912 {
913 if (isTransportGateKey)
914 {
915 Position p = backPosition();
916
917 switch (int (globalControllers[ON_MIDI]))
918 {
919 case 0: // Restart
920 p.offset = floorfrac (p.sequence + p.offset);
921 p.sequence = 0;
922 p.refFrame = ev->time.frames;
923 break;
924
925 case 2: // Restart & sync
926 {
927 double steppos = fmod (p.sequence, 1.0 / double (globalControllers[STEPS]));
928 p.offset = floorfrac (1.0 + p.sequence + p.offset - steppos);
929 p.sequence = steppos;
930 p.refFrame = ev->time.frames;
931 }
932 break;
933
934 default:// Continue
935 break;
936 }
937
938 p.playing = true;
939 pushBackPosition (p);
940 scheduleNotifyStatus = true;
941 }
942 }
943 break;
944
945 case LV2_MIDI_MSG_NOTE_OFF:
946 {
947 if (isTransportGateKey)
948 {
949 Position p = backPosition();
950 p.playing = false;
951 pushBackPosition (p);
952 scheduleNotifyStatus = true;
953 }
954 }
955 break;
956
957 case LV2_MIDI_MSG_CONTROLLER:
958 {
959 if ((note == LV2_MIDI_CTL_ALL_NOTES_OFF) ||
960 (note == LV2_MIDI_CTL_ALL_SOUNDS_OFF))
961 {
962 Position p = backPosition();
963 p.playing = false;
964 pushBackPosition (p);
965 scheduleNotifyStatus = true;
966 }
967 }
968 break;
969
970 default: break;
971 }
972 }
973 }
974 }
975
976 uint32_t next_t = (ev->time.frames < n_samples ? ev->time.frames : n_samples);
977 play (last_t, next_t);
978 last_t = next_t;
979 }
980
981 // Play remaining samples
982 if (last_t < n_samples) play (last_t, n_samples);
983
984 // Update position in case of no new barBeat submitted on next call
985 for (unsigned int i = 0; i < sizePosition(); ++i)
986 {
987 const uint64_t diff = n_samples - positions[i].refFrame;
988 const double relpos = getPositionFromFrames (positions[i].transport, diff); // Position relative to reference frame
989 const double npos = floorfrac (positions[i].sequence + relpos);
990 const int nstep = LIMIT (npos * globalControllers[STEPS], 0, globalControllers[STEPS] - 1);
991 if (nstep != int (positions[i].sequence * globalControllers[STEPS])) scheduleNotifyStatus = true;
992 positions[i].sequence = npos;
993 positions[i].refFrame = 0;
994 }
995
996 // Send collected data to GUI
997 if (waveformCounter != lastWaveformCounter) scheduleNotifyWaveformToGui = true;
998 if (ui_on)
999 {
1000 if (message.isScheduled ()) notifyMessageToGui ();
1001 if (scheduleNotifyStatus) notifyStatusToGui ();
1002 if (scheduleNotifyAllSlots) notifyAllSlotsToGui();
1003 for (int i = 0; i < NR_PAGES; ++i) {if (scheduleNotifyPageControls[i]) notifyPageControls (i);}
1004 for (int i = 0; i < NR_SLOTS; ++i) {if (scheduleNotifyShape[i]) notifyShapeToGui (i);}
1005 if (scheduleNotifyTransportGateKeys) notifyTransportGateKeysToGui();
1006 if (scheduleNotifyWaveformToGui) notifyWaveformToGui (lastWaveformCounter, waveformCounter);
1007 if (scheduleNotifySamplePathToGui) notifySamplePathToGui();
1008 if (scheduleNotifyMidiLearnedToGui) notifyMidiLearnedToGui ();
1009 }
1010 if (scheduleStateChanged) notifyStateChanged();
1011 lv2_atom_forge_pop (&forge, ¬ify_frame);
1012 }
1013
resizeSteps()1014 void BOops::resizeSteps ()
1015 {
1016 double fpst = getFramesPerStep (backPosition().transport);
1017 for (Slot& s : slots) s.framesPerStep = fpst;
1018
1019 if (!scheduleResizeBuffers)
1020 {
1021 scheduleResizeBuffers = true;
1022 LV2_Atom msg = {0, urids.bOops_allocateBuffers};
1023 workerSchedule->schedule_work (workerSchedule->handle, sizeof (msg), &msg);
1024 }
1025 }
1026
notifyAllSlotsToGui()1027 void BOops::notifyAllSlotsToGui ()
1028 {
1029 for (int page = 0; page <= pageMax; ++page)
1030 {
1031 for (int slot = 0; slot < NR_SLOTS; ++slot)
1032 {
1033 LV2_Atom_Forge_Frame frame;
1034 lv2_atom_forge_frame_time(&forge, 0);
1035 forgePads (&forge, &frame, page, slot, NR_STEPS);
1036 forgeShapeData (&forge, &frame, &pages[page].shapes[slot]);
1037 char hstr[40];
1038 bool2hstr<std::array<bool, NR_PIANO_KEYS + 1>> (pages[page].keys[slot], hstr);
1039 lv2_atom_forge_key(&forge, urids.bOops_keysData);
1040 lv2_atom_forge_string (&forge, hstr, strlen (hstr) + 1);
1041 lv2_atom_forge_pop(&forge, &frame);
1042 }
1043 }
1044
1045 scheduleNotifyAllSlots = false;
1046 }
1047
notifyShapeToGui(const int slot)1048 void BOops::notifyShapeToGui (const int slot)
1049 {
1050 LV2_Atom_Forge_Frame frame;
1051 lv2_atom_forge_frame_time(&forge, 0);
1052 forgeShape (&forge, &frame, slot, &slots[slot].shape);
1053 lv2_atom_forge_pop(&forge, &frame);
1054
1055 scheduleNotifyShape[slot] = false;
1056 }
1057
notifyMessageToGui()1058 void BOops::notifyMessageToGui()
1059 {
1060 uint32_t messageNr = message.loadMessage ();
1061
1062 // Send notifications
1063 LV2_Atom_Forge_Frame frame;
1064 lv2_atom_forge_frame_time(&forge, 0);
1065 lv2_atom_forge_object(&forge, &frame, 0, urids.bOops_messageEvent);
1066 lv2_atom_forge_key(&forge, urids.bOops_message);
1067 lv2_atom_forge_int(&forge, messageNr);
1068 lv2_atom_forge_pop(&forge, &frame);
1069 }
1070
notifyStatusToGui()1071 void BOops::notifyStatusToGui()
1072 {
1073 Position& p = backPosition();
1074 double pos =
1075 (
1076 (globalControllers[PLAY] != PLAY_OFF) && p.playing && ((p.transport.speed != 0.0f) || (globalControllers[BASE] == SECONDS)) && (p.transport.bpm >= 1.0f)?
1077 floorfrac (p.sequence) * globalControllers[STEPS] :
1078 -1
1079 );
1080 // Send notifications
1081 LV2_Atom_Forge_Frame frame;
1082 lv2_atom_forge_frame_time(&forge, 0);
1083 lv2_atom_forge_object(&forge, &frame, 0, urids.bOops_statusEvent);
1084 lv2_atom_forge_key(&forge, urids.bOops_position);
1085 lv2_atom_forge_double(&forge, pos);
1086 lv2_atom_forge_key(&forge, urids.bOops_pageID);
1087 lv2_atom_forge_int(&forge, pageNr);
1088 lv2_atom_forge_key(&forge, urids.bOops_pageMax);
1089 lv2_atom_forge_int(&forge, pageMax);
1090 lv2_atom_forge_key(&forge, urids.bOops_editorPage);
1091 lv2_atom_forge_int(&forge, editorPage);
1092 lv2_atom_forge_key(&forge, urids.bOops_editorSlot);
1093 lv2_atom_forge_int(&forge, editorSlot);
1094 lv2_atom_forge_pop(&forge, &frame);
1095
1096 scheduleNotifyStatus = false;
1097 }
1098
notifyWaveformToGui(const int start,const int end)1099 void BOops::notifyWaveformToGui (const int start, const int end)
1100 {
1101 int p1 = (start <= end ? end : WAVEFORMSIZE - 1);
1102
1103 // Notify shapeBuffer (position to end)
1104 LV2_Atom_Forge_Frame frame;
1105 lv2_atom_forge_frame_time(&forge, 0);
1106 lv2_atom_forge_object(&forge, &frame, 0, urids.bOops_waveformEvent);
1107 lv2_atom_forge_key(&forge, urids.bOops_waveformStart);
1108 lv2_atom_forge_int(&forge, start);
1109 lv2_atom_forge_key(&forge, urids.bOops_waveformData);
1110 lv2_atom_forge_vector(&forge, sizeof(float), urids.atom_Float, (uint32_t) (p1 + 1 - start), &waveform[start]);
1111 lv2_atom_forge_pop(&forge, &frame);
1112
1113 // Additional notification if position exceeds end
1114 if (start > waveformCounter)
1115 {
1116 LV2_Atom_Forge_Frame frame;
1117 lv2_atom_forge_frame_time(&forge, 0);
1118 lv2_atom_forge_object(&forge, &frame, 0, urids.bOops_waveformEvent);
1119 lv2_atom_forge_key(&forge, urids.bOops_waveformStart);
1120 lv2_atom_forge_int(&forge, 0);
1121 lv2_atom_forge_key(&forge, urids.bOops_waveformData);
1122 lv2_atom_forge_vector(&forge, sizeof(float), urids.atom_Float, (uint32_t) (end), &waveform[0]);
1123 lv2_atom_forge_pop(&forge, &frame);
1124 }
1125
1126 scheduleNotifyWaveformToGui = false;
1127 lastWaveformCounter = end;
1128 }
1129
notifyTransportGateKeysToGui()1130 void BOops::notifyTransportGateKeysToGui()
1131 {
1132 // Create buffer
1133 int keys[NR_PIANO_KEYS];
1134 int keysize = 0;
1135 std::fill (keys, keys + NR_PIANO_KEYS, 0);
1136 for (int i = 0; i < NR_PIANO_KEYS; ++i)
1137 {
1138 if (transportGateKeys[i])
1139 {
1140 keys[keysize] = i;
1141 ++keysize;
1142 }
1143 }
1144
1145 // Send notifications
1146 LV2_Atom_Forge_Frame frame;
1147 lv2_atom_forge_frame_time (&forge, 0);
1148 forgeTransportGateKeys (&forge, &frame, keys, keysize);
1149 lv2_atom_forge_pop(&forge, &frame);
1150
1151 scheduleNotifyTransportGateKeys = false;
1152 }
1153
notifySamplePathToGui()1154 void BOops::notifySamplePathToGui ()
1155 {
1156 LV2_Atom_Forge_Frame frame;
1157 lv2_atom_forge_frame_time(&forge, 0);
1158
1159 if (sample && sample->path && (sample->path[0] != 0) && (strlen (sample->path) < PATH_MAX))
1160 {
1161 forgeSamplePath (&forge, &frame, sample->path, sample->start, sample->end, sampleAmp, int32_t (sample->loop));
1162 }
1163
1164 else
1165 {
1166 const char* path = ".";
1167 forgeSamplePath (&forge, &frame, path, 0, 0, sampleAmp, false);
1168 }
1169
1170 lv2_atom_forge_pop(&forge, &frame);
1171 scheduleNotifySamplePathToGui = false;
1172 }
1173
notifyStateChanged()1174 void BOops::notifyStateChanged()
1175 {
1176 LV2_Atom_Forge_Frame frame;
1177 lv2_atom_forge_frame_time(&forge, 0);
1178 lv2_atom_forge_object(&forge, &frame, 0, urids.state_StateChanged);
1179 lv2_atom_forge_pop(&forge, &frame);
1180 scheduleStateChanged = false;
1181 }
1182
notifyPageControls(const int pageId)1183 void BOops::notifyPageControls (const int pageId)
1184 {
1185 LV2_Atom_Forge_Frame frame;
1186 lv2_atom_forge_frame_time (&forge, 0);
1187 forgePageControls (&forge, &frame, pageId);
1188 lv2_atom_forge_pop(&forge, &frame);
1189 scheduleNotifyPageControls[pageId] = false;
1190 }
1191
notifyMidiLearnedToGui()1192 void BOops::notifyMidiLearnedToGui ()
1193 {
1194 uint32_t ml = midiLearned[0] * 0x1000000 + midiLearned[1] * 0x10000 + midiLearned[2] * 0x100 + midiLearned[3];
1195 LV2_Atom_Forge_Frame frame;
1196 lv2_atom_forge_frame_time(&forge, 0);
1197 lv2_atom_forge_object(&forge, &frame, 0, urids.bOops_statusEvent);
1198 lv2_atom_forge_key(&forge, urids.bOops_midiLearned);
1199 lv2_atom_forge_int(&forge, ml);
1200 lv2_atom_forge_pop(&forge, &frame);
1201 scheduleNotifyMidiLearnedToGui = false;
1202 }
1203
forgeSamplePath(LV2_Atom_Forge * forge,LV2_Atom_Forge_Frame * frame,const char * path,const int64_t start,const int64_t end,const float amp,const int32_t loop)1204 LV2_Atom_Forge_Ref BOops::forgeSamplePath (LV2_Atom_Forge* forge, LV2_Atom_Forge_Frame* frame, const char* path, const int64_t start, const int64_t end, const float amp, const int32_t loop)
1205 {
1206 const LV2_Atom_Forge_Ref msg = lv2_atom_forge_object (forge, frame, 0, urids.bOops_samplePathEvent);
1207 if (msg)
1208 {
1209 lv2_atom_forge_key (forge, urids.bOops_samplePath);
1210 lv2_atom_forge_path (forge, path, strlen (path) + 1);
1211 lv2_atom_forge_key (forge, urids.bOops_sampleStart);
1212 lv2_atom_forge_long (forge, start);
1213 lv2_atom_forge_key (forge, urids.bOops_sampleEnd);
1214 lv2_atom_forge_long (forge, end);
1215 lv2_atom_forge_key (forge, urids.bOops_sampleAmp);
1216 lv2_atom_forge_float (forge, amp);
1217 lv2_atom_forge_key (forge, urids.bOops_sampleLoop);
1218 lv2_atom_forge_bool (forge, loop);
1219 }
1220 return msg;
1221 }
1222
forgeTransportGateKeys(LV2_Atom_Forge * forge,LV2_Atom_Forge_Frame * frame,const int * keys,const size_t size)1223 LV2_Atom_Forge_Ref BOops::forgeTransportGateKeys (LV2_Atom_Forge* forge, LV2_Atom_Forge_Frame* frame, const int* keys, const size_t size)
1224 {
1225 const LV2_Atom_Forge_Ref msg = lv2_atom_forge_object (forge, frame, 0, urids.bOops_transportGateKeyEvent);
1226 if (msg)
1227 {
1228 lv2_atom_forge_key (forge, urids.bOops_transportGateKeys);
1229 lv2_atom_forge_vector (forge, sizeof(int), urids.atom_Int, size, (void*) keys);
1230 }
1231 return msg;
1232 }
1233
forgeShape(LV2_Atom_Forge * forge,LV2_Atom_Forge_Frame * frame,const int slot,const Shape<SHAPE_MAXNODES> * shape)1234 LV2_Atom_Forge_Ref BOops::forgeShape (LV2_Atom_Forge* forge, LV2_Atom_Forge_Frame* frame, const int slot, const Shape<SHAPE_MAXNODES>* shape)
1235 {
1236 const LV2_Atom_Forge_Ref msg = lv2_atom_forge_object (forge, frame, 0, urids.bOops_shapeEvent);
1237 if (msg)
1238 {
1239 lv2_atom_forge_key(forge, urids.bOops_slot);
1240 lv2_atom_forge_int(forge, slot);
1241 forgeShapeData (forge, frame, shape);
1242 }
1243 return msg;
1244 }
1245
forgeShapeData(LV2_Atom_Forge * forge,LV2_Atom_Forge_Frame * frame,const Shape<SHAPE_MAXNODES> * shape)1246 LV2_Atom_Forge_Ref BOops::forgeShapeData (LV2_Atom_Forge* forge, LV2_Atom_Forge_Frame* frame, const Shape<SHAPE_MAXNODES>* shape)
1247 {
1248 float nodes[SHAPE_MAXNODES][7];
1249 for (unsigned int i = 0; i < shape->size(); ++i)
1250 {
1251 Node n = shape->getNode (i);
1252 nodes[i][0] = n.nodeType;
1253 nodes[i][1] = n.point.x;
1254 nodes[i][2] = n.point.y;
1255 nodes[i][3] = n.handle1.x;
1256 nodes[i][4] = n.handle1.y;
1257 nodes[i][5] = n.handle2.x;
1258 nodes[i][6] = n.handle2.y;
1259 }
1260
1261 lv2_atom_forge_key(forge, urids.bOops_shapeData);
1262 return lv2_atom_forge_vector(forge, sizeof(float), urids.atom_Float, 7 * shape->size(), nodes);
1263 }
1264
forgePads(LV2_Atom_Forge * forge,LV2_Atom_Forge_Frame * frame,const int page,const int slot,const size_t size)1265 LV2_Atom_Forge_Ref BOops::forgePads (LV2_Atom_Forge* forge, LV2_Atom_Forge_Frame* frame, const int page, const int slot, const size_t size)
1266 {
1267 const LV2_Atom_Forge_Ref msg = lv2_atom_forge_object(forge, frame, 0, urids.bOops_slotEvent);
1268 if (msg)
1269 {
1270 lv2_atom_forge_key(forge, urids.bOops_pageID);
1271 lv2_atom_forge_int(forge, page);
1272 lv2_atom_forge_key(forge, urids.bOops_slot);
1273 lv2_atom_forge_int(forge, slot);
1274 lv2_atom_forge_key(forge, urids.bOops_pads);
1275 lv2_atom_forge_vector(forge, sizeof(float), urids.atom_Float, sizeof(Pad) / sizeof(float) * NR_STEPS, (void*) &(pages[page].pads[slot][0]));
1276 }
1277 return msg;
1278 }
1279
forgePageControls(LV2_Atom_Forge * forge,LV2_Atom_Forge_Frame * frame,const int pageId)1280 LV2_Atom_Forge_Ref BOops::forgePageControls (LV2_Atom_Forge* forge, LV2_Atom_Forge_Frame* frame, const int pageId)
1281 {
1282 const LV2_Atom_Forge_Ref msg = lv2_atom_forge_object(forge, frame, 0, urids.bOops_pagePropertiesEvent);
1283 if (msg)
1284 {
1285 const PageControls& controls = pages[pageId].controls;
1286 lv2_atom_forge_key(forge, urids.bOops_pageID);
1287 lv2_atom_forge_int(forge, pageId);
1288 lv2_atom_forge_key(forge, urids.bOops_pageStatus);
1289 lv2_atom_forge_int(forge, controls.status);
1290 lv2_atom_forge_key(forge, urids.bOops_pageChannel);
1291 lv2_atom_forge_int(forge, controls.channel);
1292 lv2_atom_forge_key(forge, urids.bOops_pageMessage);
1293 lv2_atom_forge_int(forge, controls.message);
1294 lv2_atom_forge_key(forge, urids.bOops_pageValue);
1295 lv2_atom_forge_int(forge, controls.value);
1296 }
1297 return msg;
1298 }
1299
getSample(const Position & p,const double pos)1300 Stereo BOops::getSample (const Position& p, const double pos)
1301 {
1302 if (sample && (sample->end > sample->start))
1303 {
1304 const uint64_t f0 = getFramesFromPosition (p.transport, pos);
1305 const int64_t frame =
1306 (
1307 sample->loop ?
1308 (f0 % (sample->end - sample->start)) + sample->start :
1309 f0 + sample->start
1310 );
1311
1312 if (frame < sample->end) return Stereo (sample->get (frame, 0, host.rate), sample->get (frame, 1, host.rate)) * sampleAmp;
1313 else return Stereo();
1314 }
1315
1316 else return Stereo();
1317 }
1318
play(uint32_t start,uint32_t end)1319 void BOops::play (uint32_t start, uint32_t end)
1320 {
1321 if (end < start) return;
1322
1323 if (globalControllers[PLAY] == PLAY_OFF)
1324 {
1325 for (uint32_t i = start; i < end; ++i)
1326 {
1327 for (Slot& s : slots) s.buffer->push_front (Stereo {audioInput1[i], audioInput2[i]});
1328 }
1329 memset(&audioOutput1[start], 0, (end - start) * sizeof(float));
1330 memset(&audioOutput2[start], 0, (end - start) * sizeof(float));
1331 return;
1332
1333 // TODO fader
1334 }
1335
1336 if (globalControllers[PLAY] == PLAY_BYPASS)
1337 {
1338 for (uint32_t i = start; i < end; ++i)
1339 {
1340 Position& p = backPosition();
1341 double relpos = getPositionFromFrames (p.transport, i - p.refFrame); // Position relative to reference frame
1342 double pos = floorfrac (p.sequence + relpos); // 0..1 position sequence
1343
1344 // Input signal
1345 Stereo input = (globalControllers[SOURCE] == SOURCE_SAMPLE) ? getSample (p, pos) : Stereo (audioInput1[i], audioInput2[i]);
1346
1347 // Load samples to buffer
1348 for (Slot& s : slots) s.buffer->push_front (input);
1349
1350 // Waveform
1351 waveformCounter = int (pos * WAVEFORMSIZE) % WAVEFORMSIZE;
1352 waveform[waveformCounter] = (input.left + input.right) / 2;
1353
1354 // Bypass to output
1355 audioOutput1[i] = input.left;
1356 audioOutput2[i] = input.right;
1357 }
1358
1359 // TODO fader
1360
1361 return;
1362 }
1363
1364
1365 for (uint32_t i = start; i < end; ++i)
1366 {
1367 Position& p = positions[0];
1368 p.fader = p.fader + (1.0 - 2.0 * (sizePosition() > 1)) / (FADINGTIME * p.transport.rate);
1369 p.fader = LIMIT (p.fader, 0.0, 1.0);
1370
1371 // Interpolate position within the loop
1372 double relpos = getPositionFromFrames (p.transport, i - p.refFrame); // Position relative to reference frame
1373 double pos = floorfrac (p.sequence + relpos); // 0..1 position sequence
1374
1375 // Input
1376 const Stereo input = (globalControllers[SOURCE] == SOURCE_SAMPLE) ? getSample (p, pos) : Stereo (audioInput1[i], audioInput2[i]);
1377 Stereo output = input;
1378
1379 // Waveform
1380 waveformCounter = int (pos * WAVEFORMSIZE) % WAVEFORMSIZE;
1381 waveform[waveformCounter] = (input.left + input.right) / 2;
1382
1383 if
1384 (
1385 (p.playing) &&
1386 ((p.transport.speed != 0.0f) || (globalControllers[BASE] == SECONDS)) &&
1387 (p.transport.bpm >= 1.0f)
1388 )
1389 {
1390 double step = pos * globalControllers[STEPS];
1391 int iStep = LIMIT (step, 0, globalControllers[STEPS] - 1);
1392
1393 // Init step ?
1394 if (scheduleInit || (p.step != iStep))
1395 {
1396 for (Slot& s : slots)
1397 {
1398 if ((s.effect == FX_INVALID) || (s.effect == FX_NONE)) break;
1399
1400 // Old pad ended?
1401 const int iStart = s.startPos[iStep];
1402 if (((p.step < 0) || (s.startPos[p.step] != iStart)) && (s.getMode() == MODE_PATTERN))
1403 {
1404 // Stop old pad
1405 s.end ();
1406
1407 // Start new pad (if set)
1408 if (iStart >= 0) s.init (iStart);
1409 }
1410
1411 else if (scheduleInit)
1412 {
1413 s.end();
1414 s.init (LIMIT (step, 0, globalControllers[STEPS] - 1));
1415 }
1416 }
1417 scheduleInit = false;
1418 }
1419
1420 // Play slots
1421 const double dsteps = getPositionFromFrames (p.transport, 1) * globalControllers[STEPS];
1422 for (Slot& s : slots)
1423 {
1424 // Store last output
1425 s.buffer->push_front (output);
1426
1427 if ((s.effect == FX_INVALID) || (s.effect == FX_NONE)) break;
1428
1429 // Play music :-)
1430 if (s.params[SLOTS_PLAY])
1431 {
1432 if (s.getMode() == MODE_KEYS)
1433 {
1434 float mx = 0;
1435 for (MidiKey** iit = s.midis.begin(); iit < s.midis.end(); ++iit)
1436 {
1437 if (((**iit).status != 0) && s.isKey ((**iit).note)) mx = std::max (float ((**iit).velocity) * float ((**iit).value) / 127.0f, mx);
1438 }
1439 output = s.play (step, mx);
1440 }
1441
1442 else output = s.play (step);
1443 }
1444
1445 s.mixf = 1.0f;
1446
1447 // Update MidiKeys
1448 for (MidiKey** iit = s.midis.begin(); iit < s.midis.end(); )
1449 {
1450 // Update position;
1451 (**iit).count +=dsteps;
1452
1453 // Use ADSR envelope
1454 double adr = s.params[SLOTS_ATTACK] + s.params[SLOTS_DECAY] + s.params[SLOTS_RELEASE];
1455 if (adr < 1.0f) adr = 1.0f;
1456 const double a = s.params[SLOTS_ATTACK] / adr;
1457 const double d = s.params[SLOTS_DECAY] / adr;
1458 const double r = s.params[SLOTS_RELEASE] / adr;
1459
1460 // Recalculate value
1461 // NOTE_ON
1462 if ((**iit).status == 9)
1463 {
1464 if ((**iit).count < a) (**iit).value = std::min ((**iit).value + dsteps / a, 1.0);
1465 else if ((**iit).count < a + d) (**iit).value = std::max ((**iit).value - dsteps / d, double (s.params[SLOTS_SUSTAIN]));
1466 else (**iit).value = s.params[SLOTS_SUSTAIN];
1467 }
1468
1469 // NOTE_OFF
1470 else if ((**iit).status == 8)
1471 {
1472 if (r == 0) (**iit).value = 0.0;
1473 else (**iit).value -= dsteps / r;
1474 }
1475
1476 else (**iit).value = 0.0;
1477
1478 // Cleanup
1479 if (((**iit).value <= 0.0) || ((**iit).status == 0)) iit = s.midis.erase (iit);
1480 else ++iit;
1481 }
1482 }
1483
1484 p.step = iStep;
1485 }
1486
1487 audioOutput1[i] = (1.0 - p.fader) * input.left + p.fader * output.left;
1488 audioOutput2[i] = (1.0 - p.fader) * input.right + p.fader * output.right;
1489
1490 // Just faded out ?
1491 if ((p.fader <= 0) && (sizePosition() > 1))
1492 {
1493 // Switch to new position data to fade in
1494 popFrontPosition();
1495
1496 // Copy pads
1497 for (int i = 0; i < NR_SLOTS; ++i)
1498 {
1499 for (int j = 0; j < NR_STEPS; ++j) slots[i].setPad (j, pages[pageNr].pads[i][j]);
1500 slots[i].setSlotShape (pages[pageNr].shapes[i]);
1501 slots[i].setSlotKeys (pages[pageNr].keys[i]);
1502 }
1503
1504 scheduleInit = true;
1505 }
1506 }
1507 }
1508
state_save(LV2_State_Store_Function store,LV2_State_Handle handle,uint32_t flags,const LV2_Feature * const * features)1509 LV2_State_Status BOops::state_save (LV2_State_Store_Function store, LV2_State_Handle handle, uint32_t flags,
1510 const LV2_Feature* const* features)
1511 {
1512 // Store sample path
1513 if (sample && sample->path && (sample->path[0] != 0) && (globalControllers[SOURCE] == SOURCE_SAMPLE))
1514 {
1515 LV2_State_Map_Path* mapPath = NULL;
1516 #ifdef LV2_STATE__freePath
1517 LV2_State_Free_Path* freePath = NULL;
1518 #endif
1519
1520 for (int i = 0; features[i]; ++i)
1521 {
1522 if (strcmp(features[i]->URI, LV2_STATE__mapPath) == 0)
1523 {
1524 mapPath = (LV2_State_Map_Path*) features[i]->data;
1525 break;
1526 }
1527 }
1528
1529 #ifdef LV2_STATE__freePath
1530 for (int i = 0; features[i]; ++i)
1531 {
1532 if (strcmp(features[i]->URI, LV2_STATE__freePath) == 0)
1533 {
1534 freePath = (LV2_State_Free_Path*) features[i]->data;
1535 break;
1536 }
1537 }
1538 #endif
1539
1540 if (mapPath)
1541 {
1542 char* abstrPath = mapPath->abstract_path(mapPath->handle, sample->path);
1543
1544 if (abstrPath)
1545 {
1546 fprintf(stderr, "BOops.lv2: Save abstr_path:%s\n", abstrPath);
1547
1548 store(handle, urids.bOops_samplePath, abstrPath, strlen (abstrPath) + 1, urids.atom_Path, LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE);
1549 store(handle, urids.bOops_sampleStart, &sample->start, sizeof (sample->start), urids.atom_Long, LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE);
1550 store(handle, urids.bOops_sampleEnd, &sample->end, sizeof (sample->end), urids.atom_Long, LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE);
1551 store(handle, urids.bOops_sampleAmp, &sampleAmp, sizeof (sampleAmp), urids.atom_Float, LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE);
1552 const int32_t sloop = int32_t (sample->loop);
1553 store(handle, urids.bOops_sampleLoop, &sloop, sizeof (sloop), urids.atom_Bool, LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE);
1554
1555 #ifdef LV2_STATE__freePath
1556 if (freePath) freePath->free_path (freePath->handle, abstrPath);
1557 else
1558 #endif
1559 {
1560 free (abstrPath);
1561 }
1562 }
1563
1564 else fprintf(stderr, "BOops.lv2: Can't generate abstr_path from %s\n", sample->path);
1565 }
1566 else
1567 {
1568 fprintf (stderr, "BOops.lv2: Feature map_path not available! Can't save sample!\n" );
1569 return LV2_STATE_ERR_NO_FEATURE;
1570 }
1571 }
1572
1573 // Store transportGateKeys
1574 {
1575 // Create atom:Vector
1576
1577 AtomKeys atom;
1578 int keysize = 0;
1579 std::fill (atom.keys, atom.keys + NR_PIANO_KEYS, 0);
1580 for (int i = 0; i < NR_PIANO_KEYS; ++i)
1581 {
1582 if (transportGateKeys[i])
1583 {
1584 atom.keys[keysize] = i;
1585 ++keysize;
1586 }
1587 }
1588 atom.body.child_type = urids.atom_Int;
1589 atom.body.child_size = sizeof(int);
1590
1591 store (handle, urids.bOops_transportGateKeys, &atom, keysize * sizeof (int) + sizeof(LV2_Atom_Vector_Body), urids.atom_Vector, LV2_STATE_IS_POD);
1592 }
1593
1594 // Store pageNr
1595 store (handle, urids.bOops_pageID, &pageNr, sizeof (int), urids.atom_Int, LV2_STATE_IS_POD);
1596
1597 // Store pageMax
1598 store (handle, urids.bOops_pageMax, &pageMax, sizeof (int), urids.atom_Int, LV2_STATE_IS_POD);
1599
1600 // Store editor data
1601 store (handle, urids.bOops_editorPage, &editorPage, sizeof (int), urids.atom_Int, LV2_STATE_IS_POD);
1602 store (handle, urids.bOops_editorSlot, &editorSlot, sizeof (int), urids.atom_Int, LV2_STATE_IS_POD);
1603
1604 // Store page control properties
1605 {
1606 AtomPageControls atom;
1607 for (int i = 0; i <= pageMax; ++i) atom.data[i] = pages[i].controls;
1608 atom.body.child_type = urids.atom_Int;
1609 atom.body.child_size = sizeof(int);
1610
1611 store (handle, urids.bOops_pageControls, &atom, (pageMax + 1) * sizeof (PageControls) + sizeof(LV2_Atom_Vector_Body), urids.atom_Vector, LV2_STATE_IS_POD);
1612 }
1613
1614 // Store pads
1615 {
1616 char padDataString[0x8010] = "\nMatrix data:\n";
1617
1618 for (int pgNr = 0; pgNr <= pageMax; ++pgNr)
1619 {
1620 for (int slotNr = 0; slotNr < NR_SLOTS; ++slotNr)
1621 {
1622 if ((slots[slotNr].effect == FX_NONE) || (slots[slotNr].effect == FX_INVALID)) continue;
1623
1624 for (int stepNr = 0; stepNr < NR_STEPS; ++stepNr)
1625 {
1626 Pad p = pages[pgNr].pads[slotNr][stepNr];
1627 if ((p.gate > 0) && (p.size > 0) && (p.mix > 0))
1628 {
1629 char valueString[64];
1630 snprintf (valueString, 62, "pg:%d; sl:%d; st:%d; gt:%1.3f; sz:%d; mx:%1.3f", pgNr, slotNr, stepNr, p.gate, int (p.size), p.mix);
1631 if ((slotNr < NR_SLOTS - 1) || (stepNr < NR_STEPS)) strcat (valueString, ";\n");
1632 else strcat(valueString, "\n");
1633 strcat (padDataString, valueString);
1634 }
1635
1636 }
1637 }
1638 }
1639 store (handle, urids.bOops_statePad, padDataString, strlen (padDataString) + 1, urids.atom_String, LV2_STATE_IS_POD);
1640 }
1641
1642 // Store Keys
1643 {
1644 char keysDataString[8192] = "";
1645
1646 for (int pageNr = 0; pageNr <= pageMax; ++pageNr)
1647 {
1648 strcat (keysDataString, "\nKeys data slots page ");
1649 char pageString[16];
1650 snprintf (pageString, 12, "%d", pageNr);
1651 strcat (keysDataString, pageString);
1652 strcat (keysDataString, ":\n");
1653 for (int slotNr = 0; slotNr < NR_SLOTS; ++slotNr)
1654 {
1655 if (pages[pageNr].keys[slotNr][NR_PIANO_KEYS])
1656 {
1657 char slotString[16];
1658 snprintf (slotString, 12, "slo: %d", slotNr);
1659 char keysString[40];
1660 bool2hstr<std::array<bool, NR_PIANO_KEYS + 1>> (pages[pageNr].keys[slotNr], keysString);
1661
1662 strcat (keysDataString, slotString);
1663 strcat (keysDataString, " key: 0x");
1664 strcat (keysDataString, keysString);
1665 strcat (keysDataString, ";\n");
1666 }
1667 }
1668 }
1669
1670 store (handle, urids.bOops_keysData, keysDataString, strlen (keysDataString) + 1, urids.atom_String, LV2_STATE_IS_POD);
1671 }
1672
1673 // Store shapes
1674 {
1675 char shapesDataString[0x110000] = "";
1676
1677 // Slot shapes
1678 for (int pageNr = 0; pageNr <= pageMax; ++pageNr)
1679 {
1680 strcat (shapesDataString, "\nShape data slots page ");
1681 char pageString[16];
1682 snprintf (pageString, 12, "%d", pageNr);
1683 strcat (shapesDataString, pageString);
1684 strcat (shapesDataString, ":\n");
1685 for (int slotNr = 0; slotNr < NR_SLOTS; ++slotNr)
1686 {
1687 if (pages[pageNr].shapes[slotNr] != Shape<SHAPE_MAXNODES>())
1688 {
1689 for (unsigned int nodeNr = 0; nodeNr < pages[pageNr].shapes[slotNr].size(); ++nodeNr)
1690 {
1691 char valueString[160];
1692 Node node = pages[pageNr].shapes[slotNr].getNode (nodeNr);
1693 snprintf
1694 (
1695 valueString,
1696 126,
1697 "slo:%d; typ:%d; ptx:%f; pty:%f; h1x:%f; h1y:%f; h2x:%f; h2y:%f",
1698 slotNr,
1699 int (node.nodeType),
1700 node.point.x,
1701 node.point.y,
1702 node.handle1.x,
1703 node.handle1.y,
1704 node.handle2.x,
1705 node.handle2.y
1706 );
1707 if ((slotNr < NR_SLOTS - 1) || (nodeNr < pages[pageNr].shapes[slotNr].size ())) strcat (valueString, ";\n");
1708 else strcat(valueString, "\n");
1709 strcat (shapesDataString, valueString);
1710 }
1711 }
1712 }
1713 strcat(shapesDataString, "\n");
1714 }
1715
1716
1717 // Param shapes
1718 strcat (shapesDataString, "\nShape data param:\n");
1719 for (int slotNr = 0; slotNr < NR_SLOTS; ++slotNr)
1720 {
1721 if (!slots[slotNr].shape.isDefault())
1722 {
1723 for (unsigned int nodeNr = 0; nodeNr < slots[slotNr].shape.size (); ++nodeNr)
1724 {
1725 char valueString[160];
1726 Node node = slots[slotNr].shape.getNode (nodeNr);
1727 snprintf
1728 (
1729 valueString,
1730 126,
1731 "slo:%d; typ:%d; ptx:%f; pty:%f; h1x:%f; h1y:%f; h2x:%f; h2y:%f",
1732 slotNr,
1733 int (node.nodeType),
1734 node.point.x,
1735 node.point.y,
1736 node.handle1.x,
1737 node.handle1.y,
1738 node.handle2.x,
1739 node.handle2.y
1740 );
1741 if ((slotNr < NR_SLOTS - 1) || (nodeNr < slots[slotNr].shape.size ())) strcat (valueString, ";\n");
1742 else strcat(valueString, "\n");
1743 strcat (shapesDataString, valueString);
1744 }
1745 }
1746 }
1747 store (handle, urids.bOops_shapeData, shapesDataString, strlen (shapesDataString) + 1, urids.atom_String, LV2_STATE_IS_POD);
1748 }
1749
1750 return LV2_STATE_SUCCESS;
1751 }
1752
state_restore(LV2_State_Retrieve_Function retrieve,LV2_State_Handle handle,uint32_t flags,const LV2_Feature * const * features)1753 LV2_State_Status BOops::state_restore (LV2_State_Retrieve_Function retrieve, LV2_State_Handle handle, uint32_t flags,
1754 const LV2_Feature* const* features)
1755 {
1756 // Get host features
1757 LV2_Worker_Schedule* schedule = nullptr;
1758 LV2_State_Map_Path* mapPath = nullptr;
1759 #ifdef LV2_STATE__freePath
1760 LV2_State_Free_Path* freePath = nullptr;
1761 #endif
1762
1763 for (int i = 0; features[i]; ++i)
1764 {
1765 if (strcmp(features[i]->URI, LV2_STATE__mapPath) == 0)
1766 {
1767 mapPath = (LV2_State_Map_Path*) features[i]->data;
1768 break;
1769 }
1770 }
1771
1772 for (int i = 0; features[i]; ++i)
1773 {
1774 if (strcmp(features[i]->URI, LV2_WORKER__schedule) == 0)
1775 {
1776 schedule = (LV2_Worker_Schedule*) features[i]->data;
1777 break;
1778 }
1779 }
1780
1781 #ifdef LV2_STATE__freePath
1782 for (int i = 0; features[i]; ++i)
1783 {
1784 if (strcmp(features[i]->URI, LV2_STATE__freePath) == 0)
1785 {
1786 freePath = (LV2_State_Free_Path*) features[i]->data;
1787 break;
1788 }
1789 }
1790 #endif
1791
1792 if (!mapPath)
1793 {
1794 fprintf (stderr, "BOops.lv2: Feature map_path not available! Can't restore sample!\n");
1795 return LV2_STATE_ERR_NO_FEATURE;
1796 }
1797
1798 size_t size;
1799 uint32_t type;
1800 uint32_t valflags;
1801
1802 // Retireve sample data
1803 char samplePath[PATH_MAX] = {0};
1804 int64_t sampleStart = 0;
1805 int64_t sampleEnd = 0;
1806 float sampleAmp = 1.0;
1807 int32_t sampleLoop = false;
1808
1809 const void* pathData = retrieve (handle, urids.bOops_samplePath, &size, &type, &valflags);
1810 if (pathData)
1811 {
1812 char* absPath = mapPath->absolute_path (mapPath->handle, (char*)pathData);
1813 if (absPath)
1814 {
1815 if (strlen (absPath) < PATH_MAX) strcpy (samplePath, absPath);
1816 else
1817 {
1818 fprintf (stderr, "BOops.lv2: Sample path too long.\n");
1819 message.setMessage (CANT_OPEN_SAMPLE);
1820 }
1821
1822 fprintf(stderr, "BOops.lv2: Restore abs_path:%s\n", absPath);
1823
1824 #ifdef LV2_STATE__freePath
1825 if (freePath) freePath->free_path (freePath->handle, absPath);
1826 else
1827 #endif
1828 {
1829 free (absPath);
1830 }
1831 }
1832 }
1833
1834 const void* startData = retrieve (handle, urids.bOops_sampleStart, &size, &type, &valflags);
1835 if (startData && (type == urids.atom_Long)) sampleStart = *(int64_t*)startData;
1836 const void* endData = retrieve (handle, urids.bOops_sampleEnd, &size, &type, &valflags);
1837 if (endData && (type == urids.atom_Long)) sampleEnd = *(int64_t*)endData;
1838 const void* ampData = retrieve (handle, urids.bOops_sampleAmp, &size, &type, &valflags);
1839 if (ampData && (type == urids.atom_Float)) sampleAmp = *(float*)ampData;
1840 const void* loopData = retrieve (handle, urids.bOops_sampleLoop, &size, &type, &valflags);
1841 if (loopData && (type == urids.atom_Bool)) sampleLoop = *(int32_t*)loopData;
1842
1843 if (activated && schedule)
1844 {
1845 LV2_Atom_Forge forge;
1846 lv2_atom_forge_init(&forge, map);
1847 uint8_t buf[1200];
1848 lv2_atom_forge_set_buffer(&forge, buf, sizeof(buf));
1849 LV2_Atom_Forge_Frame frame;
1850 LV2_Atom* msg = (LV2_Atom*)forgeSamplePath (&forge, &frame, samplePath, sampleStart, sampleEnd, sampleAmp, sampleLoop);
1851 lv2_atom_forge_pop(&forge, &frame);
1852 if (msg) schedule->schedule_work(schedule->handle, lv2_atom_total_size(msg), msg);
1853 }
1854
1855 else
1856 {
1857 // Free old sample
1858 if (sample)
1859 {
1860 delete sample;
1861 sample = nullptr;
1862 sampleAmp = 1.0;
1863 }
1864
1865 // Load new sample
1866 message.deleteMessage (CANT_OPEN_SAMPLE);
1867 try {sample = new Sample (samplePath);}
1868 catch (std::bad_alloc &ba)
1869 {
1870 fprintf (stderr, "BOops.lv2: Can't allocate enoug memory to open sample file.\n");
1871 message.setMessage (CANT_OPEN_SAMPLE);
1872 }
1873 catch (std::invalid_argument &ia)
1874 {
1875 fprintf (stderr, "%s\n", ia.what());
1876 message.setMessage (CANT_OPEN_SAMPLE);
1877 }
1878
1879 // Set new sample properties
1880 if (sample)
1881 {
1882 sample->start = sampleStart;
1883 sample->end = sampleEnd;
1884 sample->loop = bool (sampleLoop);
1885 this->sampleAmp = sampleAmp;
1886 }
1887
1888 scheduleNotifySamplePathToGui = true;
1889 }
1890
1891 // Retrieve transportGateKeys
1892 const void* transportGateKeysData = retrieve(handle, urids.bOops_transportGateKeys, &size, &type, &valflags);
1893 if (transportGateKeysData && (type == urids.atom_Vector))
1894 {
1895 const AtomKeys* atom = (const AtomKeys*) transportGateKeysData;
1896 const int nr = LIMIT ((size - sizeof (LV2_Atom_Vector_Body)) / sizeof(int), 0, NR_PIANO_KEYS);
1897
1898 std::fill (transportGateKeys, transportGateKeys + NR_PIANO_KEYS, false);
1899 for (int i = 0; i < nr; ++i)
1900 {
1901 const int keyNr = atom->keys[i];
1902 if ((keyNr >= 0) && (keyNr < NR_PIANO_KEYS)) transportGateKeys[keyNr] = true;
1903 }
1904 scheduleNotifyTransportGateKeys = true;
1905 }
1906
1907 // Retrieve pageNr
1908 pageNr = 0;
1909 const void* pageNrData = retrieve(handle, urids.bOops_pageID, &size, &type, &valflags);
1910 if (pageNrData && (type == urids.atom_Int))
1911 {
1912 const int pgNr = LIMIT (*(const int*)pageNrData, 0, NR_PAGES - 1);
1913
1914 if (pgNr != pageNr)
1915 {
1916 Position np = backPosition();
1917 pushBackPosition (np);
1918 }
1919
1920 pageNr = pgNr;
1921 if (pageMax < pageNr) pageMax = pageNr;
1922 }
1923
1924 // Retrieve pageMax
1925 pageMax = 0;
1926 const void* pageMaxData = retrieve(handle, urids.bOops_pageMax, &size, &type, &valflags);
1927 if (pageMaxData && (type == urids.atom_Int))
1928 {
1929 pageMax = LIMIT (*(const int*)pageMaxData, 0, NR_PAGES - 1);
1930
1931 if (pageNr > pageMax)
1932 {
1933 // Limit pageNr to pageMax
1934 pageNr = pageMax;
1935
1936 // Schedule fader for page change
1937 Position np = backPosition();
1938 pushBackPosition (np);
1939 }
1940 }
1941
1942 // Retrieve editor data
1943 editorPage = 0;
1944 editorSlot = 0;
1945
1946 const void* editorPageData = retrieve(handle, urids.bOops_editorPage, &size, &type, &valflags);
1947 if (editorPageData && (type == urids.atom_Int)) editorPage = LIMIT (*(const int*)editorPageData, 0, NR_PAGES - 1);
1948
1949 const void* editorSlotData = retrieve(handle, urids.bOops_editorSlot, &size, &type, &valflags);
1950 if (editorSlotData && (type == urids.atom_Int)) editorSlot = LIMIT (*(const int*)editorSlotData, 0, NR_SLOTS - 1);
1951
1952 scheduleNotifyStatus = true;
1953
1954 // Retrieve page control properties
1955 const void* pageControlsData = retrieve(handle, urids.bOops_pageControls, &size, &type, &valflags);
1956 if (pageControlsData && (type == urids.atom_Vector))
1957 {
1958 const AtomPageControls* atom = (const AtomPageControls*) pageControlsData;
1959 const int nr = LIMIT ((size - sizeof (LV2_Atom_Vector_Body)) / sizeof(PageControls), 0, NR_PAGES - 1);
1960
1961 for (Page& page : pages) page.controls = {0, 0, 0, 0};
1962 for (int i = 0; i < nr; ++i) pages[i].controls = atom->data[i];
1963 for (int i = 0; i < NR_PAGES; ++i) scheduleNotifyPageControls[i] = true;
1964 }
1965
1966 // Retrieve pattern
1967 const void* padData = retrieve(handle, urids.bOops_statePad, &size, &type, &valflags);
1968 if (padData && (type == urids.atom_String))
1969 {
1970 // Clear pads
1971 for (int pg = 0; pg < NR_PAGES; ++pg)
1972 {
1973 for (std::array<Pad, NR_STEPS>& row : pages[pg].pads) row.fill (Pad());
1974 }
1975
1976 // Also clear pads stored in slots
1977 for (Slot& s : slots)
1978 {
1979 for (unsigned int i = 0; i < NR_STEPS; ++i) s.setPad (i, Pad());
1980 }
1981
1982 std::string padDataString = (char*) padData;
1983 const std::string keywords[6] = {"pg:", "sl:", "st:", "gt:", "sz:", "mx:"};
1984
1985 // Restore pads
1986 // Parse retrieved data
1987 while (!padDataString.empty())
1988 {
1989 // Look for optional "pg:"
1990 int pgNr = 0;
1991 size_t strPos = padDataString.find (keywords[0]);
1992 size_t nextPos = 0;
1993 if (strPos != std::string::npos)
1994 {
1995 if (strPos + 3 > padDataString.length()) break; // Nothing more after pg => end
1996 padDataString.erase (0, strPos + 3);
1997 try {pgNr = BUtilities::stof (padDataString, &nextPos);}
1998 catch (const std::exception& e)
1999 {
2000 fprintf (stderr, "BOops.lv2: Restore pad state incomplete. Can't parse page nr from \"%s...\"\n", padDataString.substr (0, 63).c_str());
2001 break;
2002 }
2003
2004 if (nextPos > 0) padDataString.erase (0, nextPos);
2005 if ((pgNr < 0) || (pgNr >= NR_PAGES))
2006 {
2007 fprintf (stderr, "BOops.lv2: Restore pad state incomplete. Invalid page nr %i.\n", pgNr);
2008 break;
2009 }
2010 }
2011
2012 // Look for mandatory "sl:"
2013 int slotNr = -1;
2014 strPos = padDataString.find (keywords[1]);
2015 nextPos = 0;
2016 if (strPos == std::string::npos) break; // No "sl:" found => end
2017 if (strPos + 3 > padDataString.length()) break; // Nothing more after sl => end
2018 padDataString.erase (0, strPos + 3);
2019 try {slotNr = BUtilities::stof (padDataString, &nextPos);}
2020 catch (const std::exception& e)
2021 {
2022 fprintf (stderr, "BOops.lv2: Restore pad state incomplete. Can't parse slot nr from \"%s...\"\n", padDataString.substr (0, 63).c_str());
2023 break;
2024 }
2025
2026 if (nextPos > 0) padDataString.erase (0, nextPos);
2027 if ((slotNr < 0) || (slotNr >= NR_SLOTS))
2028 {
2029 fprintf (stderr, "BOops.lv2: Restore pad state incomplete. Invalid slot nr %i.\n", slotNr);
2030 break;
2031 }
2032
2033 // Look for mandatory "st:"
2034 int stepNr = -1;
2035 strPos = padDataString.find (keywords[2]);
2036 nextPos = 0;
2037 if (strPos == std::string::npos) break; // No "st:" found => end
2038 if (strPos + 3 > padDataString.length()) break; // Nothing more after st => end
2039 padDataString.erase (0, strPos + 3);
2040 try {stepNr = BUtilities::stof (padDataString, &nextPos);}
2041 catch (const std::exception& e)
2042 {
2043 fprintf (stderr, "BOops.lv2: Restore pad state incomplete. Can't parse step nr from \"%s...\"\n", padDataString.substr (0, 63).c_str());
2044 break;
2045 }
2046
2047 if (nextPos > 0) padDataString.erase (0, nextPos);
2048 if ((stepNr < 0) || (stepNr >= NR_STEPS))
2049 {
2050 fprintf (stderr, "BOops.lv2: Restore pad state incomplete. Invalid step nr %i.\n", slotNr);
2051 break;
2052 }
2053
2054 // Look for pad data
2055 for (int i = 3; i < 6; ++i)
2056 {
2057 nextPos = 0;
2058 strPos = padDataString.find (keywords[i]);
2059 if (strPos == std::string::npos) continue; // Keyword not found => next keyword
2060 if (strPos + 3 >= padDataString.length()) // Nothing more after keyword => end
2061 {
2062 padDataString ="";
2063 break;
2064 }
2065 if (strPos > 0) padDataString.erase (0, strPos + 3);
2066 float val;
2067 try {val = BUtilities::stof (padDataString, &nextPos);}
2068 catch (const std::exception& e)
2069 {
2070 fprintf (stderr, "BOops.lv2: Restore pad state incomplete. Can't parse %s from \"%s...\"\n",
2071 keywords[i].substr(0,2).c_str(), padDataString.substr (0, 63).c_str());
2072 break;
2073 }
2074
2075 if (nextPos > 0) padDataString.erase (0, nextPos);
2076
2077 Pad& p = pages[pgNr].pads[slotNr][stepNr];
2078 switch (i)
2079 {
2080 case 3: p.gate = LIMIT (val, 0, 1);
2081 break;
2082
2083 case 4: p.size = LIMIT (val, 1, NR_STEPS - stepNr);
2084 break;
2085
2086 case 5: p.mix = LIMIT (val, 0, 1);
2087 break;
2088
2089 default:break;
2090 }
2091 if (pageNr == pgNr) slots[slotNr].setPad (stepNr, p);
2092 }
2093 }
2094
2095 // Schedule notify GUI
2096 scheduleNotifyAllSlots = true;
2097 }
2098
2099 // Retrieve keys
2100 for (Page& p : pages) for (std::array<bool, NR_PIANO_KEYS + 1>& k : p.keys) k.fill (false);
2101 const void* keysData = retrieve(handle, urids.bOops_keysData, &size, &type, &valflags);
2102 if (keysData && (type == urids.atom_String))
2103 {
2104 // Parse retrieved data
2105 std::string s = (char*) keysData;
2106
2107 while (!s.empty())
2108 {
2109 // Check for slots page
2110 size_t pos = s.find ("Keys data slots page");
2111 if ((pos == std::string::npos) || (pos + 20 >= s.length())) break;
2112 s.erase (0, pos + 20);
2113
2114 // Parse page number
2115 int pageNr = 0;
2116 try {pageNr = BUtilities::stof (s, &pos);}
2117 catch (const std::exception& e)
2118 {
2119 fprintf (stderr, "BOops.lv2: Restore keys state incomplete. Can't parse page number from \"%s...\"", s.substr (0, 63).c_str());
2120 break;
2121 }
2122
2123 if (pos > 0) s.erase (0, pos);
2124 if ((pageNr < 0) || (pageNr >= NR_PAGES))
2125 {
2126 fprintf (stderr, "BOops.lv2: Restore keys state incomplete. Invalid matrix data block loaded for page %i.\n", pageNr);
2127 break;
2128 }
2129
2130 while (!s.empty())
2131 {
2132 pos = s.find ("slo:");
2133 if (pos == std::string::npos) break;
2134 if (pos + 4 >= s.length())
2135 {
2136 fprintf (stderr, "BOops.lv2: Restore keys state incomplete. Can't parse slot number from \"%s...\"", s.substr (0, 63).c_str());
2137 break;
2138 }
2139
2140 s.erase (0, pos + 4);
2141
2142 int sl;
2143 try {sl = BUtilities::stof (s, &pos);}
2144 catch (const std::exception& e)
2145 {
2146 fprintf (stderr, "BOops.lv2: Restore keys state incomplete. Can't parse slot number from \"%s...\"", s.substr (0, 63).c_str());
2147 break;
2148 }
2149
2150 if (pos > 0) s.erase (0, pos);
2151 if ((sl < 0) || (sl >= NR_SLOTS))
2152 {
2153 fprintf (stderr, "BOops.lv2: Restore keys state incomplete. Invalid matrix data block loaded for slot %i.\n", sl);
2154 break;
2155 }
2156
2157 size_t kPos = s.find ("key: 0x");
2158 size_t ePos = s.find (";");
2159 if ((kPos == std::string::npos) || (kPos + 7 >= s.length()) || (kPos == std::string::npos) || (kPos + 47 < ePos))
2160 {
2161 fprintf (stderr, "BOops.lv2: Restore keys state incomplete. Invalid matrix data block loaded for slot %i.\n", sl);
2162 break;
2163 }
2164
2165 hstr2bool<std::array<bool, NR_PIANO_KEYS + 1>> (s.substr (kPos + 7, ePos - kPos - 7).c_str(), pages[pageNr].keys[sl]);
2166 s.erase (0, ePos + 1);
2167 }
2168 }
2169
2170 scheduleNotifyAllSlots = true;
2171 }
2172 for (int i = 0; i < NR_SLOTS; ++i) slots[i].setSlotKeys (pages[pageNr].keys[i]);
2173
2174 // Retrieve shapes
2175 for (Page& p : pages) for (Shape<SHAPE_MAXNODES>& s : p.shapes) s = Shape<SHAPE_MAXNODES>();
2176 const void* shapesData = retrieve(handle, urids.bOops_shapeData, &size, &type, &valflags);
2177 if (shapesData && (type == urids.atom_String))
2178 {
2179 std::array<Shape<SHAPE_MAXNODES>, NR_SLOTS> paramShapes;
2180 std::array<std::array<Shape<SHAPE_MAXNODES>, NR_SLOTS>, NR_PAGES> pageShapes;
2181 for (int sl = 0; sl < NR_SLOTS; ++sl) paramShapes[sl].clearShape();
2182 for (int pg = 0; pg < NR_PAGES; ++pg)
2183 {
2184 for (int sl = 0; sl < NR_SLOTS; ++sl) pageShapes[pg][sl].clearShape();
2185 }
2186
2187 // Parse retrieved data
2188 std::string shapesDataString = (char*) shapesData;
2189 const std::string keywords[8] = {"slo:", "typ:", "ptx:", "pty:", "h1x:", "h1y:", "h2x:", "h2y:"};
2190
2191 size_t startPos = shapesDataString.find ("Shape data");
2192 while (startPos != std::string::npos)
2193 {
2194 shapesDataString.erase (0, startPos + 10);
2195
2196 size_t nextPos = shapesDataString.find ("Shape data");
2197 std::string s = shapesDataString.substr (0, nextPos);
2198
2199 // Check for slots page
2200 size_t slotspagePos = s.find ("slots page");
2201 if ((slotspagePos != std::string::npos) && (slotspagePos < 10))
2202 {
2203 s.erase (0, slotspagePos + 10);
2204
2205 // Parse page number
2206 size_t np;
2207 int pageNr = 0;
2208 try {pageNr = BUtilities::stof (s, &np);}
2209 catch (const std::exception& e)
2210 {
2211 fprintf (stderr, "BOops.lv2: Restore shape state incomplete. Can't parse page number from \"%s...\"", s.substr (0, 63).c_str());
2212 break;
2213 }
2214
2215 if (np > 0) s.erase (0, np);
2216 if ((pageNr < 0) || (pageNr >= NR_PAGES))
2217 {
2218 fprintf (stderr, "BOops.lv2: Restore shape state incomplete. Invalid matrix data block loaded for page %i.\n", pageNr);
2219 break;
2220 }
2221
2222 to_shapes (s, pageShapes[pageNr]);
2223 for (int pg = 0; pg < NR_PAGES; ++pg)
2224 {
2225 for (int sl = 0; sl < NR_SLOTS; ++sl)
2226 {
2227 if (pageShapes[pg][sl] != Shape<SHAPE_MAXNODES>())
2228 {
2229 if (!pageShapes[pg][sl].validateShape ()) pageShapes[pg][sl].setDefaultShape ();
2230 }
2231 pages[pg].shapes[sl] = pageShapes[pg][sl];
2232 }
2233 }
2234 scheduleNotifyAllSlots = true;
2235 }
2236
2237 // Param shapes
2238 else
2239 {
2240 to_shapes (s, paramShapes);
2241 for (int sl = 0; sl < NR_SLOTS; ++sl)
2242 {
2243 if (paramShapes[sl].size () < 2) paramShapes[sl].setDefaultShape ();
2244 else
2245 {
2246 if (!paramShapes[sl].validateShape ()) paramShapes[sl].setDefaultShape ();
2247 }
2248 slots[sl].shape = paramShapes[sl];
2249 scheduleNotifyShape[sl] = true;
2250 }
2251 }
2252
2253 startPos = nextPos;
2254 }
2255 }
2256 for (int i = 0; i < NR_SLOTS; ++i) slots[i].setSlotShape (pages[pageNr].shapes[i]);
2257
2258 return LV2_STATE_SUCCESS;
2259 }
2260
work(LV2_Worker_Respond_Function respond,LV2_Worker_Respond_Handle handle,uint32_t size,const void * data)2261 LV2_Worker_Status BOops::work (LV2_Worker_Respond_Function respond, LV2_Worker_Respond_Handle handle, uint32_t size, const void* data)
2262 {
2263 const LV2_Atom* atom = (const LV2_Atom*)data;
2264 if (!atom) return LV2_WORKER_ERR_UNKNOWN;
2265
2266 // Free old buffers
2267 else if (atom->type == urids.bOops_freeBuffers)
2268 {
2269 const Atom_BufferList* bAtom = (const Atom_BufferList*) data;
2270 for (int i = 0; i < NR_SLOTS; ++i)
2271 {
2272 if (bAtom->buffers[i]) delete (bAtom->buffers[i]);
2273 }
2274 }
2275
2276 // Free old Fx
2277 else if (atom->type == urids.bOops_freeFx)
2278 {
2279 const Atom_Fx* fAtom = (const Atom_Fx*) data;
2280 if (fAtom->fx) delete (fAtom->fx);
2281 }
2282
2283 // Free old sample
2284 else if (atom->type == urids.bOops_sampleFreeEvent)
2285 {
2286 const AtomSample* sAtom = (AtomSample*) atom;
2287 if (sAtom->sample) delete sAtom->sample;
2288 }
2289
2290 // Load sample
2291 else if ((atom->type == urids.atom_Object) && (((LV2_Atom_Object*)atom)->body.otype == urids.bOops_samplePathEvent))
2292 {
2293 const LV2_Atom_Object* obj = (const LV2_Atom_Object*)data;
2294
2295 const LV2_Atom* oPath = NULL, *oStart = NULL, *oEnd = NULL, *oAmp = NULL, *oLoop = NULL;
2296 lv2_atom_object_get
2297 (
2298 obj,
2299 urids.bOops_samplePath, &oPath,
2300 urids.bOops_sampleStart, &oStart,
2301 urids.bOops_sampleEnd, &oEnd,
2302 urids.bOops_sampleAmp, &oAmp,
2303 urids.bOops_sampleLoop, &oLoop,
2304 0
2305 );
2306
2307 // New sample
2308 if (oPath && (oPath->type == urids.atom_Path))
2309 {
2310 message.deleteMessage (CANT_OPEN_SAMPLE);
2311 Sample* s = nullptr;
2312
2313 const char* pathName = (const char*)LV2_ATOM_BODY_CONST(oPath);
2314 if (pathName && (pathName[0] != 0))
2315 {
2316 try {s = new Sample (pathName);}
2317 catch (std::bad_alloc &ba)
2318 {
2319 fprintf (stderr, "BOops.lv2: Can't allocate enough memory to open sample file.\n");
2320 message.setMessage (CANT_OPEN_SAMPLE);
2321 return LV2_WORKER_ERR_NO_SPACE;
2322 }
2323 catch (std::invalid_argument &ia)
2324 {
2325 fprintf (stderr, "%s\n", ia.what());
2326 message.setMessage (CANT_OPEN_SAMPLE);
2327 return LV2_WORKER_ERR_UNKNOWN;
2328 }
2329 }
2330
2331 AtomSample sAtom;
2332 sAtom.atom = {sizeof (s), urids.bOops_installSample};
2333 sAtom.sample = s;
2334 sAtom.start = (oStart && (oStart->type == urids.atom_Long) && s ? ((LV2_Atom_Long*)oStart)->body : 0);
2335 sAtom.end = (s ? (oEnd && (oEnd->type == urids.atom_Long) ? ((LV2_Atom_Long*)oEnd)->body : s->info.frames) : 0);
2336 sAtom.amp = (oAmp && (oAmp->type == urids.atom_Float) ? ((LV2_Atom_Float*)oAmp)->body : 1.0f);
2337 sAtom.loop = (oLoop && (oLoop->type == urids.atom_Bool && s) ? ((LV2_Atom_Bool*)oLoop)->body : 0);
2338 respond (handle, sizeof(sAtom), &sAtom);
2339 }
2340
2341 else return LV2_WORKER_ERR_UNKNOWN;
2342 }
2343
2344 // Allocate new buffers
2345 else if (atom->type == urids.bOops_allocateBuffers)
2346 {
2347 //Required buffer size
2348 double fpst = getFramesPerStep (backPosition().transport);
2349 size_t bSize = slots[0].buffer->size();
2350
2351 if ((bSize < globalControllers[STEPS] * fpst) || (bSize > 2.0 * globalControllers[STEPS] * fpst))
2352 {
2353 Atom_BufferList bAtom;
2354 bAtom.atom = {sizeof (bAtom.buffers), urids.bOops_installBuffers};
2355 size_t nSize = 1.5 * globalControllers[STEPS] * fpst;
2356
2357 for (int i = 0; i < NR_SLOTS; ++i)
2358 {
2359 try
2360 {
2361 RingBuffer<Stereo>* b = new RingBuffer<Stereo> (*slots[i].buffer);
2362 bAtom.buffers[i] = b;
2363 bAtom.buffers[i]->resize (nSize);
2364 }
2365 catch (std::bad_alloc& ba)
2366 {
2367 fprintf (stderr, "BOops.lv2: Can't allocate enough memory to resize audio buffers.\n");
2368 //message.setMessage (MEMORY_ERR);
2369 return LV2_WORKER_ERR_NO_SPACE;
2370 }
2371 }
2372
2373 respond (handle, sizeof (bAtom) , &bAtom);
2374 }
2375
2376 // Resize not needed
2377 else scheduleResizeBuffers = false;
2378 }
2379
2380 else if (atom->type == urids.bOops_allocateFx)
2381 {
2382 const LV2_Atom_Int* iAtom = (const LV2_Atom_Int*)data;
2383 int slotNr = iAtom->body;
2384
2385 BOopsEffectsIndex effect = (BOopsEffectsIndex) *new_controllers[SLOTS + slotNr * (SLOTS_PARAMS + NR_PARAMS)];
2386 Fx* fx = slots[slotNr].newFx (effect);
2387 Atom_Fx fAtom = {{sizeof (int) + sizeof (BOopsEffectsIndex) + sizeof (Fx*), urids.bOops_installFx}, slotNr, effect, fx};
2388 respond (handle, sizeof (fAtom) , &fAtom);
2389 }
2390
2391 else return LV2_WORKER_ERR_UNKNOWN;
2392
2393 return LV2_WORKER_SUCCESS;
2394 }
2395
work_response(uint32_t size,const void * data)2396 LV2_Worker_Status BOops::work_response (uint32_t size, const void* data)
2397 {
2398 const LV2_Atom* atom = (const LV2_Atom*)data;
2399 if (!atom) return LV2_WORKER_ERR_UNKNOWN;
2400
2401 // Install slot audio buffers
2402 if (atom->type == urids.bOops_installBuffers)
2403 {
2404 // Schedule worker to free old buffers
2405 Atom_BufferList bAtom;
2406 bAtom.atom = {sizeof (bAtom.buffers), urids.bOops_freeBuffers};
2407 for (int i = 0; i < NR_SLOTS; ++i) bAtom.buffers[i] = slots[i].buffer;
2408 workerSchedule->schedule_work (workerSchedule->handle, sizeof (bAtom), &bAtom);
2409
2410 // Install new buffer
2411 const Atom_BufferList* nAtom = (const Atom_BufferList*) data;
2412 for (int i = 0; i < NR_SLOTS; ++i) slots[i].buffer = nAtom->buffers[i];
2413 scheduleResizeBuffers = false;
2414 }
2415
2416 // Install Fx
2417 else if (atom->type == urids.bOops_installFx)
2418 {
2419 const Atom_Fx* nAtom = (const Atom_Fx*) data;
2420
2421 // Schedule worker to free old Fx
2422 Atom_Fx fAtom;
2423 fAtom = {{sizeof (int) + sizeof (BOopsEffectsIndex) + sizeof (Fx*), urids.bOops_freeFx}, nAtom->index, slots[nAtom->index].effect, slots[nAtom->index].fx};
2424 workerSchedule->schedule_work (workerSchedule->handle, sizeof (fAtom), &fAtom);
2425
2426 // Install new Fx
2427 slots[nAtom->index].fx = nAtom->fx;
2428 slots[nAtom->index].effect = BOopsEffectsIndex (nAtom->effect);
2429 scheduleSetFx[nAtom->index] = false;
2430 }
2431
2432 // Install sample
2433 else if (atom->type == urids.bOops_installSample)
2434 {
2435 const AtomSample* nAtom = (const AtomSample*)data;
2436 // Schedule worker to free old sample
2437 AtomSample sAtom = {{sizeof (Sample*), urids.bOops_sampleFreeEvent}, sample};
2438 workerSchedule->schedule_work (workerSchedule->handle, sizeof (sAtom), &sAtom);
2439
2440 // Install new sample from data
2441 sample = nAtom->sample;
2442 if (sample)
2443 {
2444 sample->start = LIMIT (nAtom->start, 0, sample->info.frames - 1);
2445 sample->end = LIMIT (nAtom->end, sample->start, sample->info.frames);
2446 sampleAmp = LIMIT (nAtom->amp, 0.0f, 1.0f);
2447 sample->loop = bool (nAtom->loop);
2448 }
2449 scheduleNotifySamplePathToGui = true;
2450 scheduleStateChanged = true;
2451 }
2452
2453 return LV2_WORKER_SUCCESS;
2454 }
2455
instantiate(const LV2_Descriptor * descriptor,double samplerate,const char * bundle_path,const LV2_Feature * const * features)2456 static LV2_Handle instantiate (const LV2_Descriptor* descriptor, double samplerate, const char* bundle_path, const LV2_Feature* const* features)
2457 {
2458 // New instance
2459 BOops* instance;
2460 try {instance = new BOops(samplerate, bundle_path, features);}
2461 catch (std::exception& exc)
2462 {
2463 fprintf (stderr, "BOops.lv2: Plugin instantiation failed. %s\n", exc.what ());
2464 return NULL;
2465 }
2466
2467 if (!instance)
2468 {
2469 fprintf(stderr, "BOops.lv2: Plugin instantiation failed.\n");
2470 return NULL;
2471 }
2472
2473 if (!instance->map)
2474 {
2475 fprintf(stderr, "BOops.lv2: Host does not support urid:map.\n");
2476 delete (instance);
2477 return NULL;
2478 }
2479
2480 return (LV2_Handle)instance;
2481 }
2482
connect_port(LV2_Handle instance,uint32_t port,void * data)2483 static void connect_port (LV2_Handle instance, uint32_t port, void *data)
2484 {
2485 BOops* inst = (BOops*) instance;
2486 if (inst) inst->connect_port (port, data);
2487 }
2488
activate(LV2_Handle instance)2489 static void activate (LV2_Handle instance)
2490 {
2491 BOops* inst = (BOops*) instance;
2492 if (inst) inst->activate();
2493 }
2494
run(LV2_Handle instance,uint32_t n_samples)2495 static void run (LV2_Handle instance, uint32_t n_samples)
2496 {
2497 BOops* inst = (BOops*) instance;
2498 if (inst) inst->run (n_samples);
2499 }
2500
deactivate(LV2_Handle instance)2501 static void deactivate (LV2_Handle instance)
2502 {
2503 BOops* inst = (BOops*) instance;
2504 if (inst) inst->deactivate();
2505 }
2506
cleanup(LV2_Handle instance)2507 static void cleanup (LV2_Handle instance)
2508 {
2509 BOops* inst = (BOops*) instance;
2510 if (inst) delete inst;
2511 }
2512
state_save(LV2_Handle instance,LV2_State_Store_Function store,LV2_State_Handle handle,uint32_t flags,const LV2_Feature * const * features)2513 static LV2_State_Status state_save(LV2_Handle instance, LV2_State_Store_Function store, LV2_State_Handle handle, uint32_t flags,
2514 const LV2_Feature* const* features)
2515 {
2516 BOops* inst = (BOops*)instance;
2517 if (!inst) return LV2_STATE_SUCCESS;
2518
2519 inst->state_save (store, handle, flags, features);
2520 return LV2_STATE_SUCCESS;
2521 }
2522
state_restore(LV2_Handle instance,LV2_State_Retrieve_Function retrieve,LV2_State_Handle handle,uint32_t flags,const LV2_Feature * const * features)2523 static LV2_State_Status state_restore(LV2_Handle instance, LV2_State_Retrieve_Function retrieve, LV2_State_Handle handle, uint32_t flags,
2524 const LV2_Feature* const* features)
2525 {
2526 BOops* inst = (BOops*)instance;
2527 if (inst) inst->state_restore (retrieve, handle, flags, features);
2528 return LV2_STATE_SUCCESS;
2529 }
2530
work(LV2_Handle instance,LV2_Worker_Respond_Function respond,LV2_Worker_Respond_Handle handle,uint32_t size,const void * data)2531 static LV2_Worker_Status work (LV2_Handle instance, LV2_Worker_Respond_Function respond, LV2_Worker_Respond_Handle handle,
2532 uint32_t size, const void* data)
2533 {
2534 BOops* inst = (BOops*)instance;
2535 if (!inst) return LV2_WORKER_SUCCESS;
2536
2537 return inst->work (respond, handle, size, data);
2538 }
2539
work_response(LV2_Handle instance,uint32_t size,const void * data)2540 static LV2_Worker_Status work_response (LV2_Handle instance, uint32_t size, const void* data)
2541 {
2542 BOops* inst = (BOops*)instance;
2543 if (!inst) return LV2_WORKER_SUCCESS;
2544
2545 return inst->work_response (size, data);
2546 }
2547
extension_data(const char * uri)2548 static const void* extension_data(const char* uri)
2549 {
2550 static const LV2_State_Interface state = {state_save, state_restore};
2551 static const LV2_Worker_Interface worker = {work, work_response, NULL};
2552 if (!strcmp(uri, LV2_STATE__interface)) return &state;
2553 if (!strcmp(uri, LV2_WORKER__interface)) return &worker;
2554 return NULL;
2555 }
2556
2557 static const LV2_Descriptor descriptor =
2558 {
2559 BOOPS_URI,
2560 instantiate,
2561 connect_port,
2562 activate,
2563 run,
2564 deactivate,
2565 cleanup,
2566 extension_data
2567 };
2568
2569 // LV2 Symbol Export
lv2_descriptor(uint32_t index)2570 LV2_SYMBOL_EXPORT const LV2_Descriptor* lv2_descriptor(uint32_t index)
2571 {
2572 switch (index)
2573 {
2574 case 0: return &descriptor;
2575 default: return NULL;
2576 }
2577 }
2578