Mercurial > hg > pub > prymula > com
comparison DPF-Prymula-audioplugins/dpf/distrho/src/DistrhoPluginJACK.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 * This program is free software; you can redistribute it and/or | |
6 * modify it under the terms of the GNU Lesser General Public | |
7 * License as published by the Free Software Foundation. | |
8 * | |
9 * This program is distributed in the hope that it will be useful, | |
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 * GNU Lesser General Public License for more details. | |
13 * | |
14 * For a full copy of the license see the LGPL.txt file | |
15 */ | |
16 | |
17 #include "DistrhoPluginInternal.hpp" | |
18 | |
19 #ifndef STATIC_BUILD | |
20 # include "../DistrhoPluginUtils.hpp" | |
21 #endif | |
22 | |
23 #if DISTRHO_PLUGIN_HAS_UI | |
24 # include "DistrhoUIInternal.hpp" | |
25 # include "../extra/RingBuffer.hpp" | |
26 #else | |
27 # include "../extra/Sleep.hpp" | |
28 #endif | |
29 | |
30 #ifdef DPF_RUNTIME_TESTING | |
31 # include "../extra/Thread.hpp" | |
32 #endif | |
33 | |
34 #if defined(HAVE_JACK) && defined(STATIC_BUILD) && !defined(DISTRHO_OS_WASM) | |
35 # define JACKBRIDGE_DIRECT | |
36 #endif | |
37 | |
38 #include "jackbridge/JackBridge.cpp" | |
39 #include "lv2/lv2.h" | |
40 | |
41 #ifdef DISTRHO_OS_MAC | |
42 # define Point CocoaPoint | |
43 # include <CoreFoundation/CoreFoundation.h> | |
44 # undef Point | |
45 #endif | |
46 | |
47 #ifndef DISTRHO_OS_WINDOWS | |
48 # include <signal.h> | |
49 # include <unistd.h> | |
50 #endif | |
51 | |
52 #ifdef __SSE2_MATH__ | |
53 # include <xmmintrin.h> | |
54 #endif | |
55 | |
56 #ifndef JACK_METADATA_ORDER | |
57 # define JACK_METADATA_ORDER "http://jackaudio.org/metadata/order" | |
58 #endif | |
59 | |
60 #ifndef JACK_METADATA_PRETTY_NAME | |
61 # define JACK_METADATA_PRETTY_NAME "http://jackaudio.org/metadata/pretty-name" | |
62 #endif | |
63 | |
64 #ifndef JACK_METADATA_PORT_GROUP | |
65 # define JACK_METADATA_PORT_GROUP "http://jackaudio.org/metadata/port-group" | |
66 #endif | |
67 | |
68 #ifndef JACK_METADATA_SIGNAL_TYPE | |
69 # define JACK_METADATA_SIGNAL_TYPE "http://jackaudio.org/metadata/signal-type" | |
70 #endif | |
71 | |
72 // ----------------------------------------------------------------------- | |
73 | |
74 START_NAMESPACE_DISTRHO | |
75 | |
76 #if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_WANT_MIDI_INPUT | |
77 static const sendNoteFunc sendNoteCallback = nullptr; | |
78 #endif | |
79 #if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_WANT_STATE | |
80 static const setStateFunc setStateCallback = nullptr; | |
81 #endif | |
82 #if ! DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |
83 static const writeMidiFunc writeMidiCallback = nullptr; | |
84 #endif | |
85 #if ! DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST | |
86 static const requestParameterValueChangeFunc requestParameterValueChangeCallback = nullptr; | |
87 #endif | |
88 | |
89 // ----------------------------------------------------------------------- | |
90 | |
91 static volatile bool gCloseSignalReceived = false; | |
92 | |
93 #ifdef DISTRHO_OS_WINDOWS | |
94 static BOOL WINAPI winSignalHandler(DWORD dwCtrlType) noexcept | |
95 { | |
96 if (dwCtrlType == CTRL_C_EVENT) | |
97 { | |
98 gCloseSignalReceived = true; | |
99 return TRUE; | |
100 } | |
101 return FALSE; | |
102 } | |
103 | |
104 static void initSignalHandler() | |
105 { | |
106 SetConsoleCtrlHandler(winSignalHandler, TRUE); | |
107 } | |
108 #else | |
109 static void closeSignalHandler(int) noexcept | |
110 { | |
111 gCloseSignalReceived = true; | |
112 } | |
113 | |
114 static void initSignalHandler() | |
115 { | |
116 struct sigaction sig; | |
117 memset(&sig, 0, sizeof(sig)); | |
118 | |
119 sig.sa_handler = closeSignalHandler; | |
120 sig.sa_flags = SA_RESTART; | |
121 sigemptyset(&sig.sa_mask); | |
122 sigaction(SIGINT, &sig, nullptr); | |
123 sigaction(SIGTERM, &sig, nullptr); | |
124 } | |
125 #endif | |
126 | |
127 // ----------------------------------------------------------------------- | |
128 | |
129 #if DISTRHO_PLUGIN_HAS_UI | |
130 class PluginJack : public DGL_NAMESPACE::IdleCallback | |
131 #else | |
132 class PluginJack | |
133 #endif | |
134 { | |
135 public: | |
136 PluginJack(jack_client_t* const client, const uintptr_t winId) | |
137 : fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback, nullptr), | |
138 #if DISTRHO_PLUGIN_HAS_UI | |
139 fUI(this, | |
140 winId, | |
141 d_nextSampleRate, | |
142 nullptr, // edit param | |
143 setParameterValueCallback, | |
144 setStateCallback, | |
145 sendNoteCallback, | |
146 nullptr, // window size | |
147 nullptr, // file request | |
148 nullptr, // bundle | |
149 fPlugin.getInstancePointer(), | |
150 0.0), | |
151 #endif | |
152 fClient(client) | |
153 { | |
154 #if DISTRHO_PLUGIN_NUM_INPUTS > 0 || DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |
155 # if DISTRHO_PLUGIN_NUM_INPUTS > 0 | |
156 for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i) | |
157 { | |
158 const AudioPort& port(fPlugin.getAudioPort(true, i)); | |
159 ulong hints = JackPortIsInput; | |
160 if (port.hints & kAudioPortIsCV) | |
161 hints |= JackPortIsControlVoltage; | |
162 fPortAudioIns[i] = jackbridge_port_register(fClient, port.symbol, JACK_DEFAULT_AUDIO_TYPE, hints, 0); | |
163 setAudioPortMetadata(port, fPortAudioIns[i], i); | |
164 } | |
165 # endif | |
166 # if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |
167 for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) | |
168 { | |
169 const AudioPort& port(fPlugin.getAudioPort(false, i)); | |
170 ulong hints = JackPortIsOutput; | |
171 if (port.hints & kAudioPortIsCV) | |
172 hints |= JackPortIsControlVoltage; | |
173 fPortAudioOuts[i] = jackbridge_port_register(fClient, port.symbol, JACK_DEFAULT_AUDIO_TYPE, hints, 0); | |
174 setAudioPortMetadata(port, fPortAudioOuts[i], DISTRHO_PLUGIN_NUM_INPUTS+i); | |
175 } | |
176 # endif | |
177 #endif | |
178 | |
179 fPortEventsIn = jackbridge_port_register(fClient, "events-in", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0); | |
180 | |
181 #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |
182 fPortMidiOut = jackbridge_port_register(fClient, "midi-out", JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0); | |
183 fPortMidiOutBuffer = nullptr; | |
184 #endif | |
185 | |
186 #if DISTRHO_PLUGIN_WANT_PROGRAMS | |
187 if (fPlugin.getProgramCount() > 0) | |
188 { | |
189 fPlugin.loadProgram(0); | |
190 # if DISTRHO_PLUGIN_HAS_UI | |
191 fUI.programLoaded(0); | |
192 # endif | |
193 } | |
194 # if DISTRHO_PLUGIN_HAS_UI | |
195 fProgramChanged = -1; | |
196 # endif | |
197 #endif | |
198 | |
199 if (const uint32_t count = fPlugin.getParameterCount()) | |
200 { | |
201 fLastOutputValues = new float[count]; | |
202 std::memset(fLastOutputValues, 0, sizeof(float)*count); | |
203 | |
204 #if DISTRHO_PLUGIN_HAS_UI | |
205 fParametersChanged = new bool[count]; | |
206 std::memset(fParametersChanged, 0, sizeof(bool)*count); | |
207 #endif | |
208 | |
209 for (uint32_t i=0; i < count; ++i) | |
210 { | |
211 #if DISTRHO_PLUGIN_HAS_UI | |
212 if (! fPlugin.isParameterOutput(i)) | |
213 fUI.parameterChanged(i, fPlugin.getParameterValue(i)); | |
214 #endif | |
215 } | |
216 } | |
217 else | |
218 { | |
219 fLastOutputValues = nullptr; | |
220 #if DISTRHO_PLUGIN_HAS_UI | |
221 fParametersChanged = nullptr; | |
222 #endif | |
223 } | |
224 | |
225 jackbridge_set_thread_init_callback(fClient, jackThreadInitCallback, this); | |
226 jackbridge_set_buffer_size_callback(fClient, jackBufferSizeCallback, this); | |
227 jackbridge_set_sample_rate_callback(fClient, jackSampleRateCallback, this); | |
228 jackbridge_set_process_callback(fClient, jackProcessCallback, this); | |
229 jackbridge_on_shutdown(fClient, jackShutdownCallback, this); | |
230 | |
231 fPlugin.activate(); | |
232 | |
233 jackbridge_activate(fClient); | |
234 | |
235 std::fflush(stdout); | |
236 | |
237 #if DISTRHO_PLUGIN_HAS_UI | |
238 if (const char* const name = jackbridge_get_client_name(fClient)) | |
239 fUI.setWindowTitle(name); | |
240 else | |
241 fUI.setWindowTitle(fPlugin.getName()); | |
242 | |
243 fUI.exec(this); | |
244 #else | |
245 while (! gCloseSignalReceived) | |
246 d_sleep(1); | |
247 | |
248 // unused | |
249 (void)winId; | |
250 #endif | |
251 } | |
252 | |
253 ~PluginJack() | |
254 { | |
255 if (fClient != nullptr) | |
256 jackbridge_deactivate(fClient); | |
257 | |
258 if (fLastOutputValues != nullptr) | |
259 { | |
260 delete[] fLastOutputValues; | |
261 fLastOutputValues = nullptr; | |
262 } | |
263 | |
264 #if DISTRHO_PLUGIN_HAS_UI | |
265 if (fParametersChanged != nullptr) | |
266 { | |
267 delete[] fParametersChanged; | |
268 fParametersChanged = nullptr; | |
269 } | |
270 #endif | |
271 | |
272 fPlugin.deactivate(); | |
273 | |
274 if (fClient == nullptr) | |
275 return; | |
276 | |
277 #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |
278 jackbridge_port_unregister(fClient, fPortMidiOut); | |
279 fPortMidiOut = nullptr; | |
280 #endif | |
281 | |
282 jackbridge_port_unregister(fClient, fPortEventsIn); | |
283 fPortEventsIn = nullptr; | |
284 | |
285 #if DISTRHO_PLUGIN_NUM_INPUTS > 0 | |
286 for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i) | |
287 { | |
288 jackbridge_port_unregister(fClient, fPortAudioIns[i]); | |
289 fPortAudioIns[i] = nullptr; | |
290 } | |
291 #endif | |
292 | |
293 #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |
294 for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) | |
295 { | |
296 jackbridge_port_unregister(fClient, fPortAudioOuts[i]); | |
297 fPortAudioOuts[i] = nullptr; | |
298 } | |
299 #endif | |
300 | |
301 jackbridge_client_close(fClient); | |
302 } | |
303 | |
304 // ------------------------------------------------------------------- | |
305 | |
306 protected: | |
307 #if DISTRHO_PLUGIN_HAS_UI | |
308 void idleCallback() override | |
309 { | |
310 if (gCloseSignalReceived) | |
311 return fUI.quit(); | |
312 | |
313 # if DISTRHO_PLUGIN_WANT_PROGRAMS | |
314 if (fProgramChanged >= 0) | |
315 { | |
316 fUI.programLoaded(fProgramChanged); | |
317 fProgramChanged = -1; | |
318 } | |
319 # endif | |
320 | |
321 for (uint32_t i=0, count=fPlugin.getParameterCount(); i < count; ++i) | |
322 { | |
323 if (fPlugin.isParameterOutput(i)) | |
324 { | |
325 const float value = fPlugin.getParameterValue(i); | |
326 | |
327 if (d_isEqual(fLastOutputValues[i], value)) | |
328 continue; | |
329 | |
330 fLastOutputValues[i] = value; | |
331 fUI.parameterChanged(i, value); | |
332 } | |
333 else if (fParametersChanged[i]) | |
334 { | |
335 fParametersChanged[i] = false; | |
336 fUI.parameterChanged(i, fPlugin.getParameterValue(i)); | |
337 } | |
338 } | |
339 | |
340 fUI.exec_idle(); | |
341 } | |
342 #endif | |
343 | |
344 void jackBufferSize(const jack_nframes_t nframes) | |
345 { | |
346 fPlugin.setBufferSize(nframes, true); | |
347 } | |
348 | |
349 void jackSampleRate(const jack_nframes_t nframes) | |
350 { | |
351 fPlugin.setSampleRate(nframes, true); | |
352 } | |
353 | |
354 void jackProcess(const jack_nframes_t nframes) | |
355 { | |
356 #if DISTRHO_PLUGIN_NUM_INPUTS > 0 | |
357 const float* audioIns[DISTRHO_PLUGIN_NUM_INPUTS]; | |
358 | |
359 for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i) | |
360 audioIns[i] = (const float*)jackbridge_port_get_buffer(fPortAudioIns[i], nframes); | |
361 #else | |
362 static const float** audioIns = nullptr; | |
363 #endif | |
364 | |
365 #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |
366 float* audioOuts[DISTRHO_PLUGIN_NUM_OUTPUTS]; | |
367 | |
368 for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) | |
369 audioOuts[i] = (float*)jackbridge_port_get_buffer(fPortAudioOuts[i], nframes); | |
370 #else | |
371 static float** audioOuts = nullptr; | |
372 #endif | |
373 | |
374 #if DISTRHO_PLUGIN_WANT_TIMEPOS | |
375 jack_position_t pos; | |
376 fTimePosition.playing = (jackbridge_transport_query(fClient, &pos) == JackTransportRolling); | |
377 | |
378 if (pos.unique_1 == pos.unique_2) | |
379 { | |
380 fTimePosition.frame = pos.frame; | |
381 | |
382 if (pos.valid & JackPositionBBT) | |
383 { | |
384 fTimePosition.bbt.valid = true; | |
385 | |
386 fTimePosition.bbt.bar = pos.bar; | |
387 fTimePosition.bbt.beat = pos.beat; | |
388 fTimePosition.bbt.tick = pos.tick; | |
389 #ifdef JACK_TICK_DOUBLE | |
390 if (pos.valid & JackTickDouble) | |
391 fTimePosition.bbt.tick = pos.tick_double; | |
392 else | |
393 #endif | |
394 fTimePosition.bbt.tick = pos.tick; | |
395 fTimePosition.bbt.barStartTick = pos.bar_start_tick; | |
396 | |
397 fTimePosition.bbt.beatsPerBar = pos.beats_per_bar; | |
398 fTimePosition.bbt.beatType = pos.beat_type; | |
399 | |
400 fTimePosition.bbt.ticksPerBeat = pos.ticks_per_beat; | |
401 fTimePosition.bbt.beatsPerMinute = pos.beats_per_minute; | |
402 } | |
403 else | |
404 fTimePosition.bbt.valid = false; | |
405 } | |
406 else | |
407 { | |
408 fTimePosition.bbt.valid = false; | |
409 fTimePosition.frame = 0; | |
410 } | |
411 | |
412 fPlugin.setTimePosition(fTimePosition); | |
413 #endif | |
414 | |
415 updateParameterTriggers(); | |
416 | |
417 #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |
418 fPortMidiOutBuffer = jackbridge_port_get_buffer(fPortMidiOut, nframes); | |
419 jackbridge_midi_clear_buffer(fPortMidiOutBuffer); | |
420 #endif | |
421 | |
422 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |
423 uint32_t midiEventCount = 0; | |
424 MidiEvent midiEvents[512]; | |
425 | |
426 # if DISTRHO_PLUGIN_HAS_UI | |
427 while (fNotesRingBuffer.isDataAvailableForReading()) | |
428 { | |
429 uint8_t midiData[3]; | |
430 if (! fNotesRingBuffer.readCustomData(midiData, 3)) | |
431 break; | |
432 | |
433 MidiEvent& midiEvent(midiEvents[midiEventCount++]); | |
434 midiEvent.frame = 0; | |
435 midiEvent.size = 3; | |
436 std::memcpy(midiEvent.data, midiData, 3); | |
437 | |
438 if (midiEventCount == 512) | |
439 break; | |
440 } | |
441 # endif | |
442 #else | |
443 static const uint32_t midiEventCount = 0; | |
444 #endif | |
445 | |
446 void* const midiInBuf = jackbridge_port_get_buffer(fPortEventsIn, nframes); | |
447 | |
448 if (const uint32_t eventCount = std::min(512u - midiEventCount, jackbridge_midi_get_event_count(midiInBuf))) | |
449 { | |
450 jack_midi_event_t jevent; | |
451 | |
452 for (uint32_t i=0; i < eventCount; ++i) | |
453 { | |
454 if (! jackbridge_midi_event_get(&jevent, midiInBuf, i)) | |
455 break; | |
456 | |
457 // Check if message is control change on channel 1 | |
458 if (jevent.buffer[0] == 0xB0 && jevent.size == 3) | |
459 { | |
460 const uint8_t control = jevent.buffer[1]; | |
461 const uint8_t value = jevent.buffer[2]; | |
462 | |
463 /* NOTE: This is not optimal, we're iterating all parameters on every CC message. | |
464 Since the JACK standalone is more of a test tool, this will do for now. */ | |
465 for (uint32_t j=0, paramCount=fPlugin.getParameterCount(); j < paramCount; ++j) | |
466 { | |
467 if (fPlugin.isParameterOutput(j)) | |
468 continue; | |
469 if (fPlugin.getParameterMidiCC(j) != control) | |
470 continue; | |
471 | |
472 const float scaled = static_cast<float>(value)/127.0f; | |
473 const float fvalue = fPlugin.getParameterRanges(j).getUnnormalizedValue(scaled); | |
474 fPlugin.setParameterValue(j, fvalue); | |
475 #if DISTRHO_PLUGIN_HAS_UI | |
476 fParametersChanged[j] = true; | |
477 #endif | |
478 break; | |
479 } | |
480 } | |
481 #if DISTRHO_PLUGIN_WANT_PROGRAMS | |
482 // Check if message is program change on channel 1 | |
483 else if (jevent.buffer[0] == 0xC0 && jevent.size == 2) | |
484 { | |
485 const uint8_t program = jevent.buffer[1]; | |
486 | |
487 if (program < fPlugin.getProgramCount()) | |
488 { | |
489 fPlugin.loadProgram(program); | |
490 # if DISTRHO_PLUGIN_HAS_UI | |
491 fProgramChanged = program; | |
492 # endif | |
493 } | |
494 } | |
495 #endif | |
496 | |
497 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |
498 MidiEvent& midiEvent(midiEvents[midiEventCount++]); | |
499 | |
500 midiEvent.frame = jevent.time; | |
501 midiEvent.size = static_cast<uint32_t>(jevent.size); | |
502 | |
503 if (midiEvent.size > MidiEvent::kDataSize) | |
504 midiEvent.dataExt = jevent.buffer; | |
505 else | |
506 std::memcpy(midiEvent.data, jevent.buffer, midiEvent.size); | |
507 #endif | |
508 } | |
509 } | |
510 | |
511 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |
512 fPlugin.run(audioIns, audioOuts, nframes, midiEvents, midiEventCount); | |
513 #else | |
514 fPlugin.run(audioIns, audioOuts, nframes); | |
515 #endif | |
516 | |
517 #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |
518 fPortMidiOutBuffer = nullptr; | |
519 #endif | |
520 } | |
521 | |
522 void jackShutdown() | |
523 { | |
524 d_stderr("jack has shutdown, quitting now..."); | |
525 fClient = nullptr; | |
526 #if DISTRHO_PLUGIN_HAS_UI | |
527 fUI.quit(); | |
528 #endif | |
529 } | |
530 | |
531 // ------------------------------------------------------------------- | |
532 | |
533 #if DISTRHO_PLUGIN_HAS_UI | |
534 void setParameterValue(const uint32_t index, const float value) | |
535 { | |
536 fPlugin.setParameterValue(index, value); | |
537 } | |
538 | |
539 # if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |
540 void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity) | |
541 { | |
542 uint8_t midiData[3]; | |
543 midiData[0] = (velocity != 0 ? 0x90 : 0x80) | channel; | |
544 midiData[1] = note; | |
545 midiData[2] = velocity; | |
546 fNotesRingBuffer.writeCustomData(midiData, 3); | |
547 fNotesRingBuffer.commitWrite(); | |
548 } | |
549 # endif | |
550 | |
551 # if DISTRHO_PLUGIN_WANT_STATE | |
552 void setState(const char* const key, const char* const value) | |
553 { | |
554 fPlugin.setState(key, value); | |
555 } | |
556 # endif | |
557 #endif // DISTRHO_PLUGIN_HAS_UI | |
558 | |
559 // NOTE: no trigger support for JACK, simulate it here | |
560 void updateParameterTriggers() | |
561 { | |
562 float defValue; | |
563 | |
564 for (uint32_t i=0, count=fPlugin.getParameterCount(); i < count; ++i) | |
565 { | |
566 if ((fPlugin.getParameterHints(i) & kParameterIsTrigger) != kParameterIsTrigger) | |
567 continue; | |
568 | |
569 defValue = fPlugin.getParameterRanges(i).def; | |
570 | |
571 if (d_isNotEqual(defValue, fPlugin.getParameterValue(i))) | |
572 fPlugin.setParameterValue(i, defValue); | |
573 } | |
574 } | |
575 | |
576 // ------------------------------------------------------------------- | |
577 | |
578 private: | |
579 PluginExporter fPlugin; | |
580 #if DISTRHO_PLUGIN_HAS_UI | |
581 UIExporter fUI; | |
582 #endif | |
583 | |
584 jack_client_t* fClient; | |
585 | |
586 #if DISTRHO_PLUGIN_NUM_INPUTS > 0 | |
587 jack_port_t* fPortAudioIns[DISTRHO_PLUGIN_NUM_INPUTS]; | |
588 #endif | |
589 #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |
590 jack_port_t* fPortAudioOuts[DISTRHO_PLUGIN_NUM_OUTPUTS]; | |
591 #endif | |
592 jack_port_t* fPortEventsIn; | |
593 #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |
594 jack_port_t* fPortMidiOut; | |
595 void* fPortMidiOutBuffer; | |
596 #endif | |
597 #if DISTRHO_PLUGIN_WANT_TIMEPOS | |
598 TimePosition fTimePosition; | |
599 #endif | |
600 | |
601 // Temporary data | |
602 float* fLastOutputValues; | |
603 | |
604 #if DISTRHO_PLUGIN_HAS_UI | |
605 // Store DSP changes to send to UI | |
606 bool* fParametersChanged; | |
607 # if DISTRHO_PLUGIN_WANT_PROGRAMS | |
608 int fProgramChanged; | |
609 # endif | |
610 # if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |
611 SmallStackRingBuffer fNotesRingBuffer; | |
612 # endif | |
613 #endif | |
614 | |
615 void setAudioPortMetadata(const AudioPort& port, jack_port_t* const jackport, const uint32_t index) | |
616 { | |
617 DISTRHO_SAFE_ASSERT_RETURN(jackport != nullptr,); | |
618 | |
619 const jack_uuid_t uuid = jackbridge_port_uuid(jackport); | |
620 | |
621 if (uuid == JACK_UUID_EMPTY_INITIALIZER) | |
622 return; | |
623 | |
624 jackbridge_set_property(fClient, uuid, JACK_METADATA_PRETTY_NAME, port.name, "text/plain"); | |
625 | |
626 { | |
627 char strBuf[0xff]; | |
628 snprintf(strBuf, 0xff - 2, "%u", index); | |
629 strBuf[0xff - 1] = '\0'; | |
630 jackbridge_set_property(fClient, uuid, JACK_METADATA_ORDER, strBuf, "http://www.w3.org/2001/XMLSchema#integer"); | |
631 } | |
632 | |
633 if (port.groupId != kPortGroupNone) | |
634 { | |
635 const PortGroupWithId& portGroup(fPlugin.getPortGroupById(port.groupId)); | |
636 jackbridge_set_property(fClient, uuid, JACK_METADATA_PORT_GROUP, portGroup.name, "text/plain"); | |
637 } | |
638 | |
639 if (port.hints & kAudioPortIsCV) | |
640 { | |
641 jackbridge_set_property(fClient, uuid, JACK_METADATA_SIGNAL_TYPE, "CV", "text/plain"); | |
642 } | |
643 else | |
644 { | |
645 jackbridge_set_property(fClient, uuid, JACK_METADATA_SIGNAL_TYPE, "AUDIO", "text/plain"); | |
646 return; | |
647 } | |
648 | |
649 // set cv ranges | |
650 const bool cvPortScaled = port.hints & kCVPortHasScaledRange; | |
651 | |
652 if (port.hints & kCVPortHasBipolarRange) | |
653 { | |
654 if (cvPortScaled) | |
655 { | |
656 jackbridge_set_property(fClient, uuid, LV2_CORE__minimum, "-5", "http://www.w3.org/2001/XMLSchema#integer"); | |
657 jackbridge_set_property(fClient, uuid, LV2_CORE__maximum, "5", "http://www.w3.org/2001/XMLSchema#integer"); | |
658 } | |
659 else | |
660 { | |
661 jackbridge_set_property(fClient, uuid, LV2_CORE__minimum, "-1", "http://www.w3.org/2001/XMLSchema#integer"); | |
662 jackbridge_set_property(fClient, uuid, LV2_CORE__maximum, "1", "http://www.w3.org/2001/XMLSchema#integer"); | |
663 } | |
664 } | |
665 else if (port.hints & kCVPortHasNegativeUnipolarRange) | |
666 { | |
667 if (cvPortScaled) | |
668 { | |
669 jackbridge_set_property(fClient, uuid, LV2_CORE__minimum, "-10", "http://www.w3.org/2001/XMLSchema#integer"); | |
670 jackbridge_set_property(fClient, uuid, LV2_CORE__maximum, "0", "http://www.w3.org/2001/XMLSchema#integer"); | |
671 } | |
672 else | |
673 { | |
674 jackbridge_set_property(fClient, uuid, LV2_CORE__minimum, "-1", "http://www.w3.org/2001/XMLSchema#integer"); | |
675 jackbridge_set_property(fClient, uuid, LV2_CORE__maximum, "0", "http://www.w3.org/2001/XMLSchema#integer"); | |
676 } | |
677 } | |
678 else if (port.hints & kCVPortHasPositiveUnipolarRange) | |
679 { | |
680 if (cvPortScaled) | |
681 { | |
682 jackbridge_set_property(fClient, uuid, LV2_CORE__minimum, "0", "http://www.w3.org/2001/XMLSchema#integer"); | |
683 jackbridge_set_property(fClient, uuid, LV2_CORE__maximum, "10", "http://www.w3.org/2001/XMLSchema#integer"); | |
684 } | |
685 else | |
686 { | |
687 jackbridge_set_property(fClient, uuid, LV2_CORE__minimum, "0", "http://www.w3.org/2001/XMLSchema#integer"); | |
688 jackbridge_set_property(fClient, uuid, LV2_CORE__maximum, "1", "http://www.w3.org/2001/XMLSchema#integer"); | |
689 } | |
690 } | |
691 } | |
692 | |
693 // ------------------------------------------------------------------- | |
694 // Callbacks | |
695 | |
696 #define thisPtr ((PluginJack*)ptr) | |
697 | |
698 static void jackThreadInitCallback(void*) | |
699 { | |
700 #if defined(__SSE2_MATH__) | |
701 _mm_setcsr(_mm_getcsr() | 0x8040); | |
702 #elif defined(__aarch64__) | |
703 uint64_t c; | |
704 __asm__ __volatile__("mrs %0, fpcr \n" | |
705 "orr %0, %0, #0x1000000\n" | |
706 "msr fpcr, %0 \n" | |
707 "isb \n" | |
708 : "=r"(c) :: "memory"); | |
709 #elif defined(__arm__) && !defined(__SOFTFP__) | |
710 uint32_t c; | |
711 __asm__ __volatile__("vmrs %0, fpscr \n" | |
712 "orr %0, %0, #0x1000000\n" | |
713 "vmsr fpscr, %0 \n" | |
714 : "=r"(c) :: "memory"); | |
715 #endif | |
716 } | |
717 | |
718 static int jackBufferSizeCallback(jack_nframes_t nframes, void* ptr) | |
719 { | |
720 thisPtr->jackBufferSize(nframes); | |
721 return 0; | |
722 } | |
723 | |
724 static int jackSampleRateCallback(jack_nframes_t nframes, void* ptr) | |
725 { | |
726 thisPtr->jackSampleRate(nframes); | |
727 return 0; | |
728 } | |
729 | |
730 static int jackProcessCallback(jack_nframes_t nframes, void* ptr) | |
731 { | |
732 thisPtr->jackProcess(nframes); | |
733 return 0; | |
734 } | |
735 | |
736 static void jackShutdownCallback(void* ptr) | |
737 { | |
738 thisPtr->jackShutdown(); | |
739 } | |
740 | |
741 #if DISTRHO_PLUGIN_HAS_UI | |
742 static void setParameterValueCallback(void* ptr, uint32_t index, float value) | |
743 { | |
744 thisPtr->setParameterValue(index, value); | |
745 } | |
746 | |
747 # if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |
748 static void sendNoteCallback(void* ptr, uint8_t channel, uint8_t note, uint8_t velocity) | |
749 { | |
750 thisPtr->sendNote(channel, note, velocity); | |
751 } | |
752 # endif | |
753 | |
754 # if DISTRHO_PLUGIN_WANT_STATE | |
755 static void setStateCallback(void* ptr, const char* key, const char* value) | |
756 { | |
757 thisPtr->setState(key, value); | |
758 } | |
759 # endif | |
760 #endif // DISTRHO_PLUGIN_HAS_UI | |
761 | |
762 #if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST | |
763 bool requestParameterValueChange(const uint32_t index, const float value) | |
764 { | |
765 DISTRHO_SAFE_ASSERT_RETURN(index < fPlugin.getParameterCount(), false); | |
766 | |
767 fPlugin.setParameterValue(index, value); | |
768 # if DISTRHO_PLUGIN_HAS_UI | |
769 fParametersChanged[index] = true; | |
770 # endif | |
771 return true; | |
772 } | |
773 | |
774 static bool requestParameterValueChangeCallback(void* ptr, const uint32_t index, const float value) | |
775 { | |
776 return thisPtr->requestParameterValueChange(index, value); | |
777 } | |
778 #endif | |
779 | |
780 #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |
781 bool writeMidi(const MidiEvent& midiEvent) | |
782 { | |
783 DISTRHO_SAFE_ASSERT_RETURN(fPortMidiOutBuffer != nullptr, false); | |
784 | |
785 return jackbridge_midi_event_write(fPortMidiOutBuffer, | |
786 midiEvent.frame, | |
787 midiEvent.size > MidiEvent::kDataSize ? midiEvent.dataExt : midiEvent.data, | |
788 midiEvent.size); | |
789 } | |
790 | |
791 static bool writeMidiCallback(void* ptr, const MidiEvent& midiEvent) | |
792 { | |
793 return thisPtr->writeMidi(midiEvent); | |
794 } | |
795 #endif | |
796 | |
797 #undef thisPtr | |
798 }; | |
799 | |
800 // ----------------------------------------------------------------------- | |
801 | |
802 #ifdef DPF_RUNTIME_TESTING | |
803 class PluginProcessTestingThread : public Thread | |
804 { | |
805 PluginExporter& plugin; | |
806 | |
807 public: | |
808 PluginProcessTestingThread(PluginExporter& p) : plugin(p) {} | |
809 | |
810 protected: | |
811 void run() override | |
812 { | |
813 plugin.setBufferSize(256, true); | |
814 plugin.activate(); | |
815 | |
816 float buffer[256]; | |
817 const float* inputs[DISTRHO_PLUGIN_NUM_INPUTS > 0 ? DISTRHO_PLUGIN_NUM_INPUTS : 1]; | |
818 float* outputs[DISTRHO_PLUGIN_NUM_OUTPUTS > 0 ? DISTRHO_PLUGIN_NUM_OUTPUTS : 1]; | |
819 for (int i=0; i<DISTRHO_PLUGIN_NUM_INPUTS; ++i) | |
820 inputs[i] = buffer; | |
821 for (int i=0; i<DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) | |
822 outputs[i] = buffer; | |
823 | |
824 while (! shouldThreadExit()) | |
825 { | |
826 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |
827 plugin.run(inputs, outputs, 128, nullptr, 0); | |
828 #else | |
829 plugin.run(inputs, outputs, 128); | |
830 #endif | |
831 d_msleep(100); | |
832 } | |
833 | |
834 plugin.deactivate(); | |
835 } | |
836 }; | |
837 | |
838 bool runSelfTests() | |
839 { | |
840 // simple plugin creation first | |
841 { | |
842 d_nextBufferSize = 512; | |
843 d_nextSampleRate = 44100.0; | |
844 PluginExporter plugin(nullptr, nullptr, nullptr, nullptr); | |
845 d_nextBufferSize = 0; | |
846 d_nextSampleRate = 0.0; | |
847 } | |
848 | |
849 // keep values for all tests now | |
850 d_nextBufferSize = 512; | |
851 d_nextSampleRate = 44100.0; | |
852 | |
853 // simple processing | |
854 { | |
855 d_nextPluginIsSelfTest = true; | |
856 PluginExporter plugin(nullptr, nullptr, nullptr, nullptr); | |
857 d_nextPluginIsSelfTest = false; | |
858 | |
859 #if DISTRHO_PLUGIN_HAS_UI | |
860 UIExporter ui(nullptr, 0, plugin.getSampleRate(), | |
861 nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, | |
862 plugin.getInstancePointer(), 0.0); | |
863 ui.showAndFocus(); | |
864 #endif | |
865 | |
866 plugin.activate(); | |
867 plugin.deactivate(); | |
868 plugin.setBufferSize(128, true); | |
869 plugin.setSampleRate(48000, true); | |
870 plugin.activate(); | |
871 | |
872 float buffer[128] = {}; | |
873 const float* inputs[DISTRHO_PLUGIN_NUM_INPUTS > 0 ? DISTRHO_PLUGIN_NUM_INPUTS : 1]; | |
874 float* outputs[DISTRHO_PLUGIN_NUM_OUTPUTS > 0 ? DISTRHO_PLUGIN_NUM_OUTPUTS : 1]; | |
875 for (int i=0; i<DISTRHO_PLUGIN_NUM_INPUTS; ++i) | |
876 inputs[i] = buffer; | |
877 for (int i=0; i<DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) | |
878 outputs[i] = buffer; | |
879 | |
880 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |
881 plugin.run(inputs, outputs, 128, nullptr, 0); | |
882 #else | |
883 plugin.run(inputs, outputs, 128); | |
884 #endif | |
885 | |
886 plugin.deactivate(); | |
887 | |
888 #if DISTRHO_PLUGIN_HAS_UI | |
889 ui.plugin_idle(); | |
890 #endif | |
891 } | |
892 | |
893 return true; | |
894 | |
895 // multi-threaded processing with UI | |
896 { | |
897 PluginExporter pluginA(nullptr, nullptr, nullptr, nullptr); | |
898 PluginExporter pluginB(nullptr, nullptr, nullptr, nullptr); | |
899 PluginExporter pluginC(nullptr, nullptr, nullptr, nullptr); | |
900 PluginProcessTestingThread procTestA(pluginA); | |
901 PluginProcessTestingThread procTestB(pluginB); | |
902 PluginProcessTestingThread procTestC(pluginC); | |
903 procTestA.startThread(); | |
904 procTestB.startThread(); | |
905 procTestC.startThread(); | |
906 | |
907 // wait 2s | |
908 d_sleep(2); | |
909 | |
910 // stop the 2nd instance now | |
911 procTestB.stopThread(5000); | |
912 | |
913 #if DISTRHO_PLUGIN_HAS_UI | |
914 // start UI in the middle of this | |
915 { | |
916 UIExporter uiA(nullptr, 0, pluginA.getSampleRate(), | |
917 nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, | |
918 pluginA.getInstancePointer(), 0.0); | |
919 UIExporter uiB(nullptr, 0, pluginA.getSampleRate(), | |
920 nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, | |
921 pluginB.getInstancePointer(), 0.0); | |
922 UIExporter uiC(nullptr, 0, pluginA.getSampleRate(), | |
923 nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, | |
924 pluginC.getInstancePointer(), 0.0); | |
925 | |
926 // show UIs | |
927 uiB.showAndFocus(); | |
928 uiA.showAndFocus(); | |
929 uiC.showAndFocus(); | |
930 | |
931 // idle for 3s | |
932 for (int i=0; i<30; i++) | |
933 { | |
934 uiC.plugin_idle(); | |
935 uiB.plugin_idle(); | |
936 uiA.plugin_idle(); | |
937 d_msleep(100); | |
938 } | |
939 } | |
940 #endif | |
941 | |
942 procTestA.stopThread(5000); | |
943 procTestC.stopThread(5000); | |
944 } | |
945 | |
946 return true; | |
947 } | |
948 #endif // DPF_RUNTIME_TESTING | |
949 | |
950 END_NAMESPACE_DISTRHO | |
951 | |
952 // ----------------------------------------------------------------------- | |
953 | |
954 int main(int argc, char* argv[]) | |
955 { | |
956 USE_NAMESPACE_DISTRHO; | |
957 | |
958 initSignalHandler(); | |
959 | |
960 #ifndef STATIC_BUILD | |
961 // find plugin bundle | |
962 static String bundlePath; | |
963 if (bundlePath.isEmpty()) | |
964 { | |
965 String tmpPath(getBinaryFilename()); | |
966 tmpPath.truncate(tmpPath.rfind(DISTRHO_OS_SEP)); | |
967 #if defined(DISTRHO_OS_MAC) | |
968 if (tmpPath.endsWith("/MacOS")) | |
969 { | |
970 tmpPath.truncate(tmpPath.rfind('/')); | |
971 if (tmpPath.endsWith("/Contents")) | |
972 { | |
973 tmpPath.truncate(tmpPath.rfind('/')); | |
974 bundlePath = tmpPath; | |
975 d_nextBundlePath = bundlePath.buffer(); | |
976 } | |
977 } | |
978 #else | |
979 #ifdef DISTRHO_OS_WINDOWS | |
980 const DWORD attr = GetFileAttributesA(tmpPath + DISTRHO_OS_SEP_STR "resources"); | |
981 if (attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY) != 0) | |
982 #else | |
983 if (access(tmpPath + DISTRHO_OS_SEP_STR "resources", F_OK) == 0) | |
984 #endif | |
985 { | |
986 bundlePath = tmpPath; | |
987 d_nextBundlePath = bundlePath.buffer(); | |
988 } | |
989 #endif | |
990 } | |
991 #endif | |
992 | |
993 if (argc == 2 && std::strcmp(argv[1], "selftest") == 0) | |
994 { | |
995 #ifdef DPF_RUNTIME_TESTING | |
996 return runSelfTests() ? 0 : 1; | |
997 #else | |
998 d_stderr2("Code was built without DPF_RUNTIME_TESTING macro enabled, selftest option is not available"); | |
999 return 1; | |
1000 #endif | |
1001 } | |
1002 | |
1003 #if defined(DISTRHO_OS_WINDOWS) && DISTRHO_PLUGIN_HAS_UI | |
1004 /* the code below is based on | |
1005 * https://www.tillett.info/2013/05/13/how-to-create-a-windows-program-that-works-as-both-as-a-gui-and-console-application/ | |
1006 */ | |
1007 bool hasConsole = false; | |
1008 | |
1009 HANDLE consoleHandleOut, consoleHandleError; | |
1010 | |
1011 if (AttachConsole(ATTACH_PARENT_PROCESS)) | |
1012 { | |
1013 // Redirect unbuffered STDOUT to the console | |
1014 consoleHandleOut = GetStdHandle(STD_OUTPUT_HANDLE); | |
1015 if (consoleHandleOut != INVALID_HANDLE_VALUE) | |
1016 { | |
1017 freopen("CONOUT$", "w", stdout); | |
1018 setvbuf(stdout, NULL, _IONBF, 0); | |
1019 } | |
1020 | |
1021 // Redirect unbuffered STDERR to the console | |
1022 consoleHandleError = GetStdHandle(STD_ERROR_HANDLE); | |
1023 if (consoleHandleError != INVALID_HANDLE_VALUE) | |
1024 { | |
1025 freopen("CONOUT$", "w", stderr); | |
1026 setvbuf(stderr, NULL, _IONBF, 0); | |
1027 } | |
1028 | |
1029 hasConsole = true; | |
1030 } | |
1031 #endif | |
1032 | |
1033 jack_status_t status = jack_status_t(0x0); | |
1034 jack_client_t* client = jackbridge_client_open(DISTRHO_PLUGIN_NAME, JackNoStartServer, &status); | |
1035 | |
1036 #ifdef HAVE_JACK | |
1037 #define STANDALONE_NAME "JACK client" | |
1038 #else | |
1039 #define STANDALONE_NAME "Native audio driver" | |
1040 #endif | |
1041 | |
1042 if (client == nullptr) | |
1043 { | |
1044 String errorString; | |
1045 | |
1046 if (status & JackFailure) | |
1047 errorString += "Overall operation failed;\n"; | |
1048 if (status & JackInvalidOption) | |
1049 errorString += "The operation contained an invalid or unsupported option;\n"; | |
1050 if (status & JackNameNotUnique) | |
1051 errorString += "The desired client name was not unique;\n"; | |
1052 if (status & JackServerStarted) | |
1053 errorString += "The JACK server was started as a result of this operation;\n"; | |
1054 if (status & JackServerFailed) | |
1055 errorString += "Unable to connect to the JACK server;\n"; | |
1056 if (status & JackServerError) | |
1057 errorString += "Communication error with the JACK server;\n"; | |
1058 if (status & JackNoSuchClient) | |
1059 errorString += "Requested client does not exist;\n"; | |
1060 if (status & JackLoadFailure) | |
1061 errorString += "Unable to load internal client;\n"; | |
1062 if (status & JackInitFailure) | |
1063 errorString += "Unable to initialize client;\n"; | |
1064 if (status & JackShmFailure) | |
1065 errorString += "Unable to access shared memory;\n"; | |
1066 if (status & JackVersionError) | |
1067 errorString += "Client's protocol version does not match;\n"; | |
1068 if (status & JackBackendError) | |
1069 errorString += "Backend Error;\n"; | |
1070 if (status & JackClientZombie) | |
1071 errorString += "Client is being shutdown against its will;\n"; | |
1072 if (status & JackBridgeNativeFailed) | |
1073 errorString += "Native audio driver was unable to start;\n"; | |
1074 | |
1075 if (errorString.isNotEmpty()) | |
1076 { | |
1077 errorString[errorString.length()-2] = '.'; | |
1078 d_stderr("Failed to create the " STANDALONE_NAME ", reason was:\n%s", errorString.buffer()); | |
1079 } | |
1080 else | |
1081 d_stderr("Failed to create the " STANDALONE_NAME ", cannot continue!"); | |
1082 | |
1083 #if defined(DISTRHO_OS_MAC) | |
1084 CFStringRef errorTitleRef = CFStringCreateWithCString(nullptr, | |
1085 DISTRHO_PLUGIN_NAME ": Error", kCFStringEncodingUTF8); | |
1086 CFStringRef errorStringRef = CFStringCreateWithCString(nullptr, | |
1087 String("Failed to create " STANDALONE_NAME ", reason was:\n" + errorString).buffer(), kCFStringEncodingUTF8); | |
1088 | |
1089 CFUserNotificationDisplayAlert(0, kCFUserNotificationCautionAlertLevel, | |
1090 nullptr, nullptr, nullptr, | |
1091 errorTitleRef, errorStringRef, | |
1092 nullptr, nullptr, nullptr, nullptr); | |
1093 #elif defined(DISTRHO_OS_WINDOWS) && DISTRHO_PLUGIN_HAS_UI | |
1094 // make sure message box is high-dpi aware | |
1095 if (const HMODULE user32 = LoadLibrary("user32.dll")) | |
1096 { | |
1097 typedef BOOL(WINAPI* SPDA)(void); | |
1098 #if defined(__GNUC__) && (__GNUC__ >= 9) | |
1099 # pragma GCC diagnostic push | |
1100 # pragma GCC diagnostic ignored "-Wcast-function-type" | |
1101 #endif | |
1102 const SPDA SetProcessDPIAware = (SPDA)GetProcAddress(user32, "SetProcessDPIAware"); | |
1103 #if defined(__GNUC__) && (__GNUC__ >= 9) | |
1104 # pragma GCC diagnostic pop | |
1105 #endif | |
1106 if (SetProcessDPIAware) | |
1107 SetProcessDPIAware(); | |
1108 FreeLibrary(user32); | |
1109 } | |
1110 | |
1111 const String win32error = "Failed to create " STANDALONE_NAME ", reason was:\n" + errorString; | |
1112 MessageBoxA(nullptr, win32error.buffer(), "", MB_ICONERROR); | |
1113 #endif | |
1114 | |
1115 return 1; | |
1116 } | |
1117 | |
1118 d_nextBufferSize = jackbridge_get_buffer_size(client); | |
1119 d_nextSampleRate = jackbridge_get_sample_rate(client); | |
1120 d_nextCanRequestParameterValueChanges = true; | |
1121 | |
1122 uintptr_t winId = 0; | |
1123 #if DISTRHO_PLUGIN_HAS_UI | |
1124 if (argc == 3 && std::strcmp(argv[1], "embed") == 0) | |
1125 winId = static_cast<uintptr_t>(std::atoll(argv[2])); | |
1126 #endif | |
1127 | |
1128 const PluginJack p(client, winId); | |
1129 | |
1130 #if defined(DISTRHO_OS_WINDOWS) && DISTRHO_PLUGIN_HAS_UI | |
1131 /* the code below is based on | |
1132 * https://www.tillett.info/2013/05/13/how-to-create-a-windows-program-that-works-as-both-as-a-gui-and-console-application/ | |
1133 */ | |
1134 | |
1135 // Send "enter" to release application from the console | |
1136 // This is a hack, but if not used the console doesn't know the application has | |
1137 // returned. The "enter" key only sent if the console window is in focus. | |
1138 if (hasConsole && (GetConsoleWindow() == GetForegroundWindow() || SetFocus(GetConsoleWindow()) != nullptr)) | |
1139 { | |
1140 INPUT ip; | |
1141 // Set up a generic keyboard event. | |
1142 ip.type = INPUT_KEYBOARD; | |
1143 ip.ki.wScan = 0; // hardware scan code for key | |
1144 ip.ki.time = 0; | |
1145 ip.ki.dwExtraInfo = 0; | |
1146 | |
1147 // Send the "Enter" key | |
1148 ip.ki.wVk = 0x0D; // virtual-key code for the "Enter" key | |
1149 ip.ki.dwFlags = 0; // 0 for key press | |
1150 SendInput(1, &ip, sizeof(INPUT)); | |
1151 | |
1152 // Release the "Enter" key | |
1153 ip.ki.dwFlags = KEYEVENTF_KEYUP; // KEYEVENTF_KEYUP for key release | |
1154 SendInput(1, &ip, sizeof(INPUT)); | |
1155 } | |
1156 #endif | |
1157 | |
1158 return 0; | |
1159 } | |
1160 | |
1161 // ----------------------------------------------------------------------- |