Mercurial > hg > pub > prymula > com
comparison DPF-Prymula-audioplugins/dpf/distrho/src/DistrhoPluginLV2.cpp @ 3:84e66ea83026
DPF-Prymula-audioplugins-0.231015-2
author | prymula <prymula76@outlook.com> |
---|---|
date | Mon, 16 Oct 2023 21:53:34 +0200 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
2:cf2cb71d31dd | 3:84e66ea83026 |
---|---|
1 /* | |
2 * DISTRHO Plugin Framework (DPF) | |
3 * Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com> | |
4 * | |
5 * Permission to use, copy, modify, and/or distribute this software for any purpose with | |
6 * or without fee is hereby granted, provided that the above copyright notice and this | |
7 * permission notice appear in all copies. | |
8 * | |
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |
10 * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |
11 * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |
12 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |
13 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |
14 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
15 */ | |
16 | |
17 #include "DistrhoPluginInternal.hpp" | |
18 | |
19 #include "lv2/atom.h" | |
20 #include "lv2/atom-forge.h" | |
21 #include "lv2/atom-util.h" | |
22 #include "lv2/buf-size.h" | |
23 #include "lv2/data-access.h" | |
24 #include "lv2/instance-access.h" | |
25 #include "lv2/midi.h" | |
26 #include "lv2/options.h" | |
27 #include "lv2/parameters.h" | |
28 #include "lv2/patch.h" | |
29 #include "lv2/state.h" | |
30 #include "lv2/time.h" | |
31 #include "lv2/urid.h" | |
32 #include "lv2/worker.h" | |
33 #include "lv2/lv2_kxstudio_properties.h" | |
34 #include "lv2/lv2_programs.h" | |
35 #include "lv2/control-input-port-change-request.h" | |
36 | |
37 #ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD | |
38 # include "libmodla.h" | |
39 #endif | |
40 | |
41 #include <map> | |
42 | |
43 #ifndef DISTRHO_PLUGIN_URI | |
44 # error DISTRHO_PLUGIN_URI undefined! | |
45 #endif | |
46 | |
47 #ifndef DISTRHO_PLUGIN_LV2_STATE_PREFIX | |
48 # define DISTRHO_PLUGIN_LV2_STATE_PREFIX "urn:distrho:" | |
49 #endif | |
50 | |
51 #define DISTRHO_LV2_USE_EVENTS_IN (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_TIMEPOS || DISTRHO_PLUGIN_WANT_STATE) | |
52 #define DISTRHO_LV2_USE_EVENTS_OUT (DISTRHO_PLUGIN_WANT_MIDI_OUTPUT || DISTRHO_PLUGIN_WANT_STATE) | |
53 | |
54 START_NAMESPACE_DISTRHO | |
55 | |
56 typedef std::map<const String, String> StringToStringMap; | |
57 typedef std::map<const LV2_URID, String> UridToStringMap; | |
58 | |
59 #if ! DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |
60 static const writeMidiFunc writeMidiCallback = nullptr; | |
61 #endif | |
62 #if ! DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST | |
63 static const requestParameterValueChangeFunc requestParameterValueChangeCallback = nullptr; | |
64 #endif | |
65 #if ! DISTRHO_PLUGIN_WANT_STATE | |
66 static const updateStateValueFunc updateStateValueCallback = nullptr; | |
67 #endif | |
68 | |
69 // ----------------------------------------------------------------------- | |
70 | |
71 class PluginLv2 | |
72 { | |
73 public: | |
74 PluginLv2(const double sampleRate, | |
75 const LV2_URID_Map* const uridMap, | |
76 const LV2_Worker_Schedule* const worker, | |
77 const LV2_ControlInputPort_Change_Request* const ctrlInPortChangeReq, | |
78 const bool usingNominal) | |
79 : fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback, updateStateValueCallback), | |
80 fUsingNominal(usingNominal), | |
81 #ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD | |
82 fRunCount(0), | |
83 #endif | |
84 fPortControls(nullptr), | |
85 fLastControlValues(nullptr), | |
86 fSampleRate(sampleRate), | |
87 fURIDs(uridMap), | |
88 #if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST | |
89 fCtrlInPortChangeReq(ctrlInPortChangeReq), | |
90 #endif | |
91 fUridMap(uridMap), | |
92 fWorker(worker) | |
93 { | |
94 #if DISTRHO_PLUGIN_NUM_INPUTS > 0 | |
95 for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i) | |
96 fPortAudioIns[i] = nullptr; | |
97 #else | |
98 fPortAudioIns = nullptr; | |
99 #endif | |
100 | |
101 #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |
102 for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) | |
103 fPortAudioOuts[i] = nullptr; | |
104 #else | |
105 fPortAudioOuts = nullptr; | |
106 #endif | |
107 | |
108 if (const uint32_t count = fPlugin.getParameterCount()) | |
109 { | |
110 fPortControls = new float*[count]; | |
111 fLastControlValues = new float[count]; | |
112 | |
113 for (uint32_t i=0; i < count; ++i) | |
114 { | |
115 fPortControls[i] = nullptr; | |
116 fLastControlValues[i] = fPlugin.getParameterValue(i); | |
117 } | |
118 } | |
119 else | |
120 { | |
121 fPortControls = nullptr; | |
122 fLastControlValues = nullptr; | |
123 } | |
124 | |
125 #if DISTRHO_LV2_USE_EVENTS_IN | |
126 fPortEventsIn = nullptr; | |
127 #endif | |
128 #if DISTRHO_PLUGIN_WANT_LATENCY | |
129 fPortLatency = nullptr; | |
130 #endif | |
131 | |
132 #if DISTRHO_PLUGIN_WANT_STATE | |
133 std::memset(&fAtomForge, 0, sizeof(fAtomForge)); | |
134 lv2_atom_forge_init(&fAtomForge, uridMap); | |
135 | |
136 if (const uint32_t count = fPlugin.getStateCount()) | |
137 { | |
138 fUrids = new LV2_URID[count]; | |
139 fNeededUiSends = new bool[count]; | |
140 | |
141 for (uint32_t i=0; i < count; ++i) | |
142 { | |
143 fNeededUiSends[i] = false; | |
144 | |
145 const String& statekey(fPlugin.getStateKey(i)); | |
146 fStateMap[statekey] = fPlugin.getStateDefaultValue(i); | |
147 | |
148 const String lv2key(DISTRHO_PLUGIN_URI "#" + statekey); | |
149 const LV2_URID urid = fUrids[i] = uridMap->map(uridMap->handle, lv2key.buffer()); | |
150 fUridStateMap[urid] = statekey; | |
151 } | |
152 } | |
153 else | |
154 { | |
155 fUrids = nullptr; | |
156 fNeededUiSends = nullptr; | |
157 } | |
158 #else | |
159 // unused | |
160 (void)fWorker; | |
161 #endif | |
162 | |
163 #if ! DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST | |
164 // unused | |
165 (void)ctrlInPortChangeReq; | |
166 #endif | |
167 } | |
168 | |
169 ~PluginLv2() | |
170 { | |
171 if (fPortControls != nullptr) | |
172 { | |
173 delete[] fPortControls; | |
174 fPortControls = nullptr; | |
175 } | |
176 | |
177 if (fLastControlValues) | |
178 { | |
179 delete[] fLastControlValues; | |
180 fLastControlValues = nullptr; | |
181 } | |
182 | |
183 #if DISTRHO_PLUGIN_WANT_STATE | |
184 if (fNeededUiSends != nullptr) | |
185 { | |
186 delete[] fNeededUiSends; | |
187 fNeededUiSends = nullptr; | |
188 } | |
189 | |
190 if (fUrids != nullptr) | |
191 { | |
192 delete[] fUrids; | |
193 fUrids = nullptr; | |
194 } | |
195 | |
196 fStateMap.clear(); | |
197 #endif | |
198 } | |
199 | |
200 // ------------------------------------------------------------------- | |
201 | |
202 bool getPortControlValue(uint32_t index, float& value) const | |
203 { | |
204 if (const float* control = fPortControls[index]) | |
205 { | |
206 switch (fPlugin.getParameterDesignation(index)) | |
207 { | |
208 default: | |
209 value = *control; | |
210 break; | |
211 case kParameterDesignationBypass: | |
212 value = 1.0f - *control; | |
213 break; | |
214 } | |
215 | |
216 return true; | |
217 } | |
218 | |
219 return false; | |
220 } | |
221 | |
222 void setPortControlValue(uint32_t index, float value) | |
223 { | |
224 if (float* control = fPortControls[index]) | |
225 { | |
226 switch (fPlugin.getParameterDesignation(index)) | |
227 { | |
228 default: | |
229 *control = value; | |
230 break; | |
231 case kParameterDesignationBypass: | |
232 *control = 1.0f - value; | |
233 break; | |
234 } | |
235 } | |
236 } | |
237 | |
238 // ------------------------------------------------------------------- | |
239 | |
240 void lv2_activate() | |
241 { | |
242 #if DISTRHO_PLUGIN_WANT_TIMEPOS | |
243 fTimePosition.clear(); | |
244 | |
245 // hosts may not send all values, resulting on some invalid data, let's reset everything | |
246 fTimePosition.bbt.bar = 1; | |
247 fTimePosition.bbt.beat = 1; | |
248 fTimePosition.bbt.tick = 0.0; | |
249 fTimePosition.bbt.barStartTick = 0; | |
250 fTimePosition.bbt.beatsPerBar = 4; | |
251 fTimePosition.bbt.beatType = 4; | |
252 fTimePosition.bbt.ticksPerBeat = 1920.0; | |
253 fTimePosition.bbt.beatsPerMinute = 120.0; | |
254 #endif | |
255 fPlugin.activate(); | |
256 } | |
257 | |
258 void lv2_deactivate() | |
259 { | |
260 fPlugin.deactivate(); | |
261 } | |
262 | |
263 // ------------------------------------------------------------------- | |
264 | |
265 void lv2_connect_port(const uint32_t port, void* const dataLocation) | |
266 { | |
267 uint32_t index = 0; | |
268 | |
269 #if DISTRHO_PLUGIN_NUM_INPUTS > 0 | |
270 for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i) | |
271 { | |
272 if (port == index++) | |
273 { | |
274 fPortAudioIns[i] = (const float*)dataLocation; | |
275 return; | |
276 } | |
277 } | |
278 #endif | |
279 | |
280 #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |
281 for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) | |
282 { | |
283 if (port == index++) | |
284 { | |
285 fPortAudioOuts[i] = (float*)dataLocation; | |
286 return; | |
287 } | |
288 } | |
289 #endif | |
290 | |
291 #if DISTRHO_LV2_USE_EVENTS_IN | |
292 if (port == index++) | |
293 { | |
294 fPortEventsIn = (LV2_Atom_Sequence*)dataLocation; | |
295 return; | |
296 } | |
297 #endif | |
298 | |
299 #if DISTRHO_LV2_USE_EVENTS_OUT | |
300 if (port == index++) | |
301 { | |
302 fEventsOutData.port = (LV2_Atom_Sequence*)dataLocation; | |
303 return; | |
304 } | |
305 #endif | |
306 | |
307 #if DISTRHO_PLUGIN_WANT_LATENCY | |
308 if (port == index++) | |
309 { | |
310 fPortLatency = (float*)dataLocation; | |
311 return; | |
312 } | |
313 #endif | |
314 | |
315 for (uint32_t i=0, count=fPlugin.getParameterCount(); i < count; ++i) | |
316 { | |
317 if (port == index++) | |
318 { | |
319 fPortControls[i] = (float*)dataLocation; | |
320 return; | |
321 } | |
322 } | |
323 } | |
324 | |
325 // ------------------------------------------------------------------- | |
326 | |
327 void lv2_run(const uint32_t sampleCount) | |
328 { | |
329 // cache midi input and time position first | |
330 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |
331 uint32_t midiEventCount = 0; | |
332 #endif | |
333 | |
334 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_TIMEPOS | |
335 LV2_ATOM_SEQUENCE_FOREACH(fPortEventsIn, event) | |
336 { | |
337 if (event == nullptr) | |
338 break; | |
339 | |
340 # if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |
341 if (event->body.type == fURIDs.midiEvent) | |
342 { | |
343 if (midiEventCount >= kMaxMidiEvents) | |
344 continue; | |
345 | |
346 const uint8_t* const data((const uint8_t*)(event + 1)); | |
347 | |
348 MidiEvent& midiEvent(fMidiEvents[midiEventCount++]); | |
349 | |
350 midiEvent.frame = event->time.frames; | |
351 midiEvent.size = event->body.size; | |
352 | |
353 if (midiEvent.size > MidiEvent::kDataSize) | |
354 { | |
355 midiEvent.dataExt = data; | |
356 std::memset(midiEvent.data, 0, MidiEvent::kDataSize); | |
357 } | |
358 else | |
359 { | |
360 midiEvent.dataExt = nullptr; | |
361 std::memcpy(midiEvent.data, data, midiEvent.size); | |
362 } | |
363 | |
364 continue; | |
365 } | |
366 # endif | |
367 # if DISTRHO_PLUGIN_WANT_TIMEPOS | |
368 if (event->body.type == fURIDs.atomBlank || event->body.type == fURIDs.atomObject) | |
369 { | |
370 const LV2_Atom_Object* const obj((const LV2_Atom_Object*)&event->body); | |
371 | |
372 if (obj->body.otype != fURIDs.timePosition) | |
373 continue; | |
374 | |
375 LV2_Atom* bar = nullptr; | |
376 LV2_Atom* barBeat = nullptr; | |
377 LV2_Atom* beatUnit = nullptr; | |
378 LV2_Atom* beatsPerBar = nullptr; | |
379 LV2_Atom* beatsPerMinute = nullptr; | |
380 LV2_Atom* frame = nullptr; | |
381 LV2_Atom* speed = nullptr; | |
382 LV2_Atom* ticksPerBeat = nullptr; | |
383 | |
384 lv2_atom_object_get(obj, | |
385 fURIDs.timeBar, &bar, | |
386 fURIDs.timeBarBeat, &barBeat, | |
387 fURIDs.timeBeatUnit, &beatUnit, | |
388 fURIDs.timeBeatsPerBar, &beatsPerBar, | |
389 fURIDs.timeBeatsPerMinute, &beatsPerMinute, | |
390 fURIDs.timeFrame, &frame, | |
391 fURIDs.timeSpeed, &speed, | |
392 fURIDs.timeTicksPerBeat, &ticksPerBeat, | |
393 0); | |
394 | |
395 // need to handle this first as other values depend on it | |
396 if (ticksPerBeat != nullptr) | |
397 { | |
398 /**/ if (ticksPerBeat->type == fURIDs.atomDouble) | |
399 fLastPositionData.ticksPerBeat = ((LV2_Atom_Double*)ticksPerBeat)->body; | |
400 else if (ticksPerBeat->type == fURIDs.atomFloat) | |
401 fLastPositionData.ticksPerBeat = ((LV2_Atom_Float*)ticksPerBeat)->body; | |
402 else if (ticksPerBeat->type == fURIDs.atomInt) | |
403 fLastPositionData.ticksPerBeat = ((LV2_Atom_Int*)ticksPerBeat)->body; | |
404 else if (ticksPerBeat->type == fURIDs.atomLong) | |
405 fLastPositionData.ticksPerBeat = ((LV2_Atom_Long*)ticksPerBeat)->body; | |
406 else | |
407 d_stderr("Unknown lv2 ticksPerBeat value type"); | |
408 | |
409 if (fLastPositionData.ticksPerBeat > 0.0) | |
410 fTimePosition.bbt.ticksPerBeat = fLastPositionData.ticksPerBeat; | |
411 } | |
412 | |
413 // same | |
414 if (speed != nullptr) | |
415 { | |
416 /**/ if (speed->type == fURIDs.atomDouble) | |
417 fLastPositionData.speed = ((LV2_Atom_Double*)speed)->body; | |
418 else if (speed->type == fURIDs.atomFloat) | |
419 fLastPositionData.speed = ((LV2_Atom_Float*)speed)->body; | |
420 else if (speed->type == fURIDs.atomInt) | |
421 fLastPositionData.speed = ((LV2_Atom_Int*)speed)->body; | |
422 else if (speed->type == fURIDs.atomLong) | |
423 fLastPositionData.speed = ((LV2_Atom_Long*)speed)->body; | |
424 else | |
425 d_stderr("Unknown lv2 speed value type"); | |
426 | |
427 fTimePosition.playing = d_isNotZero(fLastPositionData.speed); | |
428 } | |
429 | |
430 if (bar != nullptr) | |
431 { | |
432 /**/ if (bar->type == fURIDs.atomDouble) | |
433 fLastPositionData.bar = ((LV2_Atom_Double*)bar)->body; | |
434 else if (bar->type == fURIDs.atomFloat) | |
435 fLastPositionData.bar = ((LV2_Atom_Float*)bar)->body; | |
436 else if (bar->type == fURIDs.atomInt) | |
437 fLastPositionData.bar = ((LV2_Atom_Int*)bar)->body; | |
438 else if (bar->type == fURIDs.atomLong) | |
439 fLastPositionData.bar = ((LV2_Atom_Long*)bar)->body; | |
440 else | |
441 d_stderr("Unknown lv2 bar value type"); | |
442 | |
443 if (fLastPositionData.bar >= 0) | |
444 fTimePosition.bbt.bar = fLastPositionData.bar + 1; | |
445 } | |
446 | |
447 if (barBeat != nullptr) | |
448 { | |
449 /**/ if (barBeat->type == fURIDs.atomDouble) | |
450 fLastPositionData.barBeat = ((LV2_Atom_Double*)barBeat)->body; | |
451 else if (barBeat->type == fURIDs.atomFloat) | |
452 fLastPositionData.barBeat = ((LV2_Atom_Float*)barBeat)->body; | |
453 else if (barBeat->type == fURIDs.atomInt) | |
454 fLastPositionData.barBeat = ((LV2_Atom_Int*)barBeat)->body; | |
455 else if (barBeat->type == fURIDs.atomLong) | |
456 fLastPositionData.barBeat = ((LV2_Atom_Long*)barBeat)->body; | |
457 else | |
458 d_stderr("Unknown lv2 barBeat value type"); | |
459 | |
460 if (fLastPositionData.barBeat >= 0.0f) | |
461 { | |
462 const double rest = std::fmod(fLastPositionData.barBeat, 1.0f); | |
463 fTimePosition.bbt.beat = std::round(fLastPositionData.barBeat - rest + 1.0); | |
464 fTimePosition.bbt.tick = rest * fTimePosition.bbt.ticksPerBeat; | |
465 } | |
466 } | |
467 | |
468 if (beatUnit != nullptr) | |
469 { | |
470 /**/ if (beatUnit->type == fURIDs.atomDouble) | |
471 fLastPositionData.beatUnit = ((LV2_Atom_Double*)beatUnit)->body; | |
472 else if (beatUnit->type == fURIDs.atomFloat) | |
473 fLastPositionData.beatUnit = ((LV2_Atom_Float*)beatUnit)->body; | |
474 else if (beatUnit->type == fURIDs.atomInt) | |
475 fLastPositionData.beatUnit = ((LV2_Atom_Int*)beatUnit)->body; | |
476 else if (beatUnit->type == fURIDs.atomLong) | |
477 fLastPositionData.beatUnit = ((LV2_Atom_Long*)beatUnit)->body; | |
478 else | |
479 d_stderr("Unknown lv2 beatUnit value type"); | |
480 | |
481 if (fLastPositionData.beatUnit > 0) | |
482 fTimePosition.bbt.beatType = fLastPositionData.beatUnit; | |
483 } | |
484 | |
485 if (beatsPerBar != nullptr) | |
486 { | |
487 /**/ if (beatsPerBar->type == fURIDs.atomDouble) | |
488 fLastPositionData.beatsPerBar = ((LV2_Atom_Double*)beatsPerBar)->body; | |
489 else if (beatsPerBar->type == fURIDs.atomFloat) | |
490 fLastPositionData.beatsPerBar = ((LV2_Atom_Float*)beatsPerBar)->body; | |
491 else if (beatsPerBar->type == fURIDs.atomInt) | |
492 fLastPositionData.beatsPerBar = ((LV2_Atom_Int*)beatsPerBar)->body; | |
493 else if (beatsPerBar->type == fURIDs.atomLong) | |
494 fLastPositionData.beatsPerBar = ((LV2_Atom_Long*)beatsPerBar)->body; | |
495 else | |
496 d_stderr("Unknown lv2 beatsPerBar value type"); | |
497 | |
498 if (fLastPositionData.beatsPerBar > 0.0f) | |
499 fTimePosition.bbt.beatsPerBar = fLastPositionData.beatsPerBar; | |
500 } | |
501 | |
502 if (beatsPerMinute != nullptr) | |
503 { | |
504 /**/ if (beatsPerMinute->type == fURIDs.atomDouble) | |
505 fLastPositionData.beatsPerMinute = ((LV2_Atom_Double*)beatsPerMinute)->body; | |
506 else if (beatsPerMinute->type == fURIDs.atomFloat) | |
507 fLastPositionData.beatsPerMinute = ((LV2_Atom_Float*)beatsPerMinute)->body; | |
508 else if (beatsPerMinute->type == fURIDs.atomInt) | |
509 fLastPositionData.beatsPerMinute = ((LV2_Atom_Int*)beatsPerMinute)->body; | |
510 else if (beatsPerMinute->type == fURIDs.atomLong) | |
511 fLastPositionData.beatsPerMinute = ((LV2_Atom_Long*)beatsPerMinute)->body; | |
512 else | |
513 d_stderr("Unknown lv2 beatsPerMinute value type"); | |
514 | |
515 if (fLastPositionData.beatsPerMinute > 0.0f) | |
516 { | |
517 fTimePosition.bbt.beatsPerMinute = fLastPositionData.beatsPerMinute; | |
518 | |
519 if (d_isNotZero(fLastPositionData.speed)) | |
520 fTimePosition.bbt.beatsPerMinute *= std::abs(fLastPositionData.speed); | |
521 } | |
522 } | |
523 | |
524 if (frame != nullptr) | |
525 { | |
526 /**/ if (frame->type == fURIDs.atomDouble) | |
527 fLastPositionData.frame = ((LV2_Atom_Double*)frame)->body; | |
528 else if (frame->type == fURIDs.atomFloat) | |
529 fLastPositionData.frame = ((LV2_Atom_Float*)frame)->body; | |
530 else if (frame->type == fURIDs.atomInt) | |
531 fLastPositionData.frame = ((LV2_Atom_Int*)frame)->body; | |
532 else if (frame->type == fURIDs.atomLong) | |
533 fLastPositionData.frame = ((LV2_Atom_Long*)frame)->body; | |
534 else | |
535 d_stderr("Unknown lv2 frame value type"); | |
536 | |
537 if (fLastPositionData.frame >= 0) | |
538 fTimePosition.frame = fLastPositionData.frame; | |
539 } | |
540 | |
541 fTimePosition.bbt.barStartTick = fTimePosition.bbt.ticksPerBeat* | |
542 fTimePosition.bbt.beatsPerBar* | |
543 (fTimePosition.bbt.bar-1); | |
544 | |
545 fTimePosition.bbt.valid = (fLastPositionData.beatsPerMinute > 0.0 && | |
546 fLastPositionData.beatUnit > 0 && | |
547 fLastPositionData.beatsPerBar > 0.0f); | |
548 | |
549 fPlugin.setTimePosition(fTimePosition); | |
550 | |
551 continue; | |
552 } | |
553 # endif | |
554 } | |
555 #endif | |
556 | |
557 // check for messages from UI or host | |
558 #if DISTRHO_PLUGIN_WANT_STATE | |
559 LV2_ATOM_SEQUENCE_FOREACH(fPortEventsIn, event) | |
560 { | |
561 if (event == nullptr) | |
562 break; | |
563 | |
564 #if DISTRHO_PLUGIN_HAS_UI | |
565 if (event->body.type == fURIDs.dpfKeyValue) | |
566 { | |
567 const void* const data = (const void*)(event + 1); | |
568 | |
569 // check if this is our special message | |
570 if (std::strcmp((const char*)data, "__dpf_ui_data__") == 0) | |
571 { | |
572 for (uint32_t i=0, count=fPlugin.getStateCount(); i < count; ++i) | |
573 { | |
574 if (fPlugin.getStateHints(i) & kStateIsOnlyForDSP) | |
575 continue; | |
576 fNeededUiSends[i] = true; | |
577 } | |
578 } | |
579 // no, send to DSP as usual | |
580 else if (fWorker != nullptr) | |
581 { | |
582 fWorker->schedule_work(fWorker->handle, sizeof(LV2_Atom)+event->body.size, &event->body); | |
583 } | |
584 } | |
585 else | |
586 #endif | |
587 if (event->body.type == fURIDs.atomObject && fWorker != nullptr) | |
588 { | |
589 const LV2_Atom_Object* const object = (const LV2_Atom_Object*)&event->body; | |
590 | |
591 const LV2_Atom* property = nullptr; | |
592 const LV2_Atom* value = nullptr; | |
593 lv2_atom_object_get(object, fURIDs.patchProperty, &property, fURIDs.patchValue, &value, nullptr); | |
594 | |
595 if (property != nullptr && property->type == fURIDs.atomURID && | |
596 value != nullptr && (value->type == fURIDs.atomPath || value->type == fURIDs.atomString)) | |
597 { | |
598 fWorker->schedule_work(fWorker->handle, sizeof(LV2_Atom)+event->body.size, &event->body); | |
599 } | |
600 } | |
601 } | |
602 #endif | |
603 | |
604 // Check for updated parameters | |
605 float curValue; | |
606 | |
607 for (uint32_t i=0, count=fPlugin.getParameterCount(); i < count; ++i) | |
608 { | |
609 if (!getPortControlValue(i, curValue)) | |
610 continue; | |
611 | |
612 if (fPlugin.isParameterInput(i) && d_isNotEqual(fLastControlValues[i], curValue)) | |
613 { | |
614 fLastControlValues[i] = curValue; | |
615 | |
616 fPlugin.setParameterValue(i, curValue); | |
617 } | |
618 } | |
619 | |
620 // Run plugin | |
621 if (sampleCount != 0) | |
622 { | |
623 #ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD | |
624 fRunCount = mod_license_run_begin(fRunCount, sampleCount); | |
625 #endif | |
626 | |
627 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |
628 fPlugin.run(fPortAudioIns, fPortAudioOuts, sampleCount, fMidiEvents, midiEventCount); | |
629 #else | |
630 fPlugin.run(fPortAudioIns, fPortAudioOuts, sampleCount); | |
631 #endif | |
632 | |
633 #ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD | |
634 for (uint32_t i=0; i<DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) | |
635 mod_license_run_silence(fRunCount, fPortAudioOuts[i], sampleCount, i); | |
636 #endif | |
637 | |
638 #if DISTRHO_PLUGIN_WANT_TIMEPOS | |
639 // update timePos for next callback | |
640 if (d_isNotZero(fLastPositionData.speed)) | |
641 { | |
642 if (fLastPositionData.speed > 0.0) | |
643 { | |
644 // playing forwards | |
645 fLastPositionData.frame += sampleCount; | |
646 } | |
647 else | |
648 { | |
649 // playing backwards | |
650 fLastPositionData.frame -= sampleCount; | |
651 | |
652 if (fLastPositionData.frame < 0) | |
653 fLastPositionData.frame = 0; | |
654 } | |
655 | |
656 fTimePosition.frame = fLastPositionData.frame; | |
657 | |
658 if (fTimePosition.bbt.valid) | |
659 { | |
660 const double beatsPerMinute = fLastPositionData.beatsPerMinute * fLastPositionData.speed; | |
661 const double framesPerBeat = 60.0 * fSampleRate / beatsPerMinute; | |
662 const double addedBarBeats = double(sampleCount) / framesPerBeat; | |
663 | |
664 if (fLastPositionData.barBeat >= 0.0f) | |
665 { | |
666 fLastPositionData.barBeat = std::fmod(fLastPositionData.barBeat+addedBarBeats, | |
667 (double)fLastPositionData.beatsPerBar); | |
668 | |
669 const double rest = std::fmod(fLastPositionData.barBeat, 1.0f); | |
670 fTimePosition.bbt.beat = std::round(fLastPositionData.barBeat - rest + 1.0); | |
671 fTimePosition.bbt.tick = rest * fTimePosition.bbt.ticksPerBeat; | |
672 | |
673 if (fLastPositionData.bar >= 0) | |
674 { | |
675 fLastPositionData.bar += std::floor((fLastPositionData.barBeat+addedBarBeats)/ | |
676 fLastPositionData.beatsPerBar); | |
677 | |
678 if (fLastPositionData.bar < 0) | |
679 fLastPositionData.bar = 0; | |
680 | |
681 fTimePosition.bbt.bar = fLastPositionData.bar + 1; | |
682 | |
683 fTimePosition.bbt.barStartTick = fTimePosition.bbt.ticksPerBeat* | |
684 fTimePosition.bbt.beatsPerBar* | |
685 (fTimePosition.bbt.bar-1); | |
686 } | |
687 } | |
688 | |
689 fTimePosition.bbt.beatsPerMinute = std::abs(beatsPerMinute); | |
690 } | |
691 | |
692 fPlugin.setTimePosition(fTimePosition); | |
693 } | |
694 #endif | |
695 } | |
696 | |
697 updateParameterOutputsAndTriggers(); | |
698 | |
699 #if DISTRHO_PLUGIN_WANT_STATE | |
700 fEventsOutData.initIfNeeded(fURIDs.atomSequence); | |
701 | |
702 LV2_Atom_Event* aev; | |
703 const uint32_t capacity = fEventsOutData.capacity; | |
704 | |
705 for (uint32_t i=0, count=fPlugin.getStateCount(); i < count; ++i) | |
706 { | |
707 if (! fNeededUiSends[i]) | |
708 continue; | |
709 | |
710 const uint32_t hints = fPlugin.getStateHints(i); | |
711 | |
712 #if ! DISTRHO_PLUGIN_HAS_UI | |
713 if ((hints & kStateIsHostReadable) == 0x0) | |
714 { | |
715 fNeededUiSends[i] = false; | |
716 continue; | |
717 } | |
718 #endif | |
719 | |
720 const String& curKey(fPlugin.getStateKey(i)); | |
721 | |
722 for (StringToStringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) | |
723 { | |
724 const String& key(cit->first); | |
725 | |
726 if (curKey != key) | |
727 continue; | |
728 | |
729 const String& value(cit->second); | |
730 | |
731 // set msg size | |
732 uint32_t msgSize; | |
733 | |
734 if (hints & kStateIsHostReadable) | |
735 { | |
736 // object, prop key, prop urid, value key, value | |
737 msgSize = sizeof(LV2_Atom_Object) | |
738 + sizeof(LV2_Atom_Property_Body) * 4 | |
739 + sizeof(LV2_Atom_URID) * 3 | |
740 + sizeof(LV2_Atom_String) | |
741 + value.length() + 1; | |
742 } | |
743 else | |
744 { | |
745 // key + value + 2x null terminator + separator | |
746 msgSize = static_cast<uint32_t>(key.length()+value.length())+3U; | |
747 } | |
748 | |
749 if (sizeof(LV2_Atom_Event) + msgSize > capacity - fEventsOutData.offset) | |
750 { | |
751 d_stdout("Sending key '%s' to UI failed, out of space (needs %u bytes)", | |
752 key.buffer(), msgSize); | |
753 break; | |
754 } | |
755 | |
756 // put data | |
757 aev = (LV2_Atom_Event*)(LV2_ATOM_CONTENTS(LV2_Atom_Sequence, fEventsOutData.port) + fEventsOutData.offset); | |
758 aev->time.frames = 0; | |
759 | |
760 if (hints & kStateIsHostReadable) | |
761 { | |
762 uint8_t* const msgBuf = (uint8_t*)&aev->body; | |
763 LV2_Atom_Forge atomForge = fAtomForge; | |
764 lv2_atom_forge_set_buffer(&atomForge, msgBuf, msgSize); | |
765 | |
766 LV2_Atom_Forge_Frame forgeFrame; | |
767 lv2_atom_forge_object(&atomForge, &forgeFrame, 0, fURIDs.patchSet); | |
768 | |
769 lv2_atom_forge_key(&atomForge, fURIDs.patchProperty); | |
770 lv2_atom_forge_urid(&atomForge, fUrids[i]); | |
771 | |
772 lv2_atom_forge_key(&atomForge, fURIDs.patchValue); | |
773 if ((hints & kStateIsFilenamePath) == kStateIsFilenamePath) | |
774 lv2_atom_forge_path(&atomForge, value.buffer(), static_cast<uint32_t>(value.length()+1)); | |
775 else | |
776 lv2_atom_forge_string(&atomForge, value.buffer(), static_cast<uint32_t>(value.length()+1)); | |
777 | |
778 lv2_atom_forge_pop(&atomForge, &forgeFrame); | |
779 | |
780 msgSize = ((LV2_Atom*)msgBuf)->size; | |
781 } | |
782 else | |
783 { | |
784 aev->body.type = fURIDs.dpfKeyValue; | |
785 aev->body.size = msgSize; | |
786 | |
787 uint8_t* const msgBuf = LV2_ATOM_BODY(&aev->body); | |
788 std::memset(msgBuf, 0, msgSize); | |
789 | |
790 // write key and value in atom buffer | |
791 std::memcpy(msgBuf, key.buffer(), key.length()+1); | |
792 std::memcpy(msgBuf+(key.length()+1), value.buffer(), value.length()+1); | |
793 } | |
794 | |
795 fEventsOutData.growBy(lv2_atom_pad_size(sizeof(LV2_Atom_Event) + msgSize)); | |
796 fNeededUiSends[i] = false; | |
797 break; | |
798 } | |
799 } | |
800 #endif | |
801 | |
802 #if DISTRHO_LV2_USE_EVENTS_OUT | |
803 fEventsOutData.endRun(); | |
804 #endif | |
805 } | |
806 | |
807 // ------------------------------------------------------------------- | |
808 | |
809 uint32_t lv2_get_options(LV2_Options_Option* const /*options*/) | |
810 { | |
811 // currently unused | |
812 return LV2_OPTIONS_ERR_UNKNOWN; | |
813 } | |
814 | |
815 uint32_t lv2_set_options(const LV2_Options_Option* const options) | |
816 { | |
817 for (int i=0; options[i].key != 0; ++i) | |
818 { | |
819 if (options[i].key == fUridMap->map(fUridMap->handle, LV2_BUF_SIZE__nominalBlockLength)) | |
820 { | |
821 if (options[i].type == fURIDs.atomInt) | |
822 { | |
823 const int32_t bufferSize(*(const int32_t*)options[i].value); | |
824 fPlugin.setBufferSize(bufferSize, true); | |
825 } | |
826 else | |
827 { | |
828 d_stderr("Host changed nominalBlockLength but with wrong value type"); | |
829 } | |
830 } | |
831 else if (options[i].key == fUridMap->map(fUridMap->handle, LV2_BUF_SIZE__maxBlockLength) && ! fUsingNominal) | |
832 { | |
833 if (options[i].type == fURIDs.atomInt) | |
834 { | |
835 const int32_t bufferSize(*(const int32_t*)options[i].value); | |
836 fPlugin.setBufferSize(bufferSize, true); | |
837 } | |
838 else | |
839 { | |
840 d_stderr("Host changed maxBlockLength but with wrong value type"); | |
841 } | |
842 } | |
843 else if (options[i].key == fUridMap->map(fUridMap->handle, LV2_PARAMETERS__sampleRate)) | |
844 { | |
845 if (options[i].type == fURIDs.atomFloat) | |
846 { | |
847 const float sampleRate(*(const float*)options[i].value); | |
848 fSampleRate = sampleRate; | |
849 fPlugin.setSampleRate(sampleRate, true); | |
850 } | |
851 else | |
852 { | |
853 d_stderr("Host changed sampleRate but with wrong value type"); | |
854 } | |
855 } | |
856 } | |
857 | |
858 return LV2_OPTIONS_SUCCESS; | |
859 } | |
860 | |
861 // ------------------------------------------------------------------- | |
862 | |
863 #if DISTRHO_PLUGIN_WANT_PROGRAMS | |
864 const LV2_Program_Descriptor* lv2_get_program(const uint32_t index) | |
865 { | |
866 if (index >= fPlugin.getProgramCount()) | |
867 return nullptr; | |
868 | |
869 static LV2_Program_Descriptor desc; | |
870 | |
871 desc.bank = index / 128; | |
872 desc.program = index % 128; | |
873 desc.name = fPlugin.getProgramName(index); | |
874 | |
875 return &desc; | |
876 } | |
877 | |
878 void lv2_select_program(const uint32_t bank, const uint32_t program) | |
879 { | |
880 const uint32_t realProgram(bank * 128 + program); | |
881 | |
882 if (realProgram >= fPlugin.getProgramCount()) | |
883 return; | |
884 | |
885 fPlugin.loadProgram(realProgram); | |
886 | |
887 // Update control inputs | |
888 for (uint32_t i=0, count=fPlugin.getParameterCount(); i < count; ++i) | |
889 { | |
890 if (fPlugin.isParameterOutput(i)) | |
891 continue; | |
892 | |
893 fLastControlValues[i] = fPlugin.getParameterValue(i); | |
894 | |
895 setPortControlValue(i, fLastControlValues[i]); | |
896 } | |
897 | |
898 #if DISTRHO_PLUGIN_WANT_FULL_STATE | |
899 // Update state | |
900 for (StringToStringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) | |
901 { | |
902 const String& key = cit->first; | |
903 fStateMap[key] = fPlugin.getStateValue(key); | |
904 } | |
905 #endif | |
906 } | |
907 #endif | |
908 | |
909 // ------------------------------------------------------------------- | |
910 | |
911 #if DISTRHO_PLUGIN_WANT_STATE | |
912 LV2_State_Status lv2_save(const LV2_State_Store_Function store, const LV2_State_Handle handle) | |
913 { | |
914 #if DISTRHO_PLUGIN_WANT_FULL_STATE | |
915 // Update current state | |
916 for (StringToStringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) | |
917 { | |
918 const String& key = cit->first; | |
919 fStateMap[key] = fPlugin.getStateValue(key); | |
920 } | |
921 #endif | |
922 | |
923 String lv2key; | |
924 LV2_URID urid; | |
925 | |
926 for (uint32_t i=0, count=fPlugin.getStateCount(); i < count; ++i) | |
927 { | |
928 const String& curKey(fPlugin.getStateKey(i)); | |
929 | |
930 for (StringToStringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) | |
931 { | |
932 const String& key(cit->first); | |
933 | |
934 if (curKey != key) | |
935 continue; | |
936 | |
937 const uint32_t hints = fPlugin.getStateHints(i); | |
938 | |
939 #if ! DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |
940 // do not save UI-only messages if there is no UI available | |
941 if (hints & kStateIsOnlyForUI) | |
942 break; | |
943 #endif | |
944 | |
945 if (hints & kStateIsHostReadable) | |
946 { | |
947 lv2key = DISTRHO_PLUGIN_URI "#"; | |
948 urid = (hints & kStateIsFilenamePath) == kStateIsFilenamePath | |
949 ? fURIDs.atomPath | |
950 : fURIDs.atomString; | |
951 } | |
952 else | |
953 { | |
954 lv2key = DISTRHO_PLUGIN_LV2_STATE_PREFIX; | |
955 urid = fURIDs.atomString; | |
956 } | |
957 | |
958 lv2key += key; | |
959 | |
960 const String& value(cit->second); | |
961 | |
962 // some hosts need +1 for the null terminator, even though the type is string | |
963 store(handle, | |
964 fUridMap->map(fUridMap->handle, lv2key.buffer()), | |
965 value.buffer(), | |
966 value.length()+1, | |
967 urid, | |
968 LV2_STATE_IS_POD|LV2_STATE_IS_PORTABLE); | |
969 | |
970 break; | |
971 } | |
972 } | |
973 | |
974 return LV2_STATE_SUCCESS; | |
975 } | |
976 | |
977 LV2_State_Status lv2_restore(const LV2_State_Retrieve_Function retrieve, const LV2_State_Handle handle) | |
978 { | |
979 size_t size; | |
980 uint32_t type, flags; | |
981 | |
982 String lv2key; | |
983 LV2_URID urid; | |
984 | |
985 for (uint32_t i=0, count=fPlugin.getStateCount(); i < count; ++i) | |
986 { | |
987 const String& key(fPlugin.getStateKey(i)); | |
988 | |
989 const uint32_t hints = fPlugin.getStateHints(i); | |
990 | |
991 if (hints & kStateIsHostReadable) | |
992 { | |
993 lv2key = DISTRHO_PLUGIN_URI "#"; | |
994 urid = (hints & kStateIsFilenamePath) == kStateIsFilenamePath | |
995 ? fURIDs.atomPath | |
996 : fURIDs.atomString; | |
997 } | |
998 else | |
999 { | |
1000 lv2key = DISTRHO_PLUGIN_LV2_STATE_PREFIX; | |
1001 urid = fURIDs.atomString; | |
1002 } | |
1003 | |
1004 lv2key += key; | |
1005 | |
1006 size = 0; | |
1007 type = 0; | |
1008 flags = LV2_STATE_IS_POD|LV2_STATE_IS_PORTABLE; | |
1009 const void* data = retrieve(handle, | |
1010 fUridMap->map(fUridMap->handle, lv2key.buffer()), | |
1011 &size, &type, &flags); | |
1012 | |
1013 if (data == nullptr || size == 0) | |
1014 continue; | |
1015 | |
1016 DISTRHO_SAFE_ASSERT_CONTINUE(type == urid); | |
1017 | |
1018 const char* const value = (const char*)data; | |
1019 const std::size_t length = std::strlen(value); | |
1020 DISTRHO_SAFE_ASSERT_CONTINUE(length == size || length+1 == size); | |
1021 | |
1022 setState(key, value); | |
1023 | |
1024 #if DISTRHO_PLUGIN_WANT_STATE | |
1025 // signal msg needed for UI | |
1026 if ((hints & kStateIsOnlyForDSP) == 0x0) | |
1027 fNeededUiSends[i] = true; | |
1028 #endif | |
1029 } | |
1030 | |
1031 return LV2_STATE_SUCCESS; | |
1032 } | |
1033 | |
1034 // ------------------------------------------------------------------- | |
1035 | |
1036 LV2_Worker_Status lv2_work(const void* const data) | |
1037 { | |
1038 const LV2_Atom* const eventBody = (const LV2_Atom*)data; | |
1039 | |
1040 if (eventBody->type == fURIDs.dpfKeyValue) | |
1041 { | |
1042 const char* const key = (const char*)(eventBody + 1); | |
1043 const char* const value = key + (std::strlen(key) + 1U); | |
1044 | |
1045 setState(key, value); | |
1046 return LV2_WORKER_SUCCESS; | |
1047 } | |
1048 | |
1049 if (eventBody->type == fURIDs.atomObject) | |
1050 { | |
1051 const LV2_Atom_Object* const object = (const LV2_Atom_Object*)eventBody; | |
1052 | |
1053 const LV2_Atom* property = nullptr; | |
1054 const LV2_Atom* value = nullptr; | |
1055 lv2_atom_object_get(object, fURIDs.patchProperty, &property, fURIDs.patchValue, &value, nullptr); | |
1056 DISTRHO_SAFE_ASSERT_RETURN(property != nullptr, LV2_WORKER_ERR_UNKNOWN); | |
1057 DISTRHO_SAFE_ASSERT_RETURN(property->type == fURIDs.atomURID, LV2_WORKER_ERR_UNKNOWN); | |
1058 DISTRHO_SAFE_ASSERT_RETURN(value != nullptr, LV2_WORKER_ERR_UNKNOWN); | |
1059 DISTRHO_SAFE_ASSERT_RETURN(value->type == fURIDs.atomPath || | |
1060 value->type == fURIDs.atomString, LV2_WORKER_ERR_UNKNOWN); | |
1061 | |
1062 const LV2_URID urid = ((const LV2_Atom_URID*)property)->body; | |
1063 const char* const filename = (const char*)(value + 1); | |
1064 | |
1065 String key; | |
1066 | |
1067 try { | |
1068 key = fUridStateMap[urid]; | |
1069 } DISTRHO_SAFE_EXCEPTION_RETURN("lv2_work fUridStateMap[urid]", LV2_WORKER_ERR_UNKNOWN); | |
1070 | |
1071 setState(key, filename); | |
1072 | |
1073 /* FIXME host should be responsible for updating UI side, not us | |
1074 for (uint32_t i=0, count=fPlugin.getStateCount(); i < count; ++i) | |
1075 { | |
1076 if (fPlugin.getStateKey(i) == key) | |
1077 { | |
1078 if ((fPlugin.getStateHints(i) & kStateIsOnlyForDSP) == 0x0) | |
1079 fNeededUiSends[i] = true; | |
1080 break; | |
1081 } | |
1082 } | |
1083 */ | |
1084 | |
1085 return LV2_WORKER_SUCCESS; | |
1086 } | |
1087 | |
1088 return LV2_WORKER_ERR_UNKNOWN; | |
1089 } | |
1090 | |
1091 LV2_Worker_Status lv2_work_response(uint32_t, const void*) | |
1092 { | |
1093 return LV2_WORKER_SUCCESS; | |
1094 } | |
1095 #endif | |
1096 | |
1097 // ------------------------------------------------------------------- | |
1098 | |
1099 #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |
1100 void* lv2_get_instance_pointer() | |
1101 { | |
1102 return fPlugin.getInstancePointer(); | |
1103 } | |
1104 #endif | |
1105 | |
1106 // ------------------------------------------------------------------- | |
1107 | |
1108 private: | |
1109 PluginExporter fPlugin; | |
1110 const bool fUsingNominal; // if false use maxBlockLength | |
1111 | |
1112 #ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD | |
1113 uint32_t fRunCount; | |
1114 #endif | |
1115 | |
1116 // LV2 ports | |
1117 #if DISTRHO_PLUGIN_NUM_INPUTS > 0 | |
1118 const float* fPortAudioIns[DISTRHO_PLUGIN_NUM_INPUTS]; | |
1119 #else | |
1120 const float** fPortAudioIns; | |
1121 #endif | |
1122 #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |
1123 float* fPortAudioOuts[DISTRHO_PLUGIN_NUM_OUTPUTS]; | |
1124 #else | |
1125 float** fPortAudioOuts; | |
1126 #endif | |
1127 float** fPortControls; | |
1128 #if DISTRHO_LV2_USE_EVENTS_IN | |
1129 LV2_Atom_Sequence* fPortEventsIn; | |
1130 #endif | |
1131 #if DISTRHO_PLUGIN_WANT_LATENCY | |
1132 float* fPortLatency; | |
1133 #endif | |
1134 | |
1135 // Temporary data | |
1136 float* fLastControlValues; | |
1137 double fSampleRate; | |
1138 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |
1139 MidiEvent fMidiEvents[kMaxMidiEvents]; | |
1140 #endif | |
1141 #if DISTRHO_PLUGIN_WANT_TIMEPOS | |
1142 TimePosition fTimePosition; | |
1143 | |
1144 struct Lv2PositionData { | |
1145 int64_t bar; | |
1146 float barBeat; | |
1147 uint32_t beatUnit; | |
1148 float beatsPerBar; | |
1149 float beatsPerMinute; | |
1150 int64_t frame; | |
1151 double speed; | |
1152 double ticksPerBeat; | |
1153 | |
1154 Lv2PositionData() | |
1155 : bar(-1), | |
1156 barBeat(-1.0f), | |
1157 beatUnit(0), | |
1158 beatsPerBar(0.0f), | |
1159 beatsPerMinute(0.0f), | |
1160 frame(-1), | |
1161 speed(0.0), | |
1162 ticksPerBeat(-1.0) {} | |
1163 | |
1164 } fLastPositionData; | |
1165 #endif | |
1166 | |
1167 #if DISTRHO_LV2_USE_EVENTS_OUT | |
1168 struct Lv2EventsOutData { | |
1169 uint32_t capacity, offset; | |
1170 LV2_Atom_Sequence* port; | |
1171 | |
1172 Lv2EventsOutData() | |
1173 : capacity(0), | |
1174 offset(0), | |
1175 port(nullptr) {} | |
1176 | |
1177 void initIfNeeded(const LV2_URID uridAtomSequence) | |
1178 { | |
1179 if (capacity != 0) | |
1180 return; | |
1181 | |
1182 capacity = port->atom.size; | |
1183 | |
1184 port->atom.size = sizeof(LV2_Atom_Sequence_Body); | |
1185 port->atom.type = uridAtomSequence; | |
1186 port->body.unit = 0; | |
1187 port->body.pad = 0; | |
1188 } | |
1189 | |
1190 void growBy(const uint32_t size) | |
1191 { | |
1192 offset += size; | |
1193 port->atom.size += size; | |
1194 } | |
1195 | |
1196 void endRun() | |
1197 { | |
1198 capacity = 0; | |
1199 offset = 0; | |
1200 } | |
1201 | |
1202 } fEventsOutData; | |
1203 #endif | |
1204 | |
1205 // LV2 URIDs | |
1206 struct URIDs { | |
1207 const LV2_URID_Map* _uridMap; | |
1208 LV2_URID atomBlank; | |
1209 LV2_URID atomObject; | |
1210 LV2_URID atomDouble; | |
1211 LV2_URID atomFloat; | |
1212 LV2_URID atomInt; | |
1213 LV2_URID atomLong; | |
1214 LV2_URID atomPath; | |
1215 LV2_URID atomSequence; | |
1216 LV2_URID atomString; | |
1217 LV2_URID atomURID; | |
1218 LV2_URID dpfKeyValue; | |
1219 LV2_URID midiEvent; | |
1220 LV2_URID patchSet; | |
1221 LV2_URID patchProperty; | |
1222 LV2_URID patchValue; | |
1223 LV2_URID timePosition; | |
1224 LV2_URID timeBar; | |
1225 LV2_URID timeBarBeat; | |
1226 LV2_URID timeBeatUnit; | |
1227 LV2_URID timeBeatsPerBar; | |
1228 LV2_URID timeBeatsPerMinute; | |
1229 LV2_URID timeTicksPerBeat; | |
1230 LV2_URID timeFrame; | |
1231 LV2_URID timeSpeed; | |
1232 | |
1233 URIDs(const LV2_URID_Map* const uridMap) | |
1234 : _uridMap(uridMap), | |
1235 atomBlank(map(LV2_ATOM__Blank)), | |
1236 atomObject(map(LV2_ATOM__Object)), | |
1237 atomDouble(map(LV2_ATOM__Double)), | |
1238 atomFloat(map(LV2_ATOM__Float)), | |
1239 atomInt(map(LV2_ATOM__Int)), | |
1240 atomLong(map(LV2_ATOM__Long)), | |
1241 atomPath(map(LV2_ATOM__Path)), | |
1242 atomSequence(map(LV2_ATOM__Sequence)), | |
1243 atomString(map(LV2_ATOM__String)), | |
1244 atomURID(map(LV2_ATOM__URID)), | |
1245 dpfKeyValue(map(DISTRHO_PLUGIN_LV2_STATE_PREFIX "KeyValueState")), | |
1246 midiEvent(map(LV2_MIDI__MidiEvent)), | |
1247 patchSet(map(LV2_PATCH__Set)), | |
1248 patchProperty(map(LV2_PATCH__property)), | |
1249 patchValue(map(LV2_PATCH__value)), | |
1250 timePosition(map(LV2_TIME__Position)), | |
1251 timeBar(map(LV2_TIME__bar)), | |
1252 timeBarBeat(map(LV2_TIME__barBeat)), | |
1253 timeBeatUnit(map(LV2_TIME__beatUnit)), | |
1254 timeBeatsPerBar(map(LV2_TIME__beatsPerBar)), | |
1255 timeBeatsPerMinute(map(LV2_TIME__beatsPerMinute)), | |
1256 timeTicksPerBeat(map(LV2_KXSTUDIO_PROPERTIES__TimePositionTicksPerBeat)), | |
1257 timeFrame(map(LV2_TIME__frame)), | |
1258 timeSpeed(map(LV2_TIME__speed)) {} | |
1259 | |
1260 inline LV2_URID map(const char* const uri) const | |
1261 { | |
1262 return _uridMap->map(_uridMap->handle, uri); | |
1263 } | |
1264 } fURIDs; | |
1265 | |
1266 // LV2 features | |
1267 #if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST | |
1268 const LV2_ControlInputPort_Change_Request* const fCtrlInPortChangeReq; | |
1269 #endif | |
1270 const LV2_URID_Map* const fUridMap; | |
1271 const LV2_Worker_Schedule* const fWorker; | |
1272 | |
1273 #if DISTRHO_PLUGIN_WANT_STATE | |
1274 LV2_Atom_Forge fAtomForge; | |
1275 StringToStringMap fStateMap; | |
1276 UridToStringMap fUridStateMap; | |
1277 LV2_URID* fUrids; | |
1278 bool* fNeededUiSends; | |
1279 | |
1280 void setState(const char* const key, const char* const newValue) | |
1281 { | |
1282 fPlugin.setState(key, newValue); | |
1283 | |
1284 // save this key if necessary | |
1285 if (fPlugin.wantStateKey(key)) | |
1286 updateInternalState(key, newValue, false); | |
1287 } | |
1288 | |
1289 bool updateState(const char* const key, const char* const newValue) | |
1290 { | |
1291 fPlugin.setState(key, newValue); | |
1292 return updateInternalState(key, newValue, true); | |
1293 } | |
1294 | |
1295 bool updateInternalState(const char* const key, const char* const newValue, const bool sendToUI) | |
1296 { | |
1297 // key must already exist | |
1298 for (StringToStringMap::iterator it=fStateMap.begin(), ite=fStateMap.end(); it != ite; ++it) | |
1299 { | |
1300 const String& dkey(it->first); | |
1301 | |
1302 if (dkey == key) | |
1303 { | |
1304 it->second = newValue; | |
1305 | |
1306 if (sendToUI) | |
1307 { | |
1308 for (uint32_t i=0, count=fPlugin.getStateCount(); i < count; ++i) | |
1309 { | |
1310 if (fPlugin.getStateKey(i) == key) | |
1311 { | |
1312 if ((fPlugin.getStateHints(i) & kStateIsOnlyForDSP) == 0x0) | |
1313 fNeededUiSends[i] = true; | |
1314 break; | |
1315 } | |
1316 } | |
1317 } | |
1318 | |
1319 return true; | |
1320 } | |
1321 } | |
1322 | |
1323 d_stderr("Failed to find plugin state with key \"%s\"", key); | |
1324 return false; | |
1325 } | |
1326 #endif | |
1327 | |
1328 void updateParameterOutputsAndTriggers() | |
1329 { | |
1330 float curValue; | |
1331 | |
1332 for (uint32_t i=0, count=fPlugin.getParameterCount(); i < count; ++i) | |
1333 { | |
1334 if (fPlugin.isParameterOutput(i)) | |
1335 { | |
1336 curValue = fLastControlValues[i] = fPlugin.getParameterValue(i); | |
1337 | |
1338 setPortControlValue(i, curValue); | |
1339 } | |
1340 else if ((fPlugin.getParameterHints(i) & kParameterIsTrigger) == kParameterIsTrigger) | |
1341 { | |
1342 // NOTE: host is responsible for auto-updating control port buffers | |
1343 } | |
1344 } | |
1345 | |
1346 #if DISTRHO_PLUGIN_WANT_LATENCY | |
1347 if (fPortLatency != nullptr) | |
1348 *fPortLatency = fPlugin.getLatency(); | |
1349 #endif | |
1350 } | |
1351 | |
1352 #if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST | |
1353 bool requestParameterValueChange(const uint32_t index, const float value) | |
1354 { | |
1355 if (fCtrlInPortChangeReq == nullptr) | |
1356 return false; | |
1357 return fCtrlInPortChangeReq->request_change(fCtrlInPortChangeReq->handle, index, value); | |
1358 } | |
1359 | |
1360 static bool requestParameterValueChangeCallback(void* const ptr, const uint32_t index, const float value) | |
1361 { | |
1362 return (((PluginLv2*)ptr)->requestParameterValueChange(index, value) == 0); | |
1363 } | |
1364 #endif | |
1365 | |
1366 #if DISTRHO_PLUGIN_WANT_STATE | |
1367 static bool updateStateValueCallback(void* const ptr, const char* const key, const char* const value) | |
1368 { | |
1369 return ((PluginLv2*)ptr)->updateState(key, value); | |
1370 } | |
1371 #endif | |
1372 | |
1373 #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |
1374 bool writeMidi(const MidiEvent& midiEvent) | |
1375 { | |
1376 DISTRHO_SAFE_ASSERT_RETURN(fEventsOutData.port != nullptr, false); | |
1377 | |
1378 fEventsOutData.initIfNeeded(fURIDs.atomSequence); | |
1379 | |
1380 const uint32_t capacity = fEventsOutData.capacity; | |
1381 const uint32_t offset = fEventsOutData.offset; | |
1382 | |
1383 if (sizeof(LV2_Atom_Event) + midiEvent.size > capacity - offset) | |
1384 return false; | |
1385 | |
1386 LV2_Atom_Event* const aev = (LV2_Atom_Event*)(LV2_ATOM_CONTENTS(LV2_Atom_Sequence, fEventsOutData.port) + offset); | |
1387 aev->time.frames = midiEvent.frame; | |
1388 aev->body.type = fURIDs.midiEvent; | |
1389 aev->body.size = midiEvent.size; | |
1390 std::memcpy(LV2_ATOM_BODY(&aev->body), | |
1391 midiEvent.size > MidiEvent::kDataSize ? midiEvent.dataExt : midiEvent.data, | |
1392 midiEvent.size); | |
1393 | |
1394 fEventsOutData.growBy(lv2_atom_pad_size(sizeof(LV2_Atom_Event) + midiEvent.size)); | |
1395 | |
1396 return true; | |
1397 } | |
1398 | |
1399 static bool writeMidiCallback(void* ptr, const MidiEvent& midiEvent) | |
1400 { | |
1401 return ((PluginLv2*)ptr)->writeMidi(midiEvent); | |
1402 } | |
1403 #endif | |
1404 }; | |
1405 | |
1406 // ----------------------------------------------------------------------- | |
1407 | |
1408 static LV2_Handle lv2_instantiate(const LV2_Descriptor*, double sampleRate, const char* bundlePath, const LV2_Feature* const* features) | |
1409 { | |
1410 const LV2_Options_Option* options = nullptr; | |
1411 const LV2_URID_Map* uridMap = nullptr; | |
1412 const LV2_Worker_Schedule* worker = nullptr; | |
1413 const LV2_ControlInputPort_Change_Request* ctrlInPortChangeReq = nullptr; | |
1414 | |
1415 for (int i=0; features[i] != nullptr; ++i) | |
1416 { | |
1417 if (std::strcmp(features[i]->URI, LV2_OPTIONS__options) == 0) | |
1418 options = (const LV2_Options_Option*)features[i]->data; | |
1419 else if (std::strcmp(features[i]->URI, LV2_URID__map) == 0) | |
1420 uridMap = (const LV2_URID_Map*)features[i]->data; | |
1421 else if (std::strcmp(features[i]->URI, LV2_WORKER__schedule) == 0) | |
1422 worker = (const LV2_Worker_Schedule*)features[i]->data; | |
1423 else if (std::strcmp(features[i]->URI, LV2_CONTROL_INPUT_PORT_CHANGE_REQUEST_URI) == 0) | |
1424 ctrlInPortChangeReq = (const LV2_ControlInputPort_Change_Request*)features[i]->data; | |
1425 } | |
1426 | |
1427 if (options == nullptr) | |
1428 { | |
1429 d_stderr("Options feature missing, cannot continue!"); | |
1430 return nullptr; | |
1431 } | |
1432 | |
1433 if (uridMap == nullptr) | |
1434 { | |
1435 d_stderr("URID Map feature missing, cannot continue!"); | |
1436 return nullptr; | |
1437 } | |
1438 | |
1439 #if DISTRHO_PLUGIN_WANT_STATE | |
1440 if (worker == nullptr) | |
1441 { | |
1442 d_stderr("Worker feature missing, cannot continue!"); | |
1443 return nullptr; | |
1444 } | |
1445 #endif | |
1446 | |
1447 #ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD | |
1448 mod_license_check(features, DISTRHO_PLUGIN_URI); | |
1449 #endif | |
1450 | |
1451 d_nextBufferSize = 0; | |
1452 bool usingNominal = false; | |
1453 | |
1454 for (int i=0; options[i].key != 0; ++i) | |
1455 { | |
1456 if (options[i].key == uridMap->map(uridMap->handle, LV2_BUF_SIZE__nominalBlockLength)) | |
1457 { | |
1458 if (options[i].type == uridMap->map(uridMap->handle, LV2_ATOM__Int)) | |
1459 { | |
1460 d_nextBufferSize = *(const int*)options[i].value; | |
1461 usingNominal = true; | |
1462 } | |
1463 else | |
1464 { | |
1465 d_stderr("Host provides nominalBlockLength but has wrong value type"); | |
1466 } | |
1467 break; | |
1468 } | |
1469 | |
1470 if (options[i].key == uridMap->map(uridMap->handle, LV2_BUF_SIZE__maxBlockLength)) | |
1471 { | |
1472 if (options[i].type == uridMap->map(uridMap->handle, LV2_ATOM__Int)) | |
1473 d_nextBufferSize = *(const int*)options[i].value; | |
1474 else | |
1475 d_stderr("Host provides maxBlockLength but has wrong value type"); | |
1476 | |
1477 // no break, continue in case host supports nominalBlockLength | |
1478 } | |
1479 } | |
1480 | |
1481 if (d_nextBufferSize == 0) | |
1482 { | |
1483 d_stderr("Host does not provide nominalBlockLength or maxBlockLength options"); | |
1484 d_nextBufferSize = 2048; | |
1485 } | |
1486 | |
1487 d_nextSampleRate = sampleRate; | |
1488 d_nextBundlePath = bundlePath; | |
1489 d_nextCanRequestParameterValueChanges = ctrlInPortChangeReq != nullptr; | |
1490 | |
1491 if (std::getenv("RUNNING_UNDER_LV2LINT") != nullptr) | |
1492 d_nextPluginIsDummy = true; | |
1493 | |
1494 return new PluginLv2(sampleRate, uridMap, worker, ctrlInPortChangeReq, usingNominal); | |
1495 } | |
1496 | |
1497 #define instancePtr ((PluginLv2*)instance) | |
1498 | |
1499 static void lv2_connect_port(LV2_Handle instance, uint32_t port, void* dataLocation) | |
1500 { | |
1501 instancePtr->lv2_connect_port(port, dataLocation); | |
1502 } | |
1503 | |
1504 static void lv2_activate(LV2_Handle instance) | |
1505 { | |
1506 instancePtr->lv2_activate(); | |
1507 } | |
1508 | |
1509 static void lv2_run(LV2_Handle instance, uint32_t sampleCount) | |
1510 { | |
1511 instancePtr->lv2_run(sampleCount); | |
1512 } | |
1513 | |
1514 static void lv2_deactivate(LV2_Handle instance) | |
1515 { | |
1516 instancePtr->lv2_deactivate(); | |
1517 } | |
1518 | |
1519 static void lv2_cleanup(LV2_Handle instance) | |
1520 { | |
1521 delete instancePtr; | |
1522 } | |
1523 | |
1524 // ----------------------------------------------------------------------- | |
1525 | |
1526 static uint32_t lv2_get_options(LV2_Handle instance, LV2_Options_Option* options) | |
1527 { | |
1528 return instancePtr->lv2_get_options(options); | |
1529 } | |
1530 | |
1531 static uint32_t lv2_set_options(LV2_Handle instance, const LV2_Options_Option* options) | |
1532 { | |
1533 return instancePtr->lv2_set_options(options); | |
1534 } | |
1535 | |
1536 // ----------------------------------------------------------------------- | |
1537 | |
1538 #if DISTRHO_PLUGIN_WANT_PROGRAMS | |
1539 static const LV2_Program_Descriptor* lv2_get_program(LV2_Handle instance, uint32_t index) | |
1540 { | |
1541 return instancePtr->lv2_get_program(index); | |
1542 } | |
1543 | |
1544 static void lv2_select_program(LV2_Handle instance, uint32_t bank, uint32_t program) | |
1545 { | |
1546 instancePtr->lv2_select_program(bank, program); | |
1547 } | |
1548 #endif | |
1549 | |
1550 // ----------------------------------------------------------------------- | |
1551 | |
1552 #if DISTRHO_PLUGIN_WANT_STATE | |
1553 static LV2_State_Status lv2_save(LV2_Handle instance, LV2_State_Store_Function store, LV2_State_Handle handle, uint32_t, const LV2_Feature* const*) | |
1554 { | |
1555 return instancePtr->lv2_save(store, handle); | |
1556 } | |
1557 | |
1558 static LV2_State_Status lv2_restore(LV2_Handle instance, LV2_State_Retrieve_Function retrieve, LV2_State_Handle handle, uint32_t, const LV2_Feature* const*) | |
1559 { | |
1560 return instancePtr->lv2_restore(retrieve, handle); | |
1561 } | |
1562 | |
1563 LV2_Worker_Status lv2_work(LV2_Handle instance, LV2_Worker_Respond_Function, LV2_Worker_Respond_Handle, uint32_t, const void* data) | |
1564 { | |
1565 return instancePtr->lv2_work(data); | |
1566 } | |
1567 | |
1568 LV2_Worker_Status lv2_work_response(LV2_Handle instance, uint32_t size, const void* body) | |
1569 { | |
1570 return instancePtr->lv2_work_response(size, body); | |
1571 } | |
1572 #endif | |
1573 | |
1574 // ----------------------------------------------------------------------- | |
1575 | |
1576 #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |
1577 static void* lv2_get_instance_pointer(LV2_Handle instance) | |
1578 { | |
1579 return instancePtr->lv2_get_instance_pointer(); | |
1580 } | |
1581 #endif | |
1582 | |
1583 // ----------------------------------------------------------------------- | |
1584 | |
1585 static const void* lv2_extension_data(const char* uri) | |
1586 { | |
1587 static const LV2_Options_Interface options = { lv2_get_options, lv2_set_options }; | |
1588 | |
1589 if (std::strcmp(uri, LV2_OPTIONS__interface) == 0) | |
1590 return &options; | |
1591 | |
1592 #if DISTRHO_PLUGIN_WANT_PROGRAMS | |
1593 static const LV2_Programs_Interface programs = { lv2_get_program, lv2_select_program }; | |
1594 | |
1595 if (std::strcmp(uri, LV2_PROGRAMS__Interface) == 0) | |
1596 return &programs; | |
1597 #endif | |
1598 | |
1599 #if DISTRHO_PLUGIN_WANT_STATE | |
1600 static const LV2_State_Interface state = { lv2_save, lv2_restore }; | |
1601 static const LV2_Worker_Interface worker = { lv2_work, lv2_work_response, nullptr }; | |
1602 | |
1603 if (std::strcmp(uri, LV2_STATE__interface) == 0) | |
1604 return &state; | |
1605 if (std::strcmp(uri, LV2_WORKER__interface) == 0) | |
1606 return &worker; | |
1607 #endif | |
1608 | |
1609 #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |
1610 struct LV2_DirectAccess_Interface { | |
1611 void* (*get_instance_pointer)(LV2_Handle handle); | |
1612 }; | |
1613 | |
1614 static const LV2_DirectAccess_Interface directaccess = { lv2_get_instance_pointer }; | |
1615 | |
1616 if (std::strcmp(uri, DISTRHO_PLUGIN_LV2_STATE_PREFIX "direct-access") == 0) | |
1617 return &directaccess; | |
1618 #endif | |
1619 | |
1620 #ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD | |
1621 return mod_license_interface(uri); | |
1622 #else | |
1623 return nullptr; | |
1624 #endif | |
1625 } | |
1626 | |
1627 #undef instancePtr | |
1628 | |
1629 // ----------------------------------------------------------------------- | |
1630 | |
1631 static const LV2_Descriptor sLv2Descriptor = { | |
1632 DISTRHO_PLUGIN_URI, | |
1633 lv2_instantiate, | |
1634 lv2_connect_port, | |
1635 lv2_activate, | |
1636 lv2_run, | |
1637 lv2_deactivate, | |
1638 lv2_cleanup, | |
1639 lv2_extension_data | |
1640 }; | |
1641 | |
1642 // ----------------------------------------------------------------------- | |
1643 | |
1644 END_NAMESPACE_DISTRHO | |
1645 | |
1646 DISTRHO_PLUGIN_EXPORT | |
1647 const LV2_Descriptor* lv2_descriptor(uint32_t index) | |
1648 { | |
1649 USE_NAMESPACE_DISTRHO | |
1650 return (index == 0) ? &sLv2Descriptor : nullptr; | |
1651 } | |
1652 | |
1653 // ----------------------------------------------------------------------- |