Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1360 lines
43KB

  1. /*
  2. * Carla Native Plugins
  3. * Copyright (C) 2013 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 General Public License as
  7. * published by the Free Software Foundation; either version 2 of
  8. * the License, or any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * For a full copy of the GNU General Public License see the doc/GPL.txt file.
  16. */
  17. #define CARLA_NATIVE_PLUGIN_LV2
  18. #include "carla-native-base.cpp"
  19. #include "juce_audio_basics.h"
  20. #include "CarlaString.hpp"
  21. #include "lv2/atom.h"
  22. #include "lv2/atom-util.h"
  23. #include "lv2/buf-size.h"
  24. #include "lv2/instance-access.h"
  25. #include "lv2/midi.h"
  26. #include "lv2/options.h"
  27. #include "lv2/state.h"
  28. #include "lv2/time.h"
  29. #include "lv2/ui.h"
  30. #include "lv2/urid.h"
  31. #include "lv2/lv2_external_ui.h"
  32. #include "lv2/lv2_programs.h"
  33. using juce::FloatVectorOperations;
  34. // -----------------------------------------------------------------------
  35. // LV2 descriptor functions
  36. class NativePlugin : public LV2_External_UI_Widget
  37. {
  38. public:
  39. static const uint32_t kMaxMidiEvents = 512;
  40. NativePlugin(const PluginDescriptor* const desc, const double sampleRate, const char* const bundlePath, const LV2_Feature* const* features)
  41. : fHandle(nullptr),
  42. fDescriptor(desc),
  43. fMidiEventCount(0),
  44. fIsProcessing(false),
  45. fVolume(1.0f),
  46. fDryWet(1.0f),
  47. fBufferSize(0),
  48. fSampleRate(sampleRate),
  49. fUridMap(nullptr)
  50. {
  51. run = extui_run;
  52. show = extui_show;
  53. hide = extui_hide;
  54. CarlaString resourceDir(bundlePath);
  55. #ifdef CARLA_OS_WIN
  56. resourceDir += "\\resources\\";
  57. #else
  58. resourceDir += "/resources/";
  59. #endif
  60. fHost.handle = this;
  61. fHost.resourceDir = resourceDir.dup();
  62. fHost.uiName = nullptr;
  63. fHost.get_buffer_size = host_get_buffer_size;
  64. fHost.get_sample_rate = host_get_sample_rate;
  65. fHost.is_offline = host_is_offline;
  66. fHost.get_time_info = host_get_time_info;
  67. fHost.write_midi_event = host_write_midi_event;
  68. fHost.ui_parameter_changed = host_ui_parameter_changed;
  69. fHost.ui_custom_data_changed = host_ui_custom_data_changed;
  70. fHost.ui_closed = host_ui_closed;
  71. fHost.ui_open_file = host_ui_open_file;
  72. fHost.ui_save_file = host_ui_save_file;
  73. fHost.dispatcher = host_dispatcher;
  74. const LV2_Options_Option* options = nullptr;
  75. const LV2_URID_Map* uridMap = nullptr;
  76. const LV2_URID_Unmap* uridUnmap = nullptr;
  77. for (int i=0; features[i] != nullptr; ++i)
  78. {
  79. if (std::strcmp(features[i]->URI, LV2_OPTIONS__options) == 0)
  80. options = (const LV2_Options_Option*)features[i]->data;
  81. else if (std::strcmp(features[i]->URI, LV2_URID__map) == 0)
  82. uridMap = (const LV2_URID_Map*)features[i]->data;
  83. else if (std::strcmp(features[i]->URI, LV2_URID__unmap) == 0)
  84. uridUnmap = (const LV2_URID_Unmap*)features[i]->data;
  85. }
  86. if (options == nullptr || uridMap == nullptr)
  87. {
  88. carla_stderr("Host doesn't provides option or urid-map features");
  89. return;
  90. }
  91. for (int i=0; options[i].key != 0; ++i)
  92. {
  93. if (uridUnmap != nullptr)
  94. {
  95. carla_debug("Host option %i:\"%s\"", i, uridUnmap->unmap(uridUnmap->handle, options[i].key));
  96. }
  97. if (options[i].key == uridMap->map(uridMap->handle, LV2_BUF_SIZE__maxBlockLength))
  98. {
  99. if (options[i].type == uridMap->map(uridMap->handle, LV2_ATOM__Int))
  100. {
  101. fBufferSize = *(const int*)options[i].value;
  102. if (fBufferSize == 0)
  103. carla_stderr("Host provides maxBlockLength but has null value");
  104. }
  105. else
  106. carla_stderr("Host provides maxBlockLength but has wrong value type");
  107. break;
  108. }
  109. }
  110. fUridMap = uridMap;
  111. if (fDescriptor->midiIns > 0)
  112. fUI.portOffset += desc->midiIns;
  113. else if (fDescriptor->hints & PLUGIN_USES_TIME)
  114. fUI.portOffset += 1;
  115. fUI.portOffset += desc->midiOuts;
  116. fUI.portOffset += 1; // freewheel
  117. fUI.portOffset += desc->audioIns;
  118. fUI.portOffset += desc->audioOuts;
  119. }
  120. ~NativePlugin()
  121. {
  122. CARLA_ASSERT(fHandle == nullptr);
  123. if (fHost.resourceDir != nullptr)
  124. {
  125. delete[] fHost.resourceDir;
  126. fHost.resourceDir = nullptr;
  127. }
  128. }
  129. bool init()
  130. {
  131. if (fDescriptor->instantiate == nullptr || fDescriptor->process == nullptr)
  132. {
  133. carla_stderr("Plugin is missing something...");
  134. return false;
  135. }
  136. if (fBufferSize == 0)
  137. {
  138. carla_stderr("Host is missing bufferSize feature");
  139. //return false;
  140. // as testing, continue for now
  141. fBufferSize = 1024;
  142. }
  143. fHandle = fDescriptor->instantiate(&fHost);
  144. if (fHandle == nullptr)
  145. return false;
  146. carla_zeroStruct<MidiEvent>(fMidiEvents, kMaxMidiEvents*2);
  147. carla_zeroStruct<TimeInfo>(fTimeInfo);
  148. fPorts.init(fDescriptor, fHandle);
  149. fUris.map(fUridMap);
  150. return true;
  151. }
  152. // -------------------------------------------------------------------
  153. // LV2 functions
  154. void lv2_connect_port(const uint32_t port, void* const dataLocation)
  155. {
  156. fPorts.connectPort(fDescriptor, port, dataLocation);
  157. }
  158. void lv2_activate()
  159. {
  160. if (fDescriptor->activate != nullptr)
  161. fDescriptor->activate(fHandle);
  162. carla_zeroStruct<TimeInfo>(fTimeInfo);
  163. }
  164. void lv2_deactivate()
  165. {
  166. if (fDescriptor->deactivate != nullptr)
  167. fDescriptor->deactivate(fHandle);
  168. }
  169. void lv2_cleanup()
  170. {
  171. if (fDescriptor->cleanup != nullptr)
  172. fDescriptor->cleanup(fHandle);
  173. fHandle = nullptr;
  174. }
  175. void lv2_run(const uint32_t frames)
  176. {
  177. if (frames == 0)
  178. {
  179. updateParameterOutputs();
  180. return;
  181. }
  182. // Check for updated parameters
  183. float curValue;
  184. for (uint32_t i=0; i < fPorts.paramCount; ++i)
  185. {
  186. CARLA_SAFE_ASSERT_CONTINUE(fPorts.paramsPtr[i] != nullptr)
  187. curValue = *fPorts.paramsPtr[i];
  188. if (fPorts.paramsLast[i] != curValue && (fDescriptor->get_parameter_info(fHandle, i)->hints & PARAMETER_IS_OUTPUT) == 0)
  189. {
  190. fPorts.paramsLast[i] = curValue;
  191. fDescriptor->set_parameter_value(fHandle, i, curValue);
  192. }
  193. }
  194. if (fDescriptor->midiIns > 0 || (fDescriptor->hints & PLUGIN_USES_TIME) != 0)
  195. {
  196. fMidiEventCount = 0;
  197. carla_zeroStruct<MidiEvent>(fMidiEvents, kMaxMidiEvents*2);
  198. LV2_ATOM_SEQUENCE_FOREACH(fPorts.eventsIn[0], iter)
  199. {
  200. const LV2_Atom_Event* const event((const LV2_Atom_Event*)iter);
  201. if (event == nullptr)
  202. continue;
  203. if (event->body.size > 4)
  204. continue;
  205. if (event->time.frames >= frames)
  206. break;
  207. if (event->body.type == fUris.midiEvent)
  208. {
  209. if (fMidiEventCount >= kMaxMidiEvents*2)
  210. continue;
  211. const uint8_t* const data((const uint8_t*)(event + 1));
  212. fMidiEvents[fMidiEventCount].port = 0;
  213. fMidiEvents[fMidiEventCount].time = event->time.frames;
  214. fMidiEvents[fMidiEventCount].size = event->body.size;
  215. for (uint32_t i=0; i < event->body.size; ++i)
  216. fMidiEvents[fMidiEventCount].data[i] = data[i];
  217. fMidiEventCount += 1;
  218. continue;
  219. }
  220. if (event->body.type == fUris.atomBlank)
  221. {
  222. const LV2_Atom_Object* const obj((LV2_Atom_Object*)&event->body);
  223. if (obj->body.otype != fUris.timePos)
  224. continue;
  225. LV2_Atom* bar = nullptr;
  226. LV2_Atom* barBeat = nullptr;
  227. LV2_Atom* beatsPerBar = nullptr;
  228. LV2_Atom* bpm = nullptr;
  229. LV2_Atom* beatUnit = nullptr;
  230. LV2_Atom* frame = nullptr;
  231. LV2_Atom* speed = nullptr;
  232. lv2_atom_object_get(obj,
  233. fUris.timeBar, &bar,
  234. fUris.timeBarBeat, &barBeat,
  235. fUris.timeBeatsPerBar, &beatsPerBar,
  236. fUris.timeBeatsPerMinute, &bpm,
  237. fUris.timeBeatUnit, &beatUnit,
  238. fUris.timeFrame, &frame,
  239. fUris.timeSpeed, &speed,
  240. nullptr);
  241. if (bpm != nullptr && bpm->type == fUris.atomFloat)
  242. {
  243. fTimeInfo.bbt.beatsPerMinute = ((LV2_Atom_Float*)bpm)->body;
  244. fTimeInfo.bbt.valid = true;
  245. }
  246. if (beatsPerBar != nullptr && beatsPerBar->type == fUris.atomFloat)
  247. {
  248. float beatsPerBarValue = ((LV2_Atom_Float*)beatsPerBar)->body;
  249. fTimeInfo.bbt.beatsPerBar = beatsPerBarValue;
  250. if (bar != nullptr && bar->type == fUris.atomLong)
  251. {
  252. //float barValue = ((LV2_Atom_Long*)bar)->body;
  253. //curPosInfo.ppqPositionOfLastBarStart = barValue * beatsPerBarValue;
  254. if (barBeat != nullptr && barBeat->type == fUris.atomFloat)
  255. {
  256. //float barBeatValue = ((LV2_Atom_Float*)barBeat)->body;
  257. //curPosInfo.ppqPosition = curPosInfo.ppqPositionOfLastBarStart + barBeatValue;
  258. }
  259. }
  260. }
  261. if (beatUnit != nullptr && beatUnit->type == fUris.atomFloat)
  262. fTimeInfo.bbt.beatType = ((LV2_Atom_Float*)beatUnit)->body;
  263. if (frame != nullptr && frame->type == fUris.atomLong)
  264. fTimeInfo.frame = ((LV2_Atom_Long*)frame)->body;
  265. if (speed != nullptr && speed->type == fUris.atomFloat)
  266. fTimeInfo.playing = ((LV2_Atom_Float*)speed)->body == 1.0f;
  267. continue;
  268. }
  269. }
  270. for (uint32_t i=1; i < fDescriptor->midiIns; ++i)
  271. {
  272. LV2_ATOM_SEQUENCE_FOREACH(fPorts.eventsIn[i], iter)
  273. {
  274. const LV2_Atom_Event* const event((const LV2_Atom_Event*)iter);
  275. if (event == nullptr)
  276. continue;
  277. if (event->body.type != fUris.midiEvent)
  278. continue;
  279. if (event->body.size > 4)
  280. continue;
  281. if (event->time.frames >= frames)
  282. break;
  283. if (fMidiEventCount >= kMaxMidiEvents*2)
  284. break;
  285. const uint8_t* const data((const uint8_t*)(event + 1));
  286. fMidiEvents[fMidiEventCount].port = i;
  287. fMidiEvents[fMidiEventCount].time = event->time.frames;
  288. fMidiEvents[fMidiEventCount].size = event->body.size;
  289. for (uint32_t j=0; j < event->body.size; ++j)
  290. fMidiEvents[fMidiEventCount].data[j] = data[j];
  291. fMidiEventCount += 1;
  292. }
  293. }
  294. }
  295. fIsProcessing = true;
  296. fDescriptor->process(fHandle, fPorts.audioIns, fPorts.audioOuts, frames, fMidiEvents, fMidiEventCount);
  297. fIsProcessing = false;
  298. if (fDryWet != 1.0f && fDescriptor->audioIns == fDescriptor->audioOuts)
  299. {
  300. for (uint32_t i=0; i < fDescriptor->audioOuts; ++i)
  301. {
  302. FloatVectorOperations::multiply(fPorts.audioIns[i], fVolume*(1.0f-fDryWet), frames);
  303. FloatVectorOperations::multiply(fPorts.audioOuts[i], fVolume*fDryWet, frames);
  304. FloatVectorOperations::add(fPorts.audioOuts[i], fPorts.audioIns[i], frames);
  305. }
  306. }
  307. else if (fVolume != 1.0f)
  308. {
  309. for (uint32_t i=0; i < fDescriptor->audioOuts; ++i)
  310. FloatVectorOperations::multiply(fPorts.audioOuts[i], fVolume, frames);
  311. }
  312. // TODO - midi out
  313. updateParameterOutputs();
  314. }
  315. // -------------------------------------------------------------------
  316. uint32_t lv2_get_options(LV2_Options_Option* const /*options*/) const
  317. {
  318. // currently unused
  319. return LV2_OPTIONS_SUCCESS;
  320. }
  321. uint32_t lv2_set_options(const LV2_Options_Option* const options)
  322. {
  323. for (int i=0; options[i].key != 0; ++i)
  324. {
  325. if (options[i].key == fUridMap->map(fUridMap->handle, LV2_BUF_SIZE__maxBlockLength))
  326. {
  327. if (options[i].type == fUridMap->map(fUridMap->handle, LV2_ATOM__Int))
  328. {
  329. fBufferSize = *(const int*)options[i].value;
  330. if (fDescriptor->dispatcher != nullptr)
  331. fDescriptor->dispatcher(fHandle, PLUGIN_OPCODE_BUFFER_SIZE_CHANGED, 0, fBufferSize, nullptr, 0.0f);
  332. }
  333. else
  334. carla_stderr("Host changed maxBlockLength but with wrong value type");
  335. }
  336. else if (options[i].key == fUridMap->map(fUridMap->handle, LV2_CORE__sampleRate))
  337. {
  338. if (options[i].type == fUridMap->map(fUridMap->handle, LV2_ATOM__Double))
  339. {
  340. fSampleRate = *(const double*)options[i].value;
  341. if (fDescriptor->dispatcher != nullptr)
  342. fDescriptor->dispatcher(fHandle, PLUGIN_OPCODE_SAMPLE_RATE_CHANGED, 0, 0, nullptr, fSampleRate);
  343. }
  344. else
  345. carla_stderr("Host changed sampleRate but with wrong value type");
  346. }
  347. }
  348. return LV2_OPTIONS_SUCCESS;
  349. }
  350. const LV2_Program_Descriptor* lv2_get_program(const uint32_t index)
  351. {
  352. if (fDescriptor->category == PLUGIN_CATEGORY_SYNTH)
  353. return nullptr;
  354. if (fDescriptor->get_midi_program_count == nullptr)
  355. return nullptr;
  356. if (fDescriptor->get_midi_program_info == nullptr)
  357. return nullptr;
  358. if (index >= fDescriptor->get_midi_program_count(fHandle))
  359. return nullptr;
  360. const MidiProgram* const midiProg(fDescriptor->get_midi_program_info(fHandle, index));
  361. if (midiProg == nullptr)
  362. return nullptr;
  363. fProgramDesc.bank = midiProg->bank;
  364. fProgramDesc.program = midiProg->program;
  365. fProgramDesc.name = midiProg->name;
  366. return &fProgramDesc;
  367. }
  368. void lv2_select_program(uint32_t bank, uint32_t program)
  369. {
  370. if (fDescriptor->category == PLUGIN_CATEGORY_SYNTH)
  371. return;
  372. if (fDescriptor->set_midi_program == nullptr)
  373. return;
  374. fDescriptor->set_midi_program(fHandle, 0, bank, program);
  375. }
  376. LV2_State_Status lv2_save(const LV2_State_Store_Function store, const LV2_State_Handle handle, const uint32_t /*flags*/, const LV2_Feature* const* const /*features*/) const
  377. {
  378. if ((fDescriptor->hints & PLUGIN_USES_STATE) == 0 || fDescriptor->get_state == nullptr)
  379. return LV2_STATE_ERR_NO_FEATURE;
  380. if (char* const state = fDescriptor->get_state(fHandle))
  381. {
  382. store(handle, fUridMap->map(fUridMap->handle, "http://kxstudio.sf.net/ns/carla/chunk"), state, std::strlen(state), fUris.atomString, LV2_STATE_IS_POD|LV2_STATE_IS_PORTABLE);
  383. std::free(state);
  384. return LV2_STATE_SUCCESS;
  385. }
  386. return LV2_STATE_ERR_UNKNOWN;
  387. }
  388. LV2_State_Status lv2_restore(const LV2_State_Retrieve_Function retrieve, const LV2_State_Handle handle, uint32_t flags, const LV2_Feature* const* const /*features*/) const
  389. {
  390. if ((fDescriptor->hints & PLUGIN_USES_STATE) == 0 || fDescriptor->set_state == nullptr)
  391. return LV2_STATE_ERR_NO_FEATURE;
  392. size_t size = 0;
  393. uint32_t type = 0;
  394. const void* data = retrieve(handle, fUridMap->map(fUridMap->handle, "http://kxstudio.sf.net/ns/carla/chunk"), &size, &type, &flags);
  395. if (size == 0)
  396. return LV2_STATE_ERR_UNKNOWN;
  397. if (type == 0)
  398. return LV2_STATE_ERR_UNKNOWN;
  399. if (data == nullptr)
  400. return LV2_STATE_ERR_UNKNOWN;
  401. if (type != fUris.atomString)
  402. return LV2_STATE_ERR_BAD_TYPE;
  403. fDescriptor->set_state(fHandle, (const char*)data);
  404. return LV2_STATE_SUCCESS;
  405. }
  406. // -------------------------------------------------------------------
  407. bool lv2ui_instantiate(LV2UI_Write_Function writeFunction, LV2UI_Controller controller, LV2UI_Widget* widget, const LV2_Feature* const* features)
  408. {
  409. for (int i=0; features[i] != nullptr; ++i)
  410. {
  411. if (std::strcmp(features[i]->URI, LV2_EXTERNAL_UI__Host) == 0 ||
  412. std::strcmp(features[i]->URI, LV2_EXTERNAL_UI_DEPRECATED_URI) == 0)
  413. {
  414. fUI.host = (const LV2_External_UI_Host*)features[i]->data;
  415. break;
  416. }
  417. }
  418. if (fUI.host == nullptr)
  419. return false;
  420. fUI.writeFunction = writeFunction;
  421. fUI.controller = controller;
  422. *widget = this;
  423. fHost.uiName = fUI.host->plugin_human_id;
  424. return true;
  425. }
  426. void lv2ui_port_event(uint32_t portIndex, uint32_t bufferSize, uint32_t format, const void* buffer) const
  427. {
  428. if (format != 0 || bufferSize != sizeof(float) || buffer == nullptr)
  429. return;
  430. if (portIndex >= fUI.portOffset || ! fUI.isVisible)
  431. return;
  432. if (fDescriptor->ui_set_parameter_value == nullptr)
  433. return;
  434. const float value(*(const float*)buffer);
  435. fDescriptor->ui_set_parameter_value(fHandle, portIndex-fUI.portOffset, value);
  436. }
  437. void lv2ui_cleanup()
  438. {
  439. fUI.host = nullptr;
  440. fUI.writeFunction = nullptr;
  441. fUI.controller = nullptr;
  442. if (! fUI.isVisible)
  443. return;
  444. if (fDescriptor->ui_show != nullptr)
  445. fDescriptor->ui_show(fHandle, false);
  446. fUI.isVisible = false;
  447. }
  448. // -------------------------------------------------------------------
  449. void lv2ui_select_program(uint32_t bank, uint32_t program) const
  450. {
  451. if (fDescriptor->category == PLUGIN_CATEGORY_SYNTH)
  452. return;
  453. if (fDescriptor->ui_set_midi_program == nullptr)
  454. return;
  455. fDescriptor->ui_set_midi_program(fHandle, 0, bank, program);
  456. }
  457. // -------------------------------------------------------------------
  458. protected:
  459. void handleUiRun()
  460. {
  461. if (fDescriptor->ui_idle != nullptr)
  462. fDescriptor->ui_idle(fHandle);
  463. }
  464. void handleUiShow()
  465. {
  466. if (fDescriptor->ui_show != nullptr)
  467. fDescriptor->ui_show(fHandle, true);
  468. fUI.isVisible = true;
  469. }
  470. void handleUiHide()
  471. {
  472. if (fDescriptor->ui_show != nullptr)
  473. fDescriptor->ui_show(fHandle, false);
  474. fUI.isVisible = false;
  475. }
  476. // -------------------------------------------------------------------
  477. uint32_t handleGetBufferSize() const
  478. {
  479. return fBufferSize;
  480. }
  481. double handleGetSampleRate() const
  482. {
  483. return fSampleRate;
  484. }
  485. bool handleIsOffline() const
  486. {
  487. CARLA_SAFE_ASSERT_RETURN(fIsProcessing, false);
  488. return (fPorts.freewheel != nullptr && *fPorts.freewheel >= 0.5f);
  489. }
  490. const TimeInfo* handleGetTimeInfo() const
  491. {
  492. CARLA_SAFE_ASSERT_RETURN(fIsProcessing, nullptr);
  493. return &fTimeInfo;
  494. }
  495. bool handleWriteMidiEvent(const MidiEvent* const event)
  496. {
  497. CARLA_SAFE_ASSERT_RETURN(fIsProcessing, false);
  498. CARLA_SAFE_ASSERT_RETURN(fDescriptor->midiOuts > 0, false);
  499. CARLA_SAFE_ASSERT_RETURN(event != nullptr, false);
  500. CARLA_SAFE_ASSERT_RETURN(event->data[0] != 0, false);
  501. // reverse-find first free event, and put it there
  502. for (uint32_t i=(kMaxMidiEvents*2)-1; i > fMidiEventCount; --i)
  503. {
  504. if (fMidiEvents[i].data[0] == 0)
  505. {
  506. std::memcpy(&fMidiEvents[i], event, sizeof(MidiEvent));
  507. return true;
  508. }
  509. }
  510. return false;
  511. }
  512. void handleUiParameterChanged(const uint32_t index, const float value) const
  513. {
  514. if (fUI.writeFunction != nullptr && fUI.controller != nullptr)
  515. fUI.writeFunction(fUI.controller, index+fUI.portOffset, sizeof(float), 0, &value);
  516. }
  517. void handleUiCustomDataChanged(const char* const /*key*/, const char* const /*value*/) const
  518. {
  519. //storeCustomData(key, value);
  520. }
  521. void handleUiClosed()
  522. {
  523. if (fUI.host != nullptr && fUI.host->ui_closed != nullptr && fUI.controller != nullptr)
  524. fUI.host->ui_closed(fUI.controller);
  525. fUI.host = nullptr;
  526. fUI.writeFunction = nullptr;
  527. fUI.controller = nullptr;
  528. fUI.isVisible = false;
  529. }
  530. const char* handleUiOpenFile(const bool /*isDir*/, const char* const /*title*/, const char* const /*filter*/) const
  531. {
  532. // TODO
  533. return nullptr;
  534. }
  535. const char* handleUiSaveFile(const bool /*isDir*/, const char* const /*title*/, const char* const /*filter*/) const
  536. {
  537. // TODO
  538. return nullptr;
  539. }
  540. intptr_t handleDispatcher(const HostDispatcherOpcode opcode, const int32_t index, const intptr_t value, void* const ptr, const float opt)
  541. {
  542. carla_debug("NativePlugin::handleDispatcher(%i, %i, " P_INTPTR ", %p, %f)", opcode, index, value, ptr, opt);
  543. intptr_t ret = 0;
  544. switch (opcode)
  545. {
  546. case HOST_OPCODE_NULL:
  547. break;
  548. case HOST_OPCODE_SET_VOLUME:
  549. fVolume = opt;
  550. break;
  551. case HOST_OPCODE_SET_DRYWET:
  552. fDryWet = opt;
  553. break;
  554. case HOST_OPCODE_SET_BALANCE_LEFT:
  555. case HOST_OPCODE_SET_BALANCE_RIGHT:
  556. case HOST_OPCODE_SET_PANNING:
  557. // nothing
  558. break;
  559. case HOST_OPCODE_GET_PARAMETER_MIDI_CC:
  560. case HOST_OPCODE_SET_PARAMETER_MIDI_CC:
  561. case HOST_OPCODE_SET_PROCESS_PRECISION:
  562. case HOST_OPCODE_UPDATE_PARAMETER:
  563. case HOST_OPCODE_UPDATE_MIDI_PROGRAM:
  564. case HOST_OPCODE_RELOAD_PARAMETERS:
  565. case HOST_OPCODE_RELOAD_MIDI_PROGRAMS:
  566. case HOST_OPCODE_RELOAD_ALL:
  567. // nothing
  568. break;
  569. case HOST_OPCODE_UI_UNAVAILABLE:
  570. handleUiClosed();
  571. break;
  572. }
  573. return ret;
  574. // unused for now
  575. (void)index;
  576. (void)value;
  577. (void)ptr;
  578. }
  579. void updateParameterOutputs()
  580. {
  581. for (uint32_t i=0; i < fPorts.paramCount; ++i)
  582. {
  583. if (fDescriptor->get_parameter_info(fHandle, i)->hints & PARAMETER_IS_OUTPUT)
  584. {
  585. fPorts.paramsLast[i] = fDescriptor->get_parameter_value(fHandle, i);
  586. if (fPorts.paramsPtr[i] != nullptr)
  587. *fPorts.paramsPtr[i] = fPorts.paramsLast[i];
  588. }
  589. }
  590. }
  591. // -------------------------------------------------------------------
  592. private:
  593. // Native data
  594. PluginHandle fHandle;
  595. HostDescriptor fHost;
  596. const PluginDescriptor* const fDescriptor;
  597. LV2_Program_Descriptor fProgramDesc;
  598. uint32_t fMidiEventCount;
  599. MidiEvent fMidiEvents[kMaxMidiEvents*2];
  600. TimeInfo fTimeInfo;
  601. bool fIsProcessing;
  602. float fVolume;
  603. float fDryWet;
  604. // Lv2 host data
  605. uint32_t fBufferSize;
  606. double fSampleRate;
  607. const LV2_URID_Map* fUridMap;
  608. struct URIDs {
  609. LV2_URID atomBlank;
  610. LV2_URID atomFloat;
  611. LV2_URID atomLong;
  612. LV2_URID atomSequence;
  613. LV2_URID atomString;
  614. LV2_URID midiEvent;
  615. LV2_URID timePos;
  616. LV2_URID timeBar;
  617. LV2_URID timeBarBeat;
  618. LV2_URID timeBeatsPerBar;
  619. LV2_URID timeBeatsPerMinute;
  620. LV2_URID timeBeatUnit;
  621. LV2_URID timeFrame;
  622. LV2_URID timeSpeed;
  623. URIDs()
  624. : atomBlank(0),
  625. atomFloat(0),
  626. atomLong(0),
  627. atomSequence(0),
  628. atomString(0),
  629. midiEvent(0),
  630. timePos(0),
  631. timeBar(0),
  632. timeBarBeat(0),
  633. timeBeatsPerBar(0),
  634. timeBeatsPerMinute(0),
  635. timeBeatUnit(0),
  636. timeFrame(0),
  637. timeSpeed(0) {}
  638. void map(const LV2_URID_Map* const uridMap)
  639. {
  640. atomBlank = uridMap->map(uridMap->handle, LV2_ATOM__Blank);
  641. atomFloat = uridMap->map(uridMap->handle, LV2_ATOM__Float);
  642. atomLong = uridMap->map(uridMap->handle, LV2_ATOM__Long);
  643. atomSequence = uridMap->map(uridMap->handle, LV2_ATOM__Sequence);
  644. atomString = uridMap->map(uridMap->handle, LV2_ATOM__String);
  645. midiEvent = uridMap->map(uridMap->handle, LV2_MIDI__MidiEvent);
  646. timePos = uridMap->map(uridMap->handle, LV2_TIME__Position);
  647. timeBar = uridMap->map(uridMap->handle, LV2_TIME__bar);
  648. timeBarBeat = uridMap->map(uridMap->handle, LV2_TIME__barBeat);
  649. timeBeatUnit = uridMap->map(uridMap->handle, LV2_TIME__beatUnit);
  650. timeFrame = uridMap->map(uridMap->handle, LV2_TIME__frame);
  651. timeSpeed = uridMap->map(uridMap->handle, LV2_TIME__speed);
  652. timeBeatsPerBar = uridMap->map(uridMap->handle, LV2_TIME__beatsPerBar);
  653. timeBeatsPerMinute = uridMap->map(uridMap->handle, LV2_TIME__beatsPerMinute);
  654. }
  655. } fUris;
  656. struct UI {
  657. const LV2_External_UI_Host* host;
  658. LV2UI_Write_Function writeFunction;
  659. LV2UI_Controller controller;
  660. uint32_t portOffset;
  661. bool isVisible;
  662. UI()
  663. : host(nullptr),
  664. writeFunction(nullptr),
  665. controller(nullptr),
  666. portOffset(0),
  667. isVisible(false) {}
  668. } fUI;
  669. struct Ports {
  670. LV2_Atom_Sequence** eventsIn;
  671. LV2_Atom_Sequence** midiOuts;
  672. float** audioIns;
  673. float** audioOuts;
  674. float* freewheel;
  675. uint32_t paramCount;
  676. float* paramsLast;
  677. float** paramsPtr;
  678. Ports()
  679. : eventsIn(nullptr),
  680. midiOuts(nullptr),
  681. audioIns(nullptr),
  682. audioOuts(nullptr),
  683. freewheel(nullptr),
  684. paramCount(0),
  685. paramsLast(nullptr),
  686. paramsPtr(nullptr) {}
  687. ~Ports()
  688. {
  689. if (eventsIn != nullptr)
  690. {
  691. delete[] eventsIn;
  692. eventsIn = nullptr;
  693. }
  694. if (midiOuts != nullptr)
  695. {
  696. delete[] midiOuts;
  697. midiOuts = nullptr;
  698. }
  699. if (audioIns != nullptr)
  700. {
  701. delete[] audioIns;
  702. audioIns = nullptr;
  703. }
  704. if (audioOuts != nullptr)
  705. {
  706. delete[] audioOuts;
  707. audioOuts = nullptr;
  708. }
  709. if (paramsLast != nullptr)
  710. {
  711. delete[] paramsLast;
  712. paramsLast = nullptr;
  713. }
  714. if (paramsPtr != nullptr)
  715. {
  716. delete[] paramsPtr;
  717. paramsPtr = nullptr;
  718. }
  719. }
  720. void init(const PluginDescriptor* const desc, PluginHandle handle)
  721. {
  722. CARLA_SAFE_ASSERT_RETURN(desc != nullptr && handle != nullptr,)
  723. if (desc->midiIns > 0)
  724. {
  725. eventsIn = new LV2_Atom_Sequence*[desc->midiIns];
  726. for (uint32_t i=0; i < desc->midiIns; ++i)
  727. eventsIn[i] = nullptr;
  728. }
  729. else if (desc->hints & PLUGIN_USES_TIME)
  730. {
  731. eventsIn = new LV2_Atom_Sequence*[1];
  732. eventsIn[0] = nullptr;
  733. }
  734. if (desc->midiOuts > 0)
  735. {
  736. midiOuts = new LV2_Atom_Sequence*[desc->midiOuts];
  737. for (uint32_t i=0; i < desc->midiOuts; ++i)
  738. midiOuts[i] = nullptr;
  739. }
  740. if (desc->audioIns > 0)
  741. {
  742. audioIns = new float*[desc->audioIns];
  743. for (uint32_t i=0; i < desc->audioIns; ++i)
  744. audioIns[i] = nullptr;
  745. }
  746. if (desc->audioOuts > 0)
  747. {
  748. audioOuts = new float*[desc->audioOuts];
  749. for (uint32_t i=0; i < desc->audioOuts; ++i)
  750. audioOuts[i] = nullptr;
  751. }
  752. if (desc->get_parameter_count != nullptr && desc->get_parameter_info != nullptr && desc->get_parameter_value != nullptr && desc->set_parameter_value != nullptr)
  753. {
  754. paramCount = desc->get_parameter_count(handle);
  755. if (paramCount > 0)
  756. {
  757. paramsLast = new float[paramCount];
  758. paramsPtr = new float*[paramCount];
  759. for (uint32_t i=0; i < paramCount; ++i)
  760. {
  761. paramsLast[i] = desc->get_parameter_value(handle, i);
  762. paramsPtr[i] = nullptr;
  763. }
  764. }
  765. }
  766. }
  767. void connectPort(const PluginDescriptor* const desc, const uint32_t port, void* const dataLocation)
  768. {
  769. uint32_t index = 0;
  770. if (desc->midiIns > 0 || (desc->hints & PLUGIN_USES_TIME) != 0)
  771. {
  772. if (port == index++)
  773. {
  774. eventsIn[0] = (LV2_Atom_Sequence*)dataLocation;
  775. return;
  776. }
  777. }
  778. for (uint32_t i=1; i < desc->midiIns; ++i)
  779. {
  780. if (port == index++)
  781. {
  782. eventsIn[i] = (LV2_Atom_Sequence*)dataLocation;
  783. return;
  784. }
  785. }
  786. for (uint32_t i=0; i < desc->midiOuts; ++i)
  787. {
  788. if (port == index++)
  789. {
  790. midiOuts[i] = (LV2_Atom_Sequence*)dataLocation;
  791. return;
  792. }
  793. }
  794. if (port == index++)
  795. {
  796. freewheel = (float*)dataLocation;
  797. return;
  798. }
  799. for (uint32_t i=0; i < desc->audioIns; ++i)
  800. {
  801. if (port == index++)
  802. {
  803. audioIns[i] = (float*)dataLocation;
  804. return;
  805. }
  806. }
  807. for (uint32_t i=0; i < desc->audioOuts; ++i)
  808. {
  809. if (port == index++)
  810. {
  811. audioOuts[i] = (float*)dataLocation;
  812. return;
  813. }
  814. }
  815. for (uint32_t i=0; i < paramCount; ++i)
  816. {
  817. if (port == index++)
  818. {
  819. paramsPtr[i] = (float*)dataLocation;
  820. return;
  821. }
  822. }
  823. }
  824. } fPorts;
  825. // -------------------------------------------------------------------
  826. #define handlePtr ((NativePlugin*)_this_)
  827. static void extui_run(LV2_External_UI_Widget* _this_)
  828. {
  829. handlePtr->handleUiRun();
  830. }
  831. static void extui_show(LV2_External_UI_Widget* _this_)
  832. {
  833. handlePtr->handleUiShow();
  834. }
  835. static void extui_hide(LV2_External_UI_Widget* _this_)
  836. {
  837. handlePtr->handleUiHide();
  838. }
  839. #undef handlePtr
  840. // -------------------------------------------------------------------
  841. #define handlePtr ((NativePlugin*)handle)
  842. static uint32_t host_get_buffer_size(HostHandle handle)
  843. {
  844. return handlePtr->handleGetBufferSize();
  845. }
  846. static double host_get_sample_rate(HostHandle handle)
  847. {
  848. return handlePtr->handleGetSampleRate();
  849. }
  850. static bool host_is_offline(HostHandle handle)
  851. {
  852. return handlePtr->handleIsOffline();
  853. }
  854. static const TimeInfo* host_get_time_info(HostHandle handle)
  855. {
  856. return handlePtr->handleGetTimeInfo();
  857. }
  858. static bool host_write_midi_event(HostHandle handle, const ::MidiEvent* event)
  859. {
  860. return handlePtr->handleWriteMidiEvent(event);
  861. }
  862. static void host_ui_parameter_changed(HostHandle handle, uint32_t index, float value)
  863. {
  864. handlePtr->handleUiParameterChanged(index, value);
  865. }
  866. static void host_ui_custom_data_changed(HostHandle handle, const char* key, const char* value)
  867. {
  868. handlePtr->handleUiCustomDataChanged(key, value);
  869. }
  870. static void host_ui_closed(HostHandle handle)
  871. {
  872. handlePtr->handleUiClosed();
  873. }
  874. static const char* host_ui_open_file(HostHandle handle, bool isDir, const char* title, const char* filter)
  875. {
  876. return handlePtr->handleUiOpenFile(isDir, title, filter);
  877. }
  878. static const char* host_ui_save_file(HostHandle handle, bool isDir, const char* title, const char* filter)
  879. {
  880. return handlePtr->handleUiSaveFile(isDir, title, filter);
  881. }
  882. static intptr_t host_dispatcher(HostHandle handle, HostDispatcherOpcode opcode, int32_t index, intptr_t value, void* ptr, float opt)
  883. {
  884. return handlePtr->handleDispatcher(opcode, index, value, ptr, opt);
  885. }
  886. #undef handlePtr
  887. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(NativePlugin)
  888. };
  889. // -----------------------------------------------------------------------
  890. // LV2 descriptor functions
  891. static LV2_Handle lv2_instantiate(const LV2_Descriptor* lv2Descriptor, double sampleRate, const char* bundlePath, const LV2_Feature* const* features)
  892. {
  893. carla_debug("lv2_instantiate(%p, %g, %s, %p)", lv2Descriptor, sampleRate, bundlePath, features);
  894. const PluginDescriptor* pluginDesc = nullptr;
  895. const char* pluginLabel = nullptr;
  896. if (std::strncmp(lv2Descriptor->URI, "http://kxstudio.sf.net/carla/plugins/", 37) == 0)
  897. pluginLabel = lv2Descriptor->URI+37;
  898. else if (std::strcmp(lv2Descriptor->URI, "http://kxstudio.sf.net/carla") == 0)
  899. pluginLabel = lv2Descriptor->URI+23;
  900. if (pluginLabel == nullptr)
  901. {
  902. carla_stderr("Failed to find carla native plugin with URI \"%s\"", lv2Descriptor->URI);
  903. return nullptr;
  904. }
  905. carla_debug("lv2_instantiate() - looking up label \"%s\"", pluginLabel);
  906. for (NonRtList<const PluginDescriptor*>::Itenerator it = sPluginDescsMgr.descs.begin(); it.valid(); it.next())
  907. {
  908. const PluginDescriptor*& tmpDesc(*it);
  909. if (std::strcmp(tmpDesc->label, pluginLabel) == 0)
  910. {
  911. pluginDesc = tmpDesc;
  912. break;
  913. }
  914. }
  915. if (pluginDesc == nullptr)
  916. {
  917. carla_stderr("Failed to find carla native plugin with label \"%s\"", pluginLabel);
  918. return nullptr;
  919. }
  920. NativePlugin* const plugin(new NativePlugin(pluginDesc, sampleRate, bundlePath, features));
  921. if (! plugin->init())
  922. {
  923. carla_stderr("Failed to init plugin");
  924. delete plugin;
  925. return nullptr;
  926. }
  927. return (LV2_Handle)plugin;
  928. }
  929. #define instancePtr ((NativePlugin*)instance)
  930. static void lv2_connect_port(LV2_Handle instance, uint32_t port, void* dataLocation)
  931. {
  932. instancePtr->lv2_connect_port(port, dataLocation);
  933. }
  934. static void lv2_activate(LV2_Handle instance)
  935. {
  936. carla_debug("lv2_activate(%p)", instance);
  937. instancePtr->lv2_activate();
  938. }
  939. static void lv2_run(LV2_Handle instance, uint32_t sampleCount)
  940. {
  941. instancePtr->lv2_run(sampleCount);
  942. }
  943. static void lv2_deactivate(LV2_Handle instance)
  944. {
  945. carla_debug("lv2_deactivate(%p)", instance);
  946. instancePtr->lv2_deactivate();
  947. }
  948. static void lv2_cleanup(LV2_Handle instance)
  949. {
  950. carla_debug("lv2_cleanup(%p)", instance);
  951. instancePtr->lv2_cleanup();
  952. delete instancePtr;
  953. }
  954. static uint32_t lv2_get_options(LV2_Handle instance, LV2_Options_Option* options)
  955. {
  956. carla_debug("lv2_get_options(%p, %p)", instance, options);
  957. return instancePtr->lv2_get_options(options);
  958. }
  959. static uint32_t lv2_set_options(LV2_Handle instance, const LV2_Options_Option* options)
  960. {
  961. carla_debug("lv2_set_options(%p, %p)", instance, options);
  962. return instancePtr->lv2_set_options(options);
  963. }
  964. static const LV2_Program_Descriptor* lv2_get_program(LV2_Handle instance, uint32_t index)
  965. {
  966. carla_debug("lv2_get_program(%p, %i)", instance, index);
  967. return instancePtr->lv2_get_program(index);
  968. }
  969. static void lv2_select_program(LV2_Handle instance, uint32_t bank, uint32_t program)
  970. {
  971. carla_debug("lv2_select_program(%p, %i, %i)", instance, bank, program);
  972. return instancePtr->lv2_select_program(bank, program);
  973. }
  974. static LV2_State_Status lv2_save(LV2_Handle instance, LV2_State_Store_Function store, LV2_State_Handle handle, uint32_t flags, const LV2_Feature* const* features)
  975. {
  976. carla_debug("lv2_save(%p, %p, %p, %i, %p)", instance, store, handle, flags, features);
  977. return instancePtr->lv2_save(store, handle, flags, features);
  978. }
  979. static LV2_State_Status lv2_restore(LV2_Handle instance, LV2_State_Retrieve_Function retrieve, LV2_State_Handle handle, uint32_t flags, const LV2_Feature* const* features)
  980. {
  981. carla_debug("lv2_restore(%p, %p, %p, %i, %p)", instance, retrieve, handle, flags, features);
  982. return instancePtr->lv2_restore(retrieve, handle, flags, features);
  983. }
  984. static const void* lv2_extension_data(const char* uri)
  985. {
  986. carla_debug("lv2_extension_data(\"%s\")", uri);
  987. static const LV2_Options_Interface options = { lv2_get_options, lv2_set_options };
  988. static const LV2_Programs_Interface programs = { lv2_get_program, lv2_select_program };
  989. static const LV2_State_Interface state = { lv2_save, lv2_restore };
  990. if (std::strcmp(uri, LV2_OPTIONS__interface) == 0)
  991. return &options;
  992. if (std::strcmp(uri, LV2_PROGRAMS__Interface) == 0)
  993. return &programs;
  994. if (std::strcmp(uri, LV2_STATE__interface) == 0)
  995. return &state;
  996. return nullptr;
  997. }
  998. #undef instancePtr
  999. // -----------------------------------------------------------------------
  1000. // Startup code
  1001. static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*, const char*, const char*, LV2UI_Write_Function writeFunction,
  1002. LV2UI_Controller controller, LV2UI_Widget* widget, const LV2_Feature* const* features)
  1003. {
  1004. carla_debug("lv2ui_instantiate(..., %p, %p, %p)", writeFunction, controller, widget, features);
  1005. NativePlugin* plugin = nullptr;
  1006. for (int i=0; features[i] != nullptr; ++i)
  1007. {
  1008. if (std::strcmp(features[i]->URI, LV2_INSTANCE_ACCESS_URI) == 0)
  1009. {
  1010. plugin = (NativePlugin*)features[i]->data;
  1011. break;
  1012. }
  1013. }
  1014. if (plugin == nullptr)
  1015. {
  1016. carla_stderr("Host doesn't support instance-access, cannot show UI");
  1017. return nullptr;
  1018. }
  1019. if (! plugin->lv2ui_instantiate(writeFunction, controller, widget, features))
  1020. {
  1021. carla_stderr("Host doesn't support external UI");
  1022. return nullptr;
  1023. }
  1024. return (LV2UI_Handle)plugin;
  1025. }
  1026. #define uiPtr ((NativePlugin*)ui)
  1027. static void lv2ui_port_event(LV2UI_Handle ui, uint32_t portIndex, uint32_t bufferSize, uint32_t format, const void* buffer)
  1028. {
  1029. carla_debug("lv2ui_port_event(%p, %i, %i, %i, %p)", ui, portIndex, bufferSize, format, buffer);
  1030. uiPtr->lv2ui_port_event(portIndex, bufferSize, format, buffer);
  1031. }
  1032. static void lv2ui_cleanup(LV2UI_Handle ui)
  1033. {
  1034. carla_debug("lv2ui_cleanup(%p)", ui);
  1035. uiPtr->lv2ui_cleanup();
  1036. }
  1037. static void lv2ui_select_program(LV2UI_Handle ui, uint32_t bank, uint32_t program)
  1038. {
  1039. carla_debug("lv2ui_select_program(%p, %i, %i)", ui, bank, program);
  1040. uiPtr->lv2ui_select_program(bank, program);
  1041. }
  1042. static const void* lv2ui_extension_data(const char* uri)
  1043. {
  1044. carla_debug("lv2ui_extension_data(\"%s\")", uri);
  1045. static const LV2_Programs_UI_Interface uiprograms = { lv2ui_select_program };
  1046. if (std::strcmp(uri, LV2_PROGRAMS__UIInterface) == 0)
  1047. return &uiprograms;
  1048. return nullptr;
  1049. }
  1050. #undef uiPtr
  1051. // -----------------------------------------------------------------------
  1052. // Startup code
  1053. CARLA_EXPORT
  1054. const LV2_Descriptor* lv2_descriptor(uint32_t index)
  1055. {
  1056. carla_debug("lv2_descriptor(%i)", index);
  1057. if (index >= sPluginDescsMgr.descs.count())
  1058. {
  1059. carla_debug("lv2_descriptor(%i) - out of bounds", index);
  1060. return nullptr;
  1061. }
  1062. if (index < sPluginDescsMgr.lv2Descs.count())
  1063. {
  1064. carla_debug("lv2_descriptor(%i) - found previously allocated", index);
  1065. return sPluginDescsMgr.lv2Descs.getAt(index);
  1066. }
  1067. const PluginDescriptor*& pluginDesc(sPluginDescsMgr.descs.getAt(index));
  1068. CarlaString tmpURI;
  1069. if (std::strcmp(pluginDesc->label, "carla") == 0)
  1070. {
  1071. tmpURI = "http://kxstudio.sf.net/carla";
  1072. }
  1073. else
  1074. {
  1075. tmpURI = "http://kxstudio.sf.net/carla/plugins/";
  1076. tmpURI += pluginDesc->label;
  1077. }
  1078. carla_debug("lv2_descriptor(%i) - not found, allocating new with uri \"%s\"", index, (const char*)tmpURI);
  1079. const LV2_Descriptor* const lv2Desc(new const LV2_Descriptor{
  1080. /* URI */ carla_strdup(tmpURI),
  1081. /* instantiate */ lv2_instantiate,
  1082. /* connect_port */ lv2_connect_port,
  1083. /* activate */ lv2_activate,
  1084. /* run */ lv2_run,
  1085. /* deactivate */ lv2_deactivate,
  1086. /* cleanup */ lv2_cleanup,
  1087. /* extension_data */ lv2_extension_data
  1088. });
  1089. sPluginDescsMgr.lv2Descs.append(lv2Desc);
  1090. return lv2Desc;
  1091. }
  1092. CARLA_EXPORT
  1093. const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index)
  1094. {
  1095. carla_debug("lv2ui_descriptor(%i)", index);
  1096. static const LV2UI_Descriptor lv2UiDesc = {
  1097. /* URI */ "http://kxstudio.sf.net/carla#UI",
  1098. /* instantiate */ lv2ui_instantiate,
  1099. /* cleanup */ lv2ui_cleanup,
  1100. /* port_event */ lv2ui_port_event,
  1101. /* extension_data */ lv2ui_extension_data
  1102. };
  1103. return (index == 0) ? &lv2UiDesc : nullptr;
  1104. }
  1105. // -----------------------------------------------------------------------