1 /* 2 * DISTRHO Plugin Framework (DPF) 3 * Copyright (C) 2012-2021 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 #if DISTRHO_PLUGIN_HAS_UI 20 # include "DistrhoUIInternal.hpp" 21 #endif 22 23 #include "CarlaNative.hpp" 24 25 // TODO 26 #undef DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST 27 #define DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST 0 28 29 // ----------------------------------------------------------------------- 30 31 START_NAMESPACE_DISTRHO 32 33 #if ! DISTRHO_PLUGIN_WANT_MIDI_OUTPUT 34 static const writeMidiFunc writeMidiCallback = nullptr; 35 #endif 36 #if ! DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST 37 static const requestParameterValueChangeFunc requestParameterValueChangeCallback = nullptr; 38 #endif 39 40 #if DISTRHO_PLUGIN_HAS_UI 41 // ----------------------------------------------------------------------- 42 // Carla UI 43 44 #if ! DISTRHO_PLUGIN_WANT_STATE 45 static const setStateFunc setStateCallback = nullptr; 46 #endif 47 #if ! DISTRHO_PLUGIN_IS_SYNTH 48 static const sendNoteFunc sendNoteCallback = nullptr; 49 #endif 50 51 class UICarla 52 { 53 public: UICarla(const NativeHostDescriptor * const host,PluginExporter * const plugin)54 UICarla(const NativeHostDescriptor* const host, PluginExporter* const plugin) 55 : fHost(host), 56 fUI(this, 0, editParameterCallback, setParameterCallback, setStateCallback, sendNoteCallback, setSizeCallback, plugin->getInstancePointer()) 57 { 58 fUI.setWindowTitle(host->uiName); 59 60 if (host->uiParentId != 0) 61 fUI.setWindowTransientWinId(host->uiParentId); 62 } 63 ~UICarla()64 ~UICarla() 65 { 66 fUI.quit(); 67 } 68 69 // --------------------------------------------- 70 carla_show(const bool yesNo)71 void carla_show(const bool yesNo) 72 { 73 fUI.setWindowVisible(yesNo); 74 } 75 carla_idle()76 bool carla_idle() 77 { 78 return fUI.idle(); 79 } 80 carla_setParameterValue(const uint32_t index,const float value)81 void carla_setParameterValue(const uint32_t index, const float value) 82 { 83 fUI.parameterChanged(index, value); 84 } 85 86 #if DISTRHO_PLUGIN_WANT_PROGRAMS carla_setMidiProgram(const uint32_t realProgram)87 void carla_setMidiProgram(const uint32_t realProgram) 88 { 89 fUI.programLoaded(realProgram); 90 } 91 #endif 92 93 #if DISTRHO_PLUGIN_WANT_STATE carla_setCustomData(const char * const key,const char * const value)94 void carla_setCustomData(const char* const key, const char* const value) 95 { 96 fUI.stateChanged(key, value); 97 } 98 #endif 99 carla_setUiTitle(const char * const uiTitle)100 void carla_setUiTitle(const char* const uiTitle) 101 { 102 fUI.setWindowTitle(uiTitle); 103 } 104 105 // --------------------------------------------- 106 107 protected: handleEditParameter(const uint32_t,const bool)108 void handleEditParameter(const uint32_t, const bool) 109 { 110 // TODO 111 } 112 handleSetParameterValue(const uint32_t rindex,const float value)113 void handleSetParameterValue(const uint32_t rindex, const float value) 114 { 115 fHost->ui_parameter_changed(fHost->handle, rindex, value); 116 } 117 118 #if DISTRHO_PLUGIN_WANT_STATE handleSetState(const char * const key,const char * const value)119 void handleSetState(const char* const key, const char* const value) 120 { 121 fHost->ui_custom_data_changed(fHost->handle, key, value); 122 } 123 #endif 124 125 #if DISTRHO_PLUGIN_IS_SYNTH handleSendNote(const uint8_t,const uint8_t,const uint8_t)126 void handleSendNote(const uint8_t, const uint8_t, const uint8_t) 127 { 128 // TODO 129 } 130 #endif 131 handleSetSize(const uint width,const uint height)132 void handleSetSize(const uint width, const uint height) 133 { 134 fUI.setWindowSize(width, height); 135 } 136 137 // --------------------------------------------- 138 139 private: 140 // Plugin stuff 141 const NativeHostDescriptor* const fHost; 142 143 // UI 144 UIExporter fUI; 145 146 // --------------------------------------------- 147 // Callbacks 148 149 #define handlePtr ((UICarla*)ptr) 150 editParameterCallback(void * ptr,uint32_t index,bool started)151 static void editParameterCallback(void* ptr, uint32_t index, bool started) 152 { 153 handlePtr->handleEditParameter(index, started); 154 } 155 setParameterCallback(void * ptr,uint32_t rindex,float value)156 static void setParameterCallback(void* ptr, uint32_t rindex, float value) 157 { 158 handlePtr->handleSetParameterValue(rindex, value); 159 } 160 161 #if DISTRHO_PLUGIN_WANT_STATE setStateCallback(void * ptr,const char * key,const char * value)162 static void setStateCallback(void* ptr, const char* key, const char* value) 163 { 164 handlePtr->handleSetState(key, value); 165 } 166 #endif 167 168 #if DISTRHO_PLUGIN_IS_SYNTH sendNoteCallback(void * ptr,uint8_t channel,uint8_t note,uint8_t velocity)169 static void sendNoteCallback(void* ptr, uint8_t channel, uint8_t note, uint8_t velocity) 170 { 171 handlePtr->handleSendNote(channel, note, velocity); 172 } 173 #endif 174 setSizeCallback(void * ptr,uint width,uint height)175 static void setSizeCallback(void* ptr, uint width, uint height) 176 { 177 handlePtr->handleSetSize(width, height); 178 } 179 180 #undef handlePtr 181 182 CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(UICarla) 183 }; 184 #endif // DISTRHO_PLUGIN_HAS_UI 185 186 // ----------------------------------------------------------------------- 187 // Carla Plugin 188 189 class PluginCarla : public NativePluginClass 190 { 191 public: PluginCarla(const NativeHostDescriptor * const host)192 PluginCarla(const NativeHostDescriptor* const host) 193 : NativePluginClass(host), 194 fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback), 195 fScalePointsCache(nullptr) 196 { 197 #if DISTRHO_PLUGIN_HAS_UI 198 fUiPtr = nullptr; 199 #endif 200 } 201 ~PluginCarla()202 ~PluginCarla() override 203 { 204 #if DISTRHO_PLUGIN_HAS_UI 205 if (fUiPtr != nullptr) 206 { 207 delete fUiPtr; 208 fUiPtr = nullptr; 209 } 210 #endif 211 212 if (fScalePointsCache != nullptr) 213 { 214 delete[] fScalePointsCache; 215 fScalePointsCache = nullptr; 216 } 217 } 218 219 protected: 220 // ------------------------------------------------------------------- 221 // Plugin parameter calls 222 getParameterCount() const223 uint32_t getParameterCount() const override 224 { 225 return fPlugin.getParameterCount(); 226 } 227 getParameterInfo(const uint32_t index) const228 const NativeParameter* getParameterInfo(const uint32_t index) const override 229 { 230 CARLA_SAFE_ASSERT_RETURN(index < getParameterCount(), nullptr); 231 232 static NativeParameter param; 233 234 param.scalePointCount = 0; 235 param.scalePoints = nullptr; 236 237 { 238 int nativeParamHints = ::NATIVE_PARAMETER_IS_ENABLED; 239 const uint32_t paramHints = fPlugin.getParameterHints(index); 240 241 if (paramHints & kParameterIsAutomable) 242 nativeParamHints |= ::NATIVE_PARAMETER_IS_AUTOMABLE; 243 if (paramHints & kParameterIsBoolean) 244 nativeParamHints |= ::NATIVE_PARAMETER_IS_BOOLEAN; 245 if (paramHints & kParameterIsInteger) 246 nativeParamHints |= ::NATIVE_PARAMETER_IS_INTEGER; 247 if (paramHints & kParameterIsLogarithmic) 248 nativeParamHints |= ::NATIVE_PARAMETER_IS_LOGARITHMIC; 249 if (paramHints & kParameterIsOutput) 250 nativeParamHints |= ::NATIVE_PARAMETER_IS_OUTPUT; 251 252 param.hints = static_cast<NativeParameterHints>(nativeParamHints); 253 } 254 255 param.name = fPlugin.getParameterName(index); 256 param.unit = fPlugin.getParameterUnit(index); 257 258 { 259 const ParameterRanges& ranges(fPlugin.getParameterRanges(index)); 260 261 param.ranges.def = ranges.def; 262 param.ranges.min = ranges.min; 263 param.ranges.max = ranges.max; 264 } 265 266 { 267 const ParameterEnumerationValues& enumValues(fPlugin.getParameterEnumValues(index)); 268 269 if (const uint32_t scalePointCount = enumValues.count) 270 { 271 NativeParameterScalePoint* const scalePoints = new NativeParameterScalePoint[scalePointCount]; 272 273 for (uint32_t i=0; i<scalePointCount; ++i) 274 { 275 scalePoints[i].label = enumValues.values[i].label.buffer(); 276 scalePoints[i].value = enumValues.values[i].value; 277 } 278 279 param.scalePoints = scalePoints; 280 param.scalePointCount = scalePointCount; 281 282 if (enumValues.restrictedMode) 283 param.hints = static_cast<NativeParameterHints>(param.hints|::NATIVE_PARAMETER_USES_SCALEPOINTS); 284 } 285 else if (fScalePointsCache != nullptr) 286 { 287 delete[] fScalePointsCache; 288 fScalePointsCache = nullptr; 289 } 290 } 291 292 return ¶m; 293 } 294 getParameterValue(const uint32_t index) const295 float getParameterValue(const uint32_t index) const override 296 { 297 CARLA_SAFE_ASSERT_RETURN(index < getParameterCount(), 0.0f); 298 299 return fPlugin.getParameterValue(index); 300 } 301 302 // ------------------------------------------------------------------- 303 // Plugin midi-program calls 304 305 #if DISTRHO_PLUGIN_WANT_PROGRAMS getMidiProgramCount() const306 uint32_t getMidiProgramCount() const override 307 { 308 return fPlugin.getProgramCount(); 309 } 310 getMidiProgramInfo(const uint32_t index) const311 const NativeMidiProgram* getMidiProgramInfo(const uint32_t index) const override 312 { 313 CARLA_SAFE_ASSERT_RETURN(index < getMidiProgramCount(), nullptr); 314 315 static NativeMidiProgram midiProgram; 316 317 midiProgram.bank = index / 128; 318 midiProgram.program = index % 128; 319 midiProgram.name = fPlugin.getProgramName(index); 320 321 return &midiProgram; 322 } 323 #endif 324 325 // ------------------------------------------------------------------- 326 // Plugin state calls 327 setParameterValue(const uint32_t index,const float value)328 void setParameterValue(const uint32_t index, const float value) override 329 { 330 CARLA_SAFE_ASSERT_RETURN(index < getParameterCount(),); 331 332 fPlugin.setParameterValue(index, value); 333 } 334 335 #if DISTRHO_PLUGIN_WANT_PROGRAMS setMidiProgram(const uint8_t,const uint32_t bank,const uint32_t program)336 void setMidiProgram(const uint8_t, const uint32_t bank, const uint32_t program) override 337 { 338 const uint32_t realProgram(bank * 128 + program); 339 340 CARLA_SAFE_ASSERT_RETURN(realProgram < getMidiProgramCount(),); 341 342 fPlugin.loadProgram(realProgram); 343 } 344 #endif 345 346 #if DISTRHO_PLUGIN_WANT_STATE setCustomData(const char * const key,const char * const value)347 void setCustomData(const char* const key, const char* const value) override 348 { 349 CARLA_SAFE_ASSERT_RETURN(key != nullptr && key[0] != '\0',); 350 CARLA_SAFE_ASSERT_RETURN(value != nullptr,); 351 352 fPlugin.setState(key, value); 353 } 354 #endif 355 356 // ------------------------------------------------------------------- 357 // Plugin process calls 358 activate()359 void activate() override 360 { 361 fPlugin.activate(); 362 } 363 deactivate()364 void deactivate() override 365 { 366 fPlugin.deactivate(); 367 } 368 369 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT process(float ** const inBuffer,float ** const outBuffer,const uint32_t frames,const NativeMidiEvent * const midiEvents,const uint32_t midiEventCount)370 void process(float** const inBuffer, float** const outBuffer, const uint32_t frames, const NativeMidiEvent* const midiEvents, const uint32_t midiEventCount) override 371 { 372 MidiEvent realMidiEvents[midiEventCount]; 373 374 for (uint32_t i=0; i < midiEventCount; ++i) 375 { 376 const NativeMidiEvent& midiEvent(midiEvents[i]); 377 MidiEvent& realMidiEvent(realMidiEvents[i]); 378 379 realMidiEvent.frame = midiEvent.time; 380 realMidiEvent.size = midiEvent.size; 381 382 uint8_t j=0; 383 for (; j<midiEvent.size; ++j) 384 realMidiEvent.data[j] = midiEvent.data[j]; 385 for (; j<midiEvent.size; ++j) 386 realMidiEvent.data[j] = midiEvent.data[j]; 387 388 realMidiEvent.dataExt = nullptr; 389 } 390 391 fPlugin.run(const_cast<const float**>(inBuffer), outBuffer, frames, realMidiEvents, midiEventCount); 392 } 393 #else process(float ** const inBuffer,float ** const outBuffer,const uint32_t frames,const NativeMidiEvent * const,const uint32_t)394 void process(float** const inBuffer, float** const outBuffer, const uint32_t frames, const NativeMidiEvent* const, const uint32_t) override 395 { 396 fPlugin.run(const_cast<const float**>(inBuffer), outBuffer, frames); 397 } 398 #endif 399 400 // ------------------------------------------------------------------- 401 // Plugin UI calls 402 403 #if DISTRHO_PLUGIN_HAS_UI uiShow(const bool show)404 void uiShow(const bool show) override 405 { 406 if (show) 407 { 408 createUiIfNeeded(); 409 CARLA_SAFE_ASSERT_RETURN(fUiPtr != nullptr,); 410 411 fUiPtr->carla_show(show); 412 } 413 else if (fUiPtr != nullptr) 414 { 415 delete fUiPtr; 416 fUiPtr = nullptr; 417 } 418 } 419 uiIdle()420 void uiIdle() override 421 { 422 CARLA_SAFE_ASSERT_RETURN(fUiPtr != nullptr,); 423 424 if (! fUiPtr->carla_idle()) 425 { 426 uiClosed(); 427 428 delete fUiPtr; 429 fUiPtr = nullptr; 430 } 431 } 432 uiSetParameterValue(const uint32_t index,const float value)433 void uiSetParameterValue(const uint32_t index, const float value) override 434 { 435 CARLA_SAFE_ASSERT_RETURN(fUiPtr != nullptr,); 436 CARLA_SAFE_ASSERT_RETURN(index < getParameterCount(),); 437 438 fUiPtr->carla_setParameterValue(index, value); 439 } 440 441 # if DISTRHO_PLUGIN_WANT_PROGRAMS uiSetMidiProgram(const uint8_t,const uint32_t bank,const uint32_t program)442 void uiSetMidiProgram(const uint8_t, const uint32_t bank, const uint32_t program) override 443 { 444 CARLA_SAFE_ASSERT_RETURN(fUiPtr != nullptr,); 445 446 const uint32_t realProgram(bank * 128 + program); 447 448 CARLA_SAFE_ASSERT_RETURN(realProgram < getMidiProgramCount(),); 449 450 fUiPtr->carla_setMidiProgram(realProgram); 451 } 452 # endif 453 454 # if DISTRHO_PLUGIN_WANT_STATE uiSetCustomData(const char * const key,const char * const value)455 void uiSetCustomData(const char* const key, const char* const value) override 456 { 457 CARLA_SAFE_ASSERT_RETURN(fUiPtr != nullptr,); 458 CARLA_SAFE_ASSERT_RETURN(key != nullptr && key[0] != '\0',); 459 CARLA_SAFE_ASSERT_RETURN(value != nullptr,); 460 461 fUiPtr->carla_setCustomData(key, value); 462 } 463 # endif 464 #endif 465 466 // ------------------------------------------------------------------- 467 // Plugin dispatcher calls 468 bufferSizeChanged(const uint32_t bufferSize)469 void bufferSizeChanged(const uint32_t bufferSize) override 470 { 471 fPlugin.setBufferSize(bufferSize, true); 472 } 473 sampleRateChanged(const double sampleRate)474 void sampleRateChanged(const double sampleRate) override 475 { 476 fPlugin.setSampleRate(sampleRate, true); 477 } 478 479 #if DISTRHO_PLUGIN_HAS_UI uiNameChanged(const char * const uiName)480 void uiNameChanged(const char* const uiName) override 481 { 482 CARLA_SAFE_ASSERT_RETURN(fUiPtr != nullptr,); 483 484 fUiPtr->carla_setUiTitle(uiName); 485 } 486 #endif 487 488 // ------------------------------------------------------------------- 489 490 private: 491 PluginExporter fPlugin; 492 mutable NativeParameterScalePoint* fScalePointsCache; 493 494 #if DISTRHO_PLUGIN_HAS_UI 495 // UI 496 UICarla* fUiPtr; 497 createUiIfNeeded()498 void createUiIfNeeded() 499 { 500 if (fUiPtr == nullptr) 501 { 502 d_lastUiSampleRate = getSampleRate(); 503 fUiPtr = new UICarla(getHostHandle(), &fPlugin); 504 } 505 } 506 #endif 507 508 #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT writeMidiCallback(void * ptr,const MidiEvent & midiEvent)509 static bool writeMidiCallback(void* ptr, const MidiEvent& midiEvent) 510 { 511 if (midiEvent.size > 4) 512 return; 513 514 const NativeMidiEvent event = { 515 midiEvent.frame, 0, midiEvent.size, midiEvent.data 516 }; 517 518 return ((PluginCarla*)ptr)->fPlugin.writeMidiEvent(midiEvent); 519 } 520 #endif 521 522 #if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST requestParameterValueChange(const uint32_t index,const float value)523 bool requestParameterValueChange(const uint32_t index, const float value) 524 { 525 // TODO implementation 526 return false; 527 } 528 requestParameterValueChangeCallback(void * ptr,const uint32_t index,const float value)529 static bool requestParameterValueChangeCallback(void* ptr, const uint32_t index, const float value) 530 { 531 return thisPtr->requestParameterValueChange(index, value); 532 } 533 #endif 534 535 CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginCarla) 536 537 // ------------------------------------------------------------------- 538 539 public: _instantiate(const NativeHostDescriptor * host)540 static NativePluginHandle _instantiate(const NativeHostDescriptor* host) 541 { 542 d_lastBufferSize = host->get_buffer_size(host->handle); 543 d_lastSampleRate = host->get_sample_rate(host->handle); 544 return new PluginCarla(host); 545 } 546 _cleanup(NativePluginHandle handle)547 static void _cleanup(NativePluginHandle handle) 548 { 549 delete (PluginCarla*)handle; 550 } 551 }; 552 553 END_NAMESPACE_DISTRHO 554 555 // ----------------------------------------------------------------------- 556