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.

721 lines
21KB

  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. #include "CarlaNative.hpp"
  18. #include "juce_gui_basics.h"
  19. using namespace juce;
  20. #include "vex/VexArp.h"
  21. #include "vex/VexChorus.h"
  22. #include "vex/VexDelay.h"
  23. #include "vex/VexReverb.h"
  24. #include "vex/VexSyntModule.h"
  25. #include "vex/PeggyViewComponent.h"
  26. #include "vex/lookandfeel/MyLookAndFeel.h"
  27. #include "vex/resources/Resources.h"
  28. #include "vex/resources/Resources.cpp"
  29. // -----------------------------------------------------------------------
  30. class HelperWindow2 : public DocumentWindow
  31. {
  32. public:
  33. HelperWindow2()
  34. : DocumentWindow("PlugWindow", Colour(50, 50, 200), DocumentWindow::closeButton, false),
  35. fClosed(false)
  36. {
  37. setVisible(false);
  38. setAlwaysOnTop(true);
  39. setDropShadowEnabled(false);
  40. setOpaque(true);
  41. //setResizable(false, false);
  42. //setUsingNativeTitleBar(false);
  43. }
  44. void show(Component* const comp)
  45. {
  46. fClosed = false;
  47. const int width = comp->getWidth();
  48. const int height = comp->getHeight()+getTitleBarHeight();
  49. centreWithSize(width, height);
  50. setContentNonOwned(comp, false);
  51. setSize(width, height);
  52. if (! isOnDesktop())
  53. addToDesktop();
  54. setVisible(true);
  55. }
  56. void hide()
  57. {
  58. setVisible(false);
  59. if (isOnDesktop())
  60. removeFromDesktop();
  61. clearContentComponent();
  62. }
  63. bool wasClosedByUser() const
  64. {
  65. return fClosed;
  66. }
  67. protected:
  68. void closeButtonPressed() override
  69. {
  70. fClosed = true;
  71. }
  72. private:
  73. bool fClosed;
  74. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(HelperWindow2)
  75. };
  76. // -----------------------------------------------------------------------
  77. class VexEditorComponent : public ComboBox::Listener,
  78. public Slider::Listener,
  79. public Button::Listener,
  80. public ChangeListener,
  81. public Component,
  82. public PeggyViewComponent::Callback
  83. {
  84. public:
  85. VexEditorComponent()
  86. {
  87. internalCachedImage1 = ImageCache::getFromMemory(Resources::vex3_png, Resources::vex3_pngSize);
  88. // Comboboxes, wave selection
  89. addAndMakeVisible(comboBox1 = new ComboBox (String::empty));
  90. comboBox1->setEditableText(false);
  91. comboBox1->setJustificationType(Justification::centredLeft);
  92. comboBox1->setTextWhenNothingSelected(String("silent"));
  93. comboBox1->setTextWhenNoChoicesAvailable(String("silent"));
  94. comboBox1->addListener(this);
  95. comboBox1->setColour(ComboBox::backgroundColourId, Colours::black);
  96. comboBox1->setColour(ComboBox::textColourId, Colours::lightgrey);
  97. comboBox1->setColour(ComboBox::outlineColourId, Colours::grey);
  98. comboBox1->setColour(ComboBox::buttonColourId, Colours::grey);
  99. comboBox1->setWantsKeyboardFocus(false);
  100. comboBox1->setLookAndFeel(&mlaf);
  101. addAndMakeVisible (comboBox2 = new ComboBox (String::empty));
  102. comboBox2->setEditableText (false);
  103. comboBox2->setJustificationType (Justification::centredLeft);
  104. comboBox2->setTextWhenNothingSelected (String("silent"));
  105. comboBox2->setTextWhenNoChoicesAvailable (String("silent"));
  106. comboBox2->addListener (this);
  107. comboBox2->setColour(ComboBox::backgroundColourId, Colours::black);
  108. comboBox2->setColour(ComboBox::textColourId, Colours::lightgrey);
  109. comboBox2->setColour(ComboBox::outlineColourId, Colours::grey);
  110. comboBox2->setColour(ComboBox::buttonColourId, Colours::grey);
  111. comboBox2->setWantsKeyboardFocus(false);
  112. comboBox2->setLookAndFeel(&mlaf);
  113. addAndMakeVisible (comboBox3 = new ComboBox (String::empty));
  114. comboBox3->setEditableText (false);
  115. comboBox3->setJustificationType (Justification::centredLeft);
  116. comboBox3->setTextWhenNothingSelected (String("silent"));
  117. comboBox3->setTextWhenNoChoicesAvailable (String("silent"));
  118. comboBox3->addListener (this);
  119. comboBox3->setColour(ComboBox::backgroundColourId, Colours::black);
  120. comboBox3->setColour(ComboBox::textColourId, Colours::lightgrey);
  121. comboBox3->setColour(ComboBox::outlineColourId, Colours::grey);
  122. comboBox3->setColour(ComboBox::buttonColourId, Colours::grey);
  123. comboBox3->setWantsKeyboardFocus(false);
  124. comboBox3->setLookAndFeel(&mlaf);
  125. for (int i = 0, tableSize = WaveRenderer::getWaveTableSize(); i < tableSize; ++i)
  126. {
  127. String tableName(WaveRenderer::getWaveTableName(i));
  128. comboBox1->addItem(tableName, i + 1);
  129. comboBox2->addItem(tableName, i + 1);
  130. comboBox3->addItem(tableName, i + 1);
  131. }
  132. addChildComponent(p1 = new PeggyViewComponent(1, _d, this));
  133. p1->setLookAndFeel(&mlaf);
  134. addChildComponent(p2 = new PeggyViewComponent(2, _d, this));
  135. p2->setLookAndFeel(&mlaf);
  136. addChildComponent(p3 = new PeggyViewComponent(3, _d, this));
  137. p3->setLookAndFeel(&mlaf);
  138. //ownerFilter->addChangeListener (this);
  139. setSize(800,500);
  140. }
  141. ~VexEditorComponent()
  142. {
  143. removeAllChildren();
  144. }
  145. protected:
  146. void paint(Graphics& g) override
  147. {
  148. g.drawImage(internalCachedImage1,
  149. 0, 0, 800, 500,
  150. 0, 0, internalCachedImage1.getWidth(), internalCachedImage1.getHeight());
  151. }
  152. void resized() override
  153. {
  154. comboBox1->setBounds(13, 38, 173, 23);
  155. comboBox2->setBounds(213, 38, 173, 23);
  156. comboBox3->setBounds(413, 38, 173, 23);
  157. p1->setBounds(10, 20, 207, 280);
  158. p2->setBounds(210, 20, 207, 280);
  159. p3->setBounds(410, 20, 207, 280);
  160. }
  161. void changeListenerCallback(ChangeBroadcaster* source) override
  162. {
  163. }
  164. void comboBoxChanged(ComboBox* comboBoxThatHasChanged) override
  165. {
  166. }
  167. void sliderValueChanged(Slider* sliderThatWasMoved) override
  168. {
  169. }
  170. void buttonClicked(Button* buttonThatWasClicked) override
  171. {
  172. }
  173. void somethingChanged(const uint32_t id) override
  174. {
  175. }
  176. private:
  177. Image internalCachedImage1;
  178. MyLookAndFeel mlaf;
  179. ScopedPointer<ComboBox> comboBox1;
  180. ScopedPointer<ComboBox> comboBox2;
  181. ScopedPointer<ComboBox> comboBox3;
  182. ScopedPointer<PeggyViewComponent> p1;
  183. ScopedPointer<PeggyViewComponent> p2;
  184. ScopedPointer<PeggyViewComponent> p3;
  185. VexArpSettings _d;
  186. };
  187. // -----------------------------------------------------------------------
  188. class VexSynthPlugin : public PluginClass
  189. {
  190. public:
  191. static const unsigned int kParamCount = 92;
  192. VexSynthPlugin(const HostDescriptor* const host)
  193. : PluginClass(host),
  194. obf(nullptr),
  195. abf(nullptr),
  196. dbf1(nullptr),
  197. dbf2(nullptr),
  198. dbf3(nullptr),
  199. fChorus(fParameters),
  200. fDelay(fParameters),
  201. fReverb(fParameters),
  202. fSynth(fParameters)
  203. {
  204. std::memset(fParameters, 0, sizeof(float)*92);
  205. fParameters[0] = 1.0f; // main volume
  206. for (int i = 0; i < 3; ++i)
  207. {
  208. const int offset = i * 24;
  209. fParameters[offset + 1] = 0.5f;
  210. fParameters[offset + 2] = 0.5f;
  211. fParameters[offset + 3] = 0.5f;
  212. fParameters[offset + 4] = 0.5f;
  213. fParameters[offset + 5] = 0.9f;
  214. fParameters[offset + 6] = 0.0f;
  215. fParameters[offset + 7] = 1.0f;
  216. fParameters[offset + 8] = 0.5f;
  217. fParameters[offset + 9] = 0.0f;
  218. fParameters[offset + 10] = 0.2f;
  219. fParameters[offset + 11] = 0.0f;
  220. fParameters[offset + 12] = 0.5f;
  221. fParameters[offset + 13] = 0.5f;
  222. fParameters[offset + 14] = 0.0f;
  223. fParameters[offset + 15] = 0.3f;
  224. fParameters[offset + 16] = 0.7f;
  225. fParameters[offset + 17] = 0.1f;
  226. fParameters[offset + 18] = 0.5f;
  227. fParameters[offset + 19] = 0.5f;
  228. fParameters[offset + 20] = 0.0f;
  229. fParameters[offset + 21] = 0.0f;
  230. fParameters[offset + 22] = 0.5f;
  231. fParameters[offset + 23] = 0.5f;
  232. fParameters[offset + 24] = 0.5f;
  233. }
  234. // ^1 - 72
  235. fParameters[73] = 0.5f; // Delay Time
  236. fParameters[74] = 0.4f; // Delay Feedback
  237. fParameters[75] = 0.0f; // Delay Volume
  238. fParameters[76] = 0.3f; // Chorus Rate
  239. fParameters[77] = 0.6f; // Chorus Depth
  240. fParameters[78] = 0.5f; // Chorus Volume
  241. fParameters[79] = 0.6f; // Reverb Size
  242. fParameters[80] = 0.7f; // Reverb Width
  243. fParameters[81] = 0.6f; // Reverb Damp
  244. fParameters[82] = 0.0f; // Reverb Volume
  245. fParameters[83] = 0.5f; // wave1 panning
  246. fParameters[84] = 0.5f; // wave2 panning
  247. fParameters[85] = 0.5f; // wave3 panning
  248. fParameters[86] = 0.5f; // wave1 volume
  249. fParameters[87] = 0.5f; // wave2 volume
  250. fParameters[88] = 0.5f; // wave3 volume
  251. fParameters[89] = 1.0f; // wave1 on/off
  252. fParameters[90] = 1.0f; // wave2 on/off
  253. fParameters[91] = 1.0f; // wave3 on/off
  254. bufferSizeChanged(getBufferSize());
  255. sampleRateChanged(getSampleRate());
  256. }
  257. ~VexSynthPlugin()
  258. {
  259. delete obf;
  260. delete abf;
  261. delete dbf1;
  262. delete dbf2;
  263. delete dbf3;
  264. }
  265. protected:
  266. // -------------------------------------------------------------------
  267. // Plugin parameter calls
  268. uint32_t getParameterCount() const override
  269. {
  270. return kParamCount;
  271. }
  272. const Parameter* getParameterInfo(const uint32_t index) const override
  273. {
  274. static Parameter paramInfo;
  275. int hints = PARAMETER_IS_ENABLED|PARAMETER_IS_AUTOMABLE;
  276. paramInfo.name = nullptr;
  277. paramInfo.unit = nullptr;
  278. paramInfo.ranges.def = 0.0f;
  279. paramInfo.ranges.min = 0.0f;
  280. paramInfo.ranges.max = 1.0f;
  281. paramInfo.ranges.step = PARAMETER_RANGES_DEFAULT_STEP;
  282. paramInfo.ranges.stepSmall = PARAMETER_RANGES_DEFAULT_STEP_SMALL;
  283. paramInfo.ranges.stepLarge = PARAMETER_RANGES_DEFAULT_STEP_LARGE;
  284. paramInfo.scalePointCount = 0;
  285. paramInfo.scalePoints = nullptr;
  286. if (index >= 1 && index <= 72)
  287. {
  288. uint32_t ri = index % 24;
  289. switch (ri)
  290. {
  291. case 1:
  292. paramInfo.name = "oct";
  293. break;
  294. case 2:
  295. paramInfo.name = "cent";
  296. break;
  297. case 3:
  298. paramInfo.name = "phaseOffset";
  299. break;
  300. case 4:
  301. paramInfo.name = "phaseIncOffset";
  302. break;
  303. case 5:
  304. paramInfo.name = "filter";
  305. break;
  306. case 6:
  307. paramInfo.name = "filter";
  308. break;
  309. case 7:
  310. paramInfo.name = "filter";
  311. break;
  312. case 8:
  313. paramInfo.name = "filter";
  314. break;
  315. case 9:
  316. paramInfo.name = "F ADSR";
  317. break;
  318. case 10:
  319. paramInfo.name = "F ADSR";
  320. break;
  321. case 11:
  322. paramInfo.name = "F ADSR";
  323. break;
  324. case 12:
  325. paramInfo.name = "F ADSR";
  326. break;
  327. case 13:
  328. paramInfo.name = "F velocity";
  329. break;
  330. case 14:
  331. paramInfo.name = "A ADSR";
  332. break;
  333. case 15:
  334. paramInfo.name = "A ADSR";
  335. break;
  336. case 16:
  337. paramInfo.name = "A ADSR";
  338. break;
  339. case 17:
  340. paramInfo.name = "A ADSR";
  341. break;
  342. case 18:
  343. paramInfo.name = "A velocity";
  344. break;
  345. case 19:
  346. paramInfo.name = "lfoC";
  347. break;
  348. case 20:
  349. paramInfo.name = "lfoA";
  350. break;
  351. case 21:
  352. paramInfo.name = "lfoF";
  353. break;
  354. case 22:
  355. paramInfo.name = "fx vol D";
  356. break;
  357. case 23:
  358. paramInfo.name = "fx vol C";
  359. break;
  360. case 24:
  361. case 0:
  362. paramInfo.name = "fx vol R";
  363. break;
  364. default:
  365. paramInfo.name = "unknown2";
  366. break;
  367. }
  368. paramInfo.hints = static_cast<ParameterHints>(hints);
  369. return &paramInfo;
  370. }
  371. switch (index)
  372. {
  373. case 0:
  374. paramInfo.name = "Main volume";
  375. break;
  376. case 73:
  377. paramInfo.name = "Delay Time";
  378. break;
  379. case 74:
  380. paramInfo.name = "Delay Feedback";
  381. break;
  382. case 75:
  383. paramInfo.name = "Delay Volume";
  384. break;
  385. case 76:
  386. paramInfo.name = "Chorus Rate";
  387. break;
  388. case 77:
  389. paramInfo.name = "Chorus Depth";
  390. break;
  391. case 78:
  392. paramInfo.name = "Chorus Volume";
  393. break;
  394. case 79:
  395. paramInfo.name = "Reverb Size";
  396. break;
  397. case 80:
  398. paramInfo.name = "Reverb Width";
  399. break;
  400. case 81:
  401. paramInfo.name = "Reverb Damp";
  402. break;
  403. case 82:
  404. paramInfo.name = "Reverb Volume";
  405. break;
  406. case 83:
  407. paramInfo.name = "Wave1 Panning";
  408. break;
  409. case 84:
  410. paramInfo.name = "Wave2 Panning";
  411. break;
  412. case 85:
  413. paramInfo.name = "Wave3 Panning";
  414. break;
  415. case 86:
  416. paramInfo.name = "Wave1 Volume";
  417. break;
  418. case 87:
  419. paramInfo.name = "Wave2 Volume";
  420. break;
  421. case 88:
  422. paramInfo.name = "Wave3 Volume";
  423. break;
  424. case 89:
  425. paramInfo.name = "Wave1 on/off";
  426. break;
  427. case 90:
  428. paramInfo.name = "Wave2 on/off";
  429. break;
  430. case 91:
  431. paramInfo.name = "Wave3 on/off";
  432. break;
  433. default:
  434. paramInfo.name = "unknown";
  435. break;
  436. }
  437. paramInfo.hints = static_cast<ParameterHints>(hints);
  438. return &paramInfo;
  439. }
  440. float getParameterValue(const uint32_t index) const override
  441. {
  442. return (index < kParamCount) ? fParameters[index] : 0.0f;
  443. }
  444. // -------------------------------------------------------------------
  445. // Plugin state calls
  446. void setParameterValue(const uint32_t index, const float value) override
  447. {
  448. if (index < kParamCount)
  449. {
  450. fParameters[index] = value;
  451. fSynth.update(index);
  452. }
  453. }
  454. // -------------------------------------------------------------------
  455. // Plugin process calls
  456. void process(float**, float** outBuffer, const uint32_t frames, const MidiEvent* const midiEvents, const uint32_t midiEventCount) override
  457. {
  458. const TimeInfo* const timeInfo(getTimeInfo());
  459. const double bpm((timeInfo != nullptr && timeInfo->bbt.valid) ? timeInfo->bbt.beatsPerMinute : 120.0);
  460. for (uint32_t i=0; i < midiEventCount; ++i)
  461. {
  462. const MidiEvent* const midiEvent(&midiEvents[i]);
  463. const uint8_t status(MIDI_GET_STATUS_FROM_DATA(midiEvent->data));
  464. if (status == MIDI_STATUS_NOTE_ON)
  465. {
  466. fSynth.playNote(midiEvent->data[1], midiEvent->data[2], 0, 1);
  467. }
  468. else if (status == MIDI_STATUS_NOTE_OFF)
  469. {
  470. fSynth.releaseNote(midiEvent->data[1], 0, 1);
  471. }
  472. else if (status == MIDI_STATUS_CONTROL_CHANGE)
  473. {
  474. const uint8_t control(midiEvent->data[1]);
  475. if (control == MIDI_CONTROL_ALL_SOUND_OFF)
  476. fSynth.kill();
  477. else if (control == MIDI_CONTROL_ALL_NOTES_OFF)
  478. fSynth.releaseAll(1);
  479. }
  480. }
  481. if (obf->getNumSamples() != (int)frames)
  482. {
  483. obf->setSize(2, frames, 0, 0, 1);
  484. abf->setSize(2, frames, 0, 0, 1);
  485. dbf1->setSize(2, frames, 0, 0, 1);
  486. dbf2->setSize(2, frames, 0, 0, 1);
  487. dbf3->setSize(2, frames, 0, 0, 1);
  488. }
  489. obf ->clear();
  490. dbf1->clear();
  491. dbf2->clear();
  492. dbf3->clear();
  493. fSynth.doProcess(*obf, *abf, *dbf1, *dbf2, *dbf3);
  494. if (fParameters[75] > 0.001f) fDelay.processBlock(dbf1, bpm);
  495. if (fParameters[78] > 0.001f) fChorus.processBlock(dbf2);
  496. if (fParameters[82] > 0.001f) fReverb.processBlock(dbf3);
  497. AudioSampleBuffer output(outBuffer, 2, 0, frames);
  498. output.clear();
  499. obf->addFrom(0, 0, *dbf1, 0, 0, frames, fParameters[75]);
  500. obf->addFrom(1, 0, *dbf1, 1, 0, frames, fParameters[75]);
  501. obf->addFrom(0, 0, *dbf2, 0, 0, frames, fParameters[78]);
  502. obf->addFrom(1, 0, *dbf2, 1, 0, frames, fParameters[78]);
  503. obf->addFrom(0, 0, *dbf3, 0, 0, frames, fParameters[82]);
  504. obf->addFrom(1, 0, *dbf3, 1, 0, frames, fParameters[82]);
  505. output.addFrom(0, 0, *obf, 0, 0, frames, fParameters[0]);
  506. output.addFrom(1, 0, *obf, 1, 0, frames, fParameters[0]);
  507. }
  508. // -------------------------------------------------------------------
  509. // Plugin UI calls
  510. void uiShow(const bool show) override
  511. {
  512. if (show)
  513. {
  514. if (fWindow == nullptr)
  515. {
  516. fWindow = new HelperWindow2();
  517. fWindow->setName(getUiName());
  518. }
  519. if (fView == nullptr)
  520. fView = new VexEditorComponent();
  521. fWindow->show(fView);
  522. }
  523. else if (fWindow != nullptr)
  524. {
  525. fWindow->hide();
  526. fView = nullptr;
  527. fWindow = nullptr;
  528. }
  529. }
  530. void uiIdle() override
  531. {
  532. if (fWindow == nullptr)
  533. return;
  534. if (fWindow->wasClosedByUser())
  535. {
  536. uiShow(false);
  537. uiClosed();
  538. }
  539. }
  540. void uiSetParameterValue(const uint32_t, const float) override
  541. {
  542. if (fView == nullptr)
  543. return;
  544. //fView->update();
  545. }
  546. // -------------------------------------------------------------------
  547. // Plugin dispatcher calls
  548. void bufferSizeChanged(const uint32_t bufferSize) override
  549. {
  550. delete obf;
  551. delete abf;
  552. delete dbf1;
  553. delete dbf2;
  554. delete dbf3;
  555. obf = new AudioSampleBuffer(2, bufferSize);
  556. abf = new AudioSampleBuffer(2, bufferSize);
  557. dbf1 = new AudioSampleBuffer(2, bufferSize);
  558. dbf2 = new AudioSampleBuffer(2, bufferSize);
  559. dbf3 = new AudioSampleBuffer(2, bufferSize);
  560. }
  561. void sampleRateChanged(const double sampleRate) override
  562. {
  563. fChorus.setSampleRate(sampleRate);
  564. fDelay.setSampleRate(sampleRate);
  565. fSynth.setSampleRate(sampleRate);
  566. }
  567. void uiNameChanged(const char* const uiName) override
  568. {
  569. if (fWindow == nullptr)
  570. return;
  571. fWindow->setName(uiName);
  572. }
  573. private:
  574. float fParameters[92];
  575. AudioSampleBuffer* obf;
  576. AudioSampleBuffer* abf;
  577. AudioSampleBuffer* dbf1; // delay
  578. AudioSampleBuffer* dbf2; // chorus
  579. AudioSampleBuffer* dbf3; // reverb
  580. VexChorus fChorus;
  581. VexDelay fDelay;
  582. VexReverb fReverb;
  583. VexSyntModule fSynth;
  584. ScopedPointer<VexEditorComponent> fView;
  585. ScopedPointer<HelperWindow2> fWindow;
  586. PluginClassEND(VexSynthPlugin)
  587. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(VexSynthPlugin)
  588. };
  589. // -----------------------------------------------------------------------
  590. static const PluginDescriptor vexSynthDesc = {
  591. /* category */ PLUGIN_CATEGORY_SYNTH,
  592. /* hints */ static_cast<PluginHints>(PLUGIN_HAS_GUI|PLUGIN_NEEDS_SINGLE_THREAD|PLUGIN_USES_TIME),
  593. /* supports */ static_cast<PluginSupports>(0x0),
  594. /* audioIns */ 0,
  595. /* audioOuts */ 2,
  596. /* midiIns */ 1,
  597. /* midiOuts */ 0,
  598. /* paramIns */ VexSynthPlugin::kParamCount,
  599. /* paramOuts */ 0,
  600. /* name */ "VexSynth",
  601. /* label */ "vexSynth",
  602. /* maker */ "falkTX",
  603. /* copyright */ "GNU GPL v2+",
  604. PluginDescriptorFILL(VexSynthPlugin)
  605. };
  606. // -----------------------------------------------------------------------
  607. CARLA_EXPORT
  608. void carla_register_native_plugin_vex_synth()
  609. {
  610. carla_register_native_plugin(&vexSynthDesc);
  611. }
  612. // -----------------------------------------------------------------------