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