Collection of tools useful for audio production
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.

1338 lines
41KB

  1. /*
  2. * Carla Plugin discovery code
  3. * Copyright (C) 2011-2012 Filipe Coelho <falktx@falktx.com>
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * 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 COPYING file
  16. */
  17. #include <iostream>
  18. #include <QtCore/QDir>
  19. #include <QtCore/QFileInfo>
  20. #include <QtCore/QUrl>
  21. #include "carla_backend.hpp"
  22. #include "carla_lib_utils.hpp"
  23. #ifdef WANT_LADSPA
  24. # include "carla_ladspa_utils.hpp"
  25. #endif
  26. #ifdef WANT_DSSI
  27. # include "carla_ladspa_utils.hpp"
  28. # include "dssi/dssi.h"
  29. #endif
  30. #ifdef WANT_LV2
  31. # include "carla_lv2_utils.hpp"
  32. #endif
  33. #ifdef WANT_VST
  34. # include "carla_vst_utils.hpp"
  35. #endif
  36. #ifdef WANT_FLUIDSYNTH
  37. # include <fluidsynth.h>
  38. #endif
  39. #ifdef WANT_LINUXSAMPLER
  40. # include "linuxsampler/EngineFactory.h"
  41. #endif
  42. #define DISCOVERY_OUT(x, y) std::cout << "\ncarla-discovery::" << x << "::" << y << std::endl;
  43. // Fake values to test plugins with
  44. const uint32_t bufferSize = 512;
  45. const double sampleRate = 44100.0;
  46. // Since discovery can find multi-architecture binaries, don't print ELF/EXE related errors
  47. void print_lib_error(const char* const filename)
  48. {
  49. const char* const error = lib_error(filename);
  50. if (error && strstr(error, "wrong ELF class") == nullptr && strstr(error, "Bad EXE format") == nullptr)
  51. DISCOVERY_OUT("error", error);
  52. }
  53. using namespace CarlaBackend;
  54. // ------------------------------ VST Stuff ------------------------------
  55. #ifdef WANT_VST
  56. bool vstWantsMidi = false;
  57. intptr_t vstCurrentUniqueId = 0;
  58. intptr_t vstHostCanDo(const char* const feature)
  59. {
  60. qDebug("vstHostCanDo(\"%s\")", feature);
  61. if (strcmp(feature, "supplyIdle") == 0)
  62. return 1;
  63. if (strcmp(feature, "sendVstEvents") == 0)
  64. return 1;
  65. if (strcmp(feature, "sendVstMidiEvent") == 0)
  66. return 1;
  67. if (strcmp(feature, "sendVstMidiEventFlagIsRealtime") == 0)
  68. return -1;
  69. if (strcmp(feature, "sendVstTimeInfo") == 0)
  70. return 1;
  71. if (strcmp(feature, "receiveVstEvents") == 0)
  72. return 1;
  73. if (strcmp(feature, "receiveVstMidiEvent") == 0)
  74. return 1;
  75. if (strcmp(feature, "receiveVstTimeInfo") == 0)
  76. return -1;
  77. if (strcmp(feature, "reportConnectionChanges") == 0)
  78. return -1;
  79. if (strcmp(feature, "acceptIOChanges") == 0)
  80. return 1;
  81. if (strcmp(feature, "sizeWindow") == 0)
  82. return 1;
  83. if (strcmp(feature, "offline") == 0)
  84. return -1;
  85. if (strcmp(feature, "openFileSelector") == 0)
  86. return -1;
  87. if (strcmp(feature, "closeFileSelector") == 0)
  88. return -1;
  89. if (strcmp(feature, "startStopProcess") == 0)
  90. return 1;
  91. if (strcmp(feature, "supportShell") == 0)
  92. return 1;
  93. if (strcmp(feature, "shellCategory") == 0)
  94. return 1;
  95. // unimplemented
  96. qWarning("vstHostCanDo(\"%s\") - unknown feature", feature);
  97. return 0;
  98. }
  99. intptr_t VSTCALLBACK vstHostCallback(AEffect* const effect, const int32_t opcode, const int32_t index, const intptr_t value, void* const ptr, const float opt)
  100. {
  101. #if DEBUG
  102. qDebug("vstHostCallback(%p, %s, %i, " P_INTPTR ", %p, %f)", effect, vstMasterOpcode2str(opcode), index, value, ptr, opt);
  103. #endif
  104. intptr_t ret = 0;
  105. switch (opcode)
  106. {
  107. case audioMasterAutomate:
  108. if (effect)
  109. {
  110. effect->setParameter(effect, index, opt);
  111. ret = 1;
  112. }
  113. break;
  114. case audioMasterVersion:
  115. ret = kVstVersion;
  116. break;
  117. case audioMasterCurrentId:
  118. ret = vstCurrentUniqueId;
  119. break;
  120. #if ! VST_FORCE_DEPRECATED
  121. case audioMasterWantMidi:
  122. vstWantsMidi = true;
  123. ret = 1;
  124. break;
  125. #endif
  126. case audioMasterGetTime:
  127. static VstTimeInfo_R timeInfo;
  128. memset(&timeInfo, 0, sizeof(VstTimeInfo_R));
  129. timeInfo.sampleRate = sampleRate;
  130. // Tempo
  131. timeInfo.tempo = 120.0;
  132. timeInfo.flags |= kVstTempoValid;
  133. // Time Signature
  134. timeInfo.timeSigNumerator = 4;
  135. timeInfo.timeSigDenominator = 4;
  136. timeInfo.flags |= kVstTimeSigValid;
  137. ret = (intptr_t)&timeInfo;
  138. break;
  139. #if ! VST_FORCE_DEPRECATED
  140. case audioMasterTempoAt:
  141. ret = 120 * 10000;
  142. break;
  143. case audioMasterGetNumAutomatableParameters:
  144. ret = carla_minPositiveI(effect->numParams, MAX_PARAMETERS);
  145. break;
  146. case audioMasterGetParameterQuantization:
  147. ret = 1; // full single float precision
  148. break;
  149. #endif
  150. case audioMasterGetSampleRate:
  151. ret = sampleRate;
  152. break;
  153. case audioMasterGetBlockSize:
  154. ret = bufferSize;
  155. break;
  156. #if ! VST_FORCE_DEPRECATED
  157. case audioMasterWillReplaceOrAccumulate:
  158. ret = 1; // replace
  159. break;
  160. #endif
  161. case audioMasterGetCurrentProcessLevel:
  162. ret = kVstProcessLevelUser;
  163. break;
  164. case audioMasterGetAutomationState:
  165. ret = kVstAutomationOff;
  166. break;
  167. case audioMasterGetVendorString:
  168. if (ptr)
  169. {
  170. strcpy((char*)ptr, "Cadence");
  171. ret = 1;
  172. }
  173. break;
  174. case audioMasterGetProductString:
  175. if (ptr)
  176. {
  177. strcpy((char*)ptr, "Carla-Discovery");
  178. ret = 1;
  179. }
  180. break;
  181. case audioMasterGetVendorVersion:
  182. ret = 0x050; // 0.5.0
  183. break;
  184. case audioMasterCanDo:
  185. if (ptr)
  186. ret = vstHostCanDo((const char*)ptr);
  187. break;
  188. case audioMasterGetLanguage:
  189. ret = kVstLangEnglish;
  190. break;
  191. default:
  192. qDebug("vstHostCallback(%p, %s, %i, " P_INTPTR ", %p, %f)", effect, vstMasterOpcode2str(opcode), index, value, ptr, opt);
  193. break;
  194. }
  195. return ret;
  196. }
  197. #endif
  198. // ------------------------------ Plugin Checks -----------------------------
  199. void do_ladspa_check(void* const libHandle, const bool init)
  200. {
  201. #ifdef WANT_LADSPA
  202. const LADSPA_Descriptor_Function descFn = (LADSPA_Descriptor_Function)lib_symbol(libHandle, "ladspa_descriptor");
  203. if (! descFn)
  204. {
  205. DISCOVERY_OUT("error", "Not a LADSPA plugin");
  206. return;
  207. }
  208. unsigned long i = 0;
  209. const LADSPA_Descriptor* descriptor;
  210. while ((descriptor = descFn(i++)))
  211. {
  212. CARLA_ASSERT(descriptor->run);
  213. int hints = 0;
  214. int audioIns = 0;
  215. int audioOuts = 0;
  216. int audioTotal = 0;
  217. int parametersIns = 0;
  218. int parametersOuts = 0;
  219. int parametersTotal = 0;
  220. for (unsigned long j=0; j < descriptor->PortCount; j++)
  221. {
  222. const LADSPA_PortDescriptor portDescriptor = descriptor->PortDescriptors[j];
  223. if (LADSPA_IS_PORT_AUDIO(portDescriptor))
  224. {
  225. if (LADSPA_IS_PORT_INPUT(portDescriptor))
  226. audioIns += 1;
  227. else if (LADSPA_IS_PORT_OUTPUT(portDescriptor))
  228. audioOuts += 1;
  229. audioTotal += 1;
  230. }
  231. else if (LADSPA_IS_PORT_CONTROL(portDescriptor))
  232. {
  233. if (LADSPA_IS_PORT_INPUT(portDescriptor))
  234. parametersIns += 1;
  235. else if (LADSPA_IS_PORT_OUTPUT(portDescriptor) && strcmp(descriptor->PortNames[j], "latency") && strcmp(descriptor->PortNames[j], "_latency") && strcmp(descriptor->PortNames[j], "_sample-rate"))
  236. parametersOuts += 1;
  237. parametersTotal += 1;
  238. }
  239. }
  240. if (init)
  241. {
  242. // -----------------------------------------------------------------------
  243. // start crash-free plugin test
  244. const LADSPA_Handle handle = descriptor->instantiate(descriptor, sampleRate);
  245. if (! handle)
  246. {
  247. DISCOVERY_OUT("error", "Failed to init LADSPA plugin");
  248. continue;
  249. }
  250. LADSPA_Data bufferAudio[bufferSize][audioTotal];
  251. LADSPA_Data bufferParams[parametersTotal];
  252. LADSPA_Data min, max, def;
  253. for (unsigned long j=0, iA=0, iP=0; j < descriptor->PortCount; j++)
  254. {
  255. const LADSPA_PortDescriptor portType = descriptor->PortDescriptors[j];
  256. const LADSPA_PortRangeHint portHints = descriptor->PortRangeHints[j];
  257. if (LADSPA_IS_PORT_AUDIO(portType))
  258. {
  259. carla_zeroF(bufferAudio[iA], bufferSize);
  260. descriptor->connect_port(handle, j, bufferAudio[iA++]);
  261. }
  262. else if (LADSPA_IS_PORT_CONTROL(portType))
  263. {
  264. // min value
  265. if (LADSPA_IS_HINT_BOUNDED_BELOW(portHints.HintDescriptor))
  266. min = portHints.LowerBound;
  267. else
  268. min = 0.0f;
  269. // max value
  270. if (LADSPA_IS_HINT_BOUNDED_ABOVE(portHints.HintDescriptor))
  271. max = portHints.UpperBound;
  272. else
  273. max = 1.0f;
  274. if (min > max)
  275. max = min;
  276. else if (max < min)
  277. min = max;
  278. if (max - min == 0.0f)
  279. {
  280. DISCOVERY_OUT("error", "Broken parameter '" << descriptor->PortNames[j] << "': max - min == 0");
  281. max = min + 0.1f;
  282. }
  283. // default value
  284. def = get_default_ladspa_port_value(portHints.HintDescriptor, min, max);
  285. if (LADSPA_IS_HINT_SAMPLE_RATE(portHints.HintDescriptor))
  286. {
  287. min *= sampleRate;
  288. max *= sampleRate;
  289. def *= sampleRate;
  290. }
  291. if (LADSPA_IS_PORT_OUTPUT(portType) && (strcmp(descriptor->PortNames[j], "latency") == 0 || strcmp(descriptor->PortNames[j], "_latency") == 0))
  292. {
  293. // latency parameter
  294. min = 0.0f;
  295. max = sampleRate;
  296. def = 0.0f;
  297. }
  298. if (def < min)
  299. def = min;
  300. else if (def > max)
  301. def = max;
  302. bufferParams[iP] = def;
  303. descriptor->connect_port(handle, j, &bufferParams[iP++]);
  304. }
  305. }
  306. if (descriptor->activate)
  307. descriptor->activate(handle);
  308. if (descriptor->run)
  309. descriptor->run(handle, bufferSize);
  310. if (descriptor->deactivate)
  311. descriptor->deactivate(handle);
  312. if (descriptor->cleanup)
  313. descriptor->cleanup(handle);
  314. // end crash-free plugin test
  315. // -----------------------------------------------------------------------
  316. }
  317. DISCOVERY_OUT("init", "-----------");
  318. DISCOVERY_OUT("name", descriptor->Name);
  319. DISCOVERY_OUT("label", descriptor->Label);
  320. DISCOVERY_OUT("maker", descriptor->Maker);
  321. DISCOVERY_OUT("copyright", descriptor->Copyright);
  322. DISCOVERY_OUT("unique_id", descriptor->UniqueID);
  323. DISCOVERY_OUT("hints", hints);
  324. DISCOVERY_OUT("audio.ins", audioIns);
  325. DISCOVERY_OUT("audio.outs", audioOuts);
  326. DISCOVERY_OUT("audio.total", audioTotal);
  327. DISCOVERY_OUT("parameters.ins", parametersIns);
  328. DISCOVERY_OUT("parameters.outs", parametersOuts);
  329. DISCOVERY_OUT("parameters.total", parametersTotal);
  330. DISCOVERY_OUT("build", BINARY_NATIVE);
  331. DISCOVERY_OUT("end", "------------");
  332. }
  333. #else
  334. DISCOVERY_OUT("error", "LADSPA support not available");
  335. Q_UNUSED(libHandle);
  336. Q_UNUSED(init);
  337. #endif
  338. }
  339. void do_dssi_check(void* const libHandle, const bool init)
  340. {
  341. #ifdef WANT_DSSI
  342. const DSSI_Descriptor_Function descFn = (DSSI_Descriptor_Function)lib_symbol(libHandle, "dssi_descriptor");
  343. if (! descFn)
  344. {
  345. DISCOVERY_OUT("error", "Not a DSSI plugin");
  346. return;
  347. }
  348. unsigned long i = 0;
  349. const DSSI_Descriptor* descriptor;
  350. while ((descriptor = descFn(i++)))
  351. {
  352. const LADSPA_Descriptor* const ldescriptor = descriptor->LADSPA_Plugin;
  353. CARLA_ASSERT(ldescriptor);
  354. CARLA_ASSERT(ldescriptor->run || descriptor->run_synth || descriptor->run_multiple_synths);
  355. int hints = 0;
  356. int audioIns = 0;
  357. int audioOuts = 0;
  358. int audioTotal = 0;
  359. int midiIns = 0;
  360. int midiTotal = 0;
  361. int parametersIns = 0;
  362. int parametersOuts = 0;
  363. int parametersTotal = 0;
  364. int programsTotal = 0;
  365. for (unsigned long j=0; j < ldescriptor->PortCount; j++)
  366. {
  367. const LADSPA_PortDescriptor portDescriptor = ldescriptor->PortDescriptors[j];
  368. if (LADSPA_IS_PORT_AUDIO(portDescriptor))
  369. {
  370. if (LADSPA_IS_PORT_INPUT(portDescriptor))
  371. audioIns += 1;
  372. else if (LADSPA_IS_PORT_OUTPUT(portDescriptor))
  373. audioOuts += 1;
  374. audioTotal += 1;
  375. }
  376. else if (LADSPA_IS_PORT_CONTROL(portDescriptor))
  377. {
  378. if (LADSPA_IS_PORT_INPUT(portDescriptor))
  379. parametersIns += 1;
  380. else if (LADSPA_IS_PORT_OUTPUT(portDescriptor) && strcmp(ldescriptor->PortNames[j], "latency") && strcmp(ldescriptor->PortNames[j], "_latency") && strcmp(ldescriptor->PortNames[j], "_sample-rate"))
  381. parametersOuts += 1;
  382. parametersTotal += 1;
  383. }
  384. }
  385. if (descriptor->run_synth || descriptor->run_multiple_synths)
  386. midiIns = midiTotal = 1;
  387. if (midiIns > 0 && audioOuts > 0)
  388. hints |= PLUGIN_IS_SYNTH;
  389. if (init)
  390. {
  391. // -----------------------------------------------------------------------
  392. // start crash-free plugin test
  393. const LADSPA_Handle handle = ldescriptor->instantiate(ldescriptor, sampleRate);
  394. if (! handle)
  395. {
  396. DISCOVERY_OUT("error", "Failed to init DSSI plugin");
  397. continue;
  398. }
  399. // we can only get program info per-handle
  400. if (descriptor->get_program && descriptor->select_program)
  401. {
  402. while ((descriptor->get_program(handle, programsTotal++)))
  403. continue;
  404. }
  405. LADSPA_Data bufferAudio[bufferSize][audioTotal];
  406. LADSPA_Data bufferParams[parametersTotal];
  407. LADSPA_Data min, max, def;
  408. for (unsigned long j=0, iA=0, iP=0; j < ldescriptor->PortCount; j++)
  409. {
  410. const LADSPA_PortDescriptor portType = ldescriptor->PortDescriptors[j];
  411. const LADSPA_PortRangeHint portHints = ldescriptor->PortRangeHints[j];
  412. if (LADSPA_IS_PORT_AUDIO(portType))
  413. {
  414. carla_zeroF(bufferAudio[iA], bufferSize);
  415. ldescriptor->connect_port(handle, j, bufferAudio[iA++]);
  416. }
  417. else if (LADSPA_IS_PORT_CONTROL(portType))
  418. {
  419. // min value
  420. if (LADSPA_IS_HINT_BOUNDED_BELOW(portHints.HintDescriptor))
  421. min = portHints.LowerBound;
  422. else
  423. min = 0.0f;
  424. // max value
  425. if (LADSPA_IS_HINT_BOUNDED_ABOVE(portHints.HintDescriptor))
  426. max = portHints.UpperBound;
  427. else
  428. max = 1.0f;
  429. if (min > max)
  430. max = min;
  431. else if (max < min)
  432. min = max;
  433. if (max - min == 0.0f)
  434. {
  435. DISCOVERY_OUT("error", "Broken parameter '" << ldescriptor->PortNames[j] << "': max - min == 0");
  436. max = min + 0.1f;
  437. }
  438. // default value
  439. def = get_default_ladspa_port_value(portHints.HintDescriptor, min, max);
  440. if (LADSPA_IS_HINT_SAMPLE_RATE(portHints.HintDescriptor))
  441. {
  442. min *= sampleRate;
  443. max *= sampleRate;
  444. def *= sampleRate;
  445. }
  446. if (LADSPA_IS_PORT_OUTPUT(portType) && (strcmp(ldescriptor->PortNames[j], "latency") == 0 || strcmp(ldescriptor->PortNames[j], "_latency") == 0))
  447. {
  448. // latency parameter
  449. min = 0.0f;
  450. max = sampleRate;
  451. def = 0.0f;
  452. }
  453. if (def < min)
  454. def = min;
  455. else if (def > max)
  456. def = max;
  457. bufferParams[iP] = def;
  458. ldescriptor->connect_port(handle, j, &bufferParams[iP++]);
  459. }
  460. }
  461. // select first midi-program if available
  462. if (programsTotal > 0)
  463. {
  464. const DSSI_Program_Descriptor* pDesc = descriptor->get_program(handle, 0);
  465. CARLA_ASSERT(pDesc);
  466. if (pDesc)
  467. descriptor->select_program(handle, pDesc->Bank, pDesc->Program);
  468. }
  469. if (ldescriptor->activate)
  470. ldescriptor->activate(handle);
  471. if (descriptor->run_synth || descriptor->run_multiple_synths)
  472. {
  473. snd_seq_event_t midiEvents[2];
  474. memset(midiEvents, 0, sizeof(snd_seq_event_t)*2);
  475. const unsigned long midiEventCount = 2;
  476. midiEvents[0].type = SND_SEQ_EVENT_NOTEON;
  477. midiEvents[0].data.note.note = 64;
  478. midiEvents[0].data.note.velocity = 100;
  479. midiEvents[1].type = SND_SEQ_EVENT_NOTEOFF;
  480. midiEvents[1].data.note.note = 64;
  481. midiEvents[1].data.note.velocity = 0;
  482. midiEvents[1].time.tick = bufferSize/2;
  483. if (descriptor->run_multiple_synths && ! descriptor->run_synth)
  484. {
  485. LADSPA_Handle handlePtr[1] = { handle };
  486. snd_seq_event_t* midiEventsPtr[1] = { midiEvents };
  487. unsigned long midiEventCountPtr[1] = { midiEventCount };
  488. descriptor->run_multiple_synths(1, handlePtr, bufferSize, midiEventsPtr, midiEventCountPtr);
  489. }
  490. else
  491. descriptor->run_synth(handle, bufferSize, midiEvents, midiEventCount);
  492. }
  493. else if (ldescriptor->run)
  494. ldescriptor->run(handle, bufferSize);
  495. if (ldescriptor->deactivate)
  496. ldescriptor->deactivate(handle);
  497. if (ldescriptor->cleanup)
  498. ldescriptor->cleanup(handle);
  499. // end crash-free plugin test
  500. // -----------------------------------------------------------------------
  501. }
  502. DISCOVERY_OUT("init", "-----------");
  503. DISCOVERY_OUT("name", ldescriptor->Name);
  504. DISCOVERY_OUT("label", ldescriptor->Label);
  505. DISCOVERY_OUT("maker", ldescriptor->Maker);
  506. DISCOVERY_OUT("copyright", ldescriptor->Copyright);
  507. DISCOVERY_OUT("unique_id", ldescriptor->UniqueID);
  508. DISCOVERY_OUT("hints", hints);
  509. DISCOVERY_OUT("audio.ins", audioIns);
  510. DISCOVERY_OUT("audio.outs", audioOuts);
  511. DISCOVERY_OUT("audio.total", audioTotal);
  512. DISCOVERY_OUT("midi.ins", midiIns);
  513. DISCOVERY_OUT("midi.total", midiTotal);
  514. DISCOVERY_OUT("parameters.ins", parametersIns);
  515. DISCOVERY_OUT("parameters.outs", parametersOuts);
  516. DISCOVERY_OUT("parameters.total", parametersTotal);
  517. DISCOVERY_OUT("programs.total", programsTotal);
  518. DISCOVERY_OUT("build", BINARY_NATIVE);
  519. DISCOVERY_OUT("end", "------------");
  520. }
  521. #else
  522. DISCOVERY_OUT("error", "DSSI support not available");
  523. Q_UNUSED(libHandle);
  524. Q_UNUSED(init);
  525. #endif
  526. }
  527. void do_lv2_check(const char* const bundle, const bool init)
  528. {
  529. #ifdef WANT_LV2
  530. // Convert bundle filename to URI
  531. QString qBundle(QUrl::fromLocalFile(bundle).toString());
  532. if (! qBundle.endsWith(QDir::separator()))
  533. qBundle += QDir::separator();
  534. // Load bundle
  535. Lilv::Node lilvBundle(lv2World.new_uri(qBundle.toUtf8().constData()));
  536. lv2World.load_bundle(lilvBundle);
  537. // Load plugins in this bundle
  538. const Lilv::Plugins lilvPlugins = lv2World.get_all_plugins();
  539. // Get all plugin URIs in this bundle
  540. QStringList URIs;
  541. LILV_FOREACH(plugins, i, lilvPlugins)
  542. {
  543. Lilv::Plugin lilvPlugin(lilv_plugins_get(lilvPlugins, i));
  544. URIs.append(QString(lilvPlugin.get_uri().as_string()));
  545. }
  546. // Get & check every plugin-instance
  547. for (int i=0; i < URIs.count(); i++)
  548. {
  549. const LV2_RDF_Descriptor* const rdf_descriptor = lv2_rdf_new(URIs.at(i).toUtf8().constData());
  550. CARLA_ASSERT(rdf_descriptor && rdf_descriptor->URI);
  551. if (! (rdf_descriptor && rdf_descriptor->URI))
  552. {
  553. DISCOVERY_OUT("error", "Failed to find LV2 plugin '" << URIs.at(i).toUtf8().constData() << "'");
  554. continue;
  555. }
  556. if (init)
  557. {
  558. // test if DLL is loadable
  559. void* const libHandle = lib_open(rdf_descriptor->Binary);
  560. if (! libHandle)
  561. {
  562. print_lib_error(rdf_descriptor->Binary);
  563. delete rdf_descriptor;
  564. continue;
  565. }
  566. lib_close(libHandle);
  567. // test if we support all required ports and features
  568. bool supported = true;
  569. for (uint32_t j=0; j < rdf_descriptor->PortCount; j++)
  570. {
  571. const LV2_RDF_Port* const port = &rdf_descriptor->Ports[j];
  572. bool validPort = (LV2_IS_PORT_CONTROL(port->Type) || LV2_IS_PORT_AUDIO(port->Type) || LV2_IS_PORT_ATOM_SEQUENCE(port->Type) /*|| LV2_IS_PORT_CV(port->Type)*/ || LV2_IS_PORT_EVENT(port->Type) || LV2_IS_PORT_MIDI_LL(port->Type));
  573. if (! (validPort || LV2_IS_PORT_OPTIONAL(port->Properties)))
  574. {
  575. DISCOVERY_OUT("error", "plugin requires a non-supported port type, port-name: " << port->Name);
  576. supported = false;
  577. break;
  578. }
  579. }
  580. for (uint32_t j=0; j < rdf_descriptor->FeatureCount && supported; j++)
  581. {
  582. const LV2_RDF_Feature* const feature = &rdf_descriptor->Features[j];
  583. if (LV2_IS_FEATURE_REQUIRED(feature->Type) && ! is_lv2_feature_supported(feature->URI))
  584. {
  585. DISCOVERY_OUT("error", "plugin requires a non-supported feature " << feature->URI);
  586. supported = false;
  587. break;
  588. }
  589. }
  590. if (! supported)
  591. {
  592. delete rdf_descriptor;
  593. continue;
  594. }
  595. }
  596. int hints = 0;
  597. int audioIns = 0;
  598. int audioOuts = 0;
  599. int audioTotal = 0;
  600. int midiIns = 0;
  601. int midiOuts = 0;
  602. int midiTotal = 0;
  603. int parametersIns = 0;
  604. int parametersOuts = 0;
  605. int parametersTotal = 0;
  606. for (uint32_t j=0; j < rdf_descriptor->PortCount; j++)
  607. {
  608. const LV2_RDF_Port* const port = &rdf_descriptor->Ports[j];
  609. if (LV2_IS_PORT_AUDIO(port->Type))
  610. {
  611. if (LV2_IS_PORT_INPUT(port->Type))
  612. audioIns += 1;
  613. else if (LV2_IS_PORT_OUTPUT(port->Type))
  614. audioOuts += 1;
  615. audioTotal += 1;
  616. }
  617. else if (LV2_IS_PORT_CONTROL(port->Type))
  618. {
  619. if (LV2_IS_PORT_DESIGNATION_LATENCY(port->Designation) || LV2_IS_PORT_DESIGNATION_SAMPLE_RATE(port->Designation) ||
  620. LV2_IS_PORT_DESIGNATION_FREEWHEELING(port->Designation) || LV2_IS_PORT_DESIGNATION_TIME(port->Designation))
  621. {
  622. pass();
  623. }
  624. else
  625. {
  626. if (LV2_IS_PORT_INPUT(port->Type))
  627. parametersIns += 1;
  628. else if (LV2_IS_PORT_OUTPUT(port->Type))
  629. parametersOuts += 1;
  630. parametersTotal += 1;
  631. }
  632. }
  633. else if (port->Type & LV2_PORT_SUPPORTS_MIDI_EVENT)
  634. {
  635. if (LV2_IS_PORT_INPUT(port->Type))
  636. midiIns += 1;
  637. else if (LV2_IS_PORT_OUTPUT(port->Type))
  638. midiOuts += 1;
  639. midiTotal += 1;
  640. }
  641. }
  642. if (rdf_descriptor->Type & LV2_CLASS_INSTRUMENT)
  643. hints |= PLUGIN_IS_SYNTH;
  644. if (rdf_descriptor->UICount > 0)
  645. hints |= PLUGIN_HAS_GUI;
  646. DISCOVERY_OUT("init", "-----------");
  647. DISCOVERY_OUT("label", rdf_descriptor->URI);
  648. if (rdf_descriptor->Name)
  649. DISCOVERY_OUT("name", rdf_descriptor->Name);
  650. if (rdf_descriptor->Author)
  651. DISCOVERY_OUT("maker", rdf_descriptor->Author);
  652. if (rdf_descriptor->License)
  653. DISCOVERY_OUT("copyright", rdf_descriptor->License);
  654. DISCOVERY_OUT("unique_id", rdf_descriptor->UniqueID);
  655. DISCOVERY_OUT("hints", hints);
  656. DISCOVERY_OUT("audio.ins", audioIns);
  657. DISCOVERY_OUT("audio.outs", audioOuts);
  658. DISCOVERY_OUT("audio.total", audioTotal);
  659. DISCOVERY_OUT("midi.ins", midiIns);
  660. DISCOVERY_OUT("midi.outs", midiOuts);
  661. DISCOVERY_OUT("midi.total", midiTotal);
  662. DISCOVERY_OUT("parameters.ins", parametersIns);
  663. DISCOVERY_OUT("parameters.outs", parametersOuts);
  664. DISCOVERY_OUT("parameters.total", parametersTotal);
  665. DISCOVERY_OUT("build", BINARY_NATIVE);
  666. DISCOVERY_OUT("end", "------------");
  667. delete rdf_descriptor;
  668. }
  669. #else
  670. DISCOVERY_OUT("error", "LV2 support not available");
  671. Q_UNUSED(bundle);
  672. Q_UNUSED(init);
  673. #endif
  674. }
  675. void do_vst_check(void* const libHandle, const bool init)
  676. {
  677. #ifdef WANT_VST
  678. VST_Function vstFn = (VST_Function)lib_symbol(libHandle, "VSTPluginMain");
  679. if (! vstFn)
  680. vstFn = (VST_Function)lib_symbol(libHandle, "main");
  681. if (! vstFn)
  682. {
  683. DISCOVERY_OUT("error", "Not a VST plugin");
  684. return;
  685. }
  686. AEffect* const effect = vstFn(vstHostCallback);
  687. if (! (effect && effect->magic == kEffectMagic))
  688. {
  689. DISCOVERY_OUT("error", "Failed to init VST plugin, or VST magic failed");
  690. return;
  691. }
  692. const char* cName;
  693. const char* cProduct;
  694. const char* cVendor;
  695. char strBuf[255] = { 0 };
  696. effect->dispatcher(effect, effOpen, 0, 0, nullptr, 0.0f);
  697. effect->dispatcher(effect, effGetEffectName, 0, 0, strBuf, 0.0f);
  698. cName = strdup((strBuf[0] != 0) ? strBuf : "");
  699. strBuf[0] = 0;
  700. effect->dispatcher(effect, effGetProductString, 0, 0, strBuf, 0.0f);
  701. cProduct = strdup((strBuf[0] != 0) ? strBuf : "");
  702. strBuf[0] = 0;
  703. effect->dispatcher(effect, effGetVendorString, 0, 0, strBuf, 0.0f);
  704. cVendor = strdup((strBuf[0] != 0) ? strBuf : "");
  705. vstCurrentUniqueId = effect->uniqueID;
  706. intptr_t vstCategory = effect->dispatcher(effect, effGetPlugCategory, 0, 0, nullptr, 0.0f);
  707. while (true)
  708. {
  709. int hints = 0;
  710. int audioIns = effect->numInputs;
  711. int audioOuts = effect->numOutputs;
  712. int audioTotal = audioIns + audioOuts;
  713. int midiIns = 0;
  714. int midiOuts = 0;
  715. int midiTotal = 0;
  716. int parametersIns = effect->numParams;
  717. int parametersTotal = parametersIns;
  718. int programsTotal = effect->numPrograms;
  719. if (effect->flags & effFlagsHasEditor)
  720. hints |= PLUGIN_HAS_GUI;
  721. if (effect->flags & effFlagsIsSynth)
  722. hints |= PLUGIN_IS_SYNTH;
  723. if (vstPluginCanDo(effect, "receiveVstEvents") || vstPluginCanDo(effect, "receiveVstMidiEvent") || vstWantsMidi || (effect->flags & effFlagsIsSynth) > 0)
  724. midiIns = 1;
  725. if (vstPluginCanDo(effect, "sendVstEvents") || vstPluginCanDo(effect, "sendVstMidiEvent"))
  726. midiOuts = 1;
  727. midiTotal = midiIns + midiOuts;
  728. // -----------------------------------------------------------------------
  729. // start crash-free plugin test
  730. if (init)
  731. {
  732. float** bufferAudioIn = new float* [audioIns];
  733. for (int j=0; j < audioIns; j++)
  734. {
  735. bufferAudioIn[j] = new float [bufferSize];
  736. memset(bufferAudioIn[j], 0, sizeof(float)*bufferSize);
  737. }
  738. float** bufferAudioOut = new float* [audioOuts];
  739. for (int j=0; j < audioOuts; j++)
  740. {
  741. bufferAudioOut[j] = new float [bufferSize];
  742. memset(bufferAudioOut[j], 0, sizeof(float)*bufferSize);
  743. }
  744. struct {
  745. int32_t numEvents;
  746. intptr_t reserved;
  747. VstEvent* data[2];
  748. } events;
  749. VstMidiEvent midiEvents[2];
  750. memset(midiEvents, 0, sizeof(VstMidiEvent)*2);
  751. midiEvents[0].type = kVstMidiType;
  752. midiEvents[0].byteSize = sizeof(VstMidiEvent);
  753. midiEvents[0].midiData[0] = 0x90;
  754. midiEvents[0].midiData[1] = 64;
  755. midiEvents[0].midiData[2] = 100;
  756. midiEvents[1].type = kVstMidiType;
  757. midiEvents[1].byteSize = sizeof(VstMidiEvent);
  758. midiEvents[1].midiData[0] = 0x80;
  759. midiEvents[1].midiData[1] = 64;
  760. midiEvents[1].deltaFrames = bufferSize/2;
  761. events.numEvents = 2;
  762. events.reserved = 0;
  763. events.data[0] = (VstEvent*)&midiEvents[0];
  764. events.data[1] = (VstEvent*)&midiEvents[1];
  765. #if ! VST_FORCE_DEPRECATED
  766. effect->dispatcher(effect, effSetBlockSizeAndSampleRate, 0, bufferSize, nullptr, sampleRate);
  767. #endif
  768. effect->dispatcher(effect, effSetBlockSize, 0, bufferSize, nullptr, 0.0f);
  769. effect->dispatcher(effect, effSetSampleRate, 0, 0, nullptr, sampleRate);
  770. effect->dispatcher(effect, effSetProcessPrecision, 0, kVstProcessPrecision32, nullptr, 0.0f);
  771. effect->dispatcher(effect, effMainsChanged, 0, 1, nullptr, 0.0f);
  772. effect->dispatcher(effect, effStartProcess, 0, 0, nullptr, 0.0f);
  773. if (midiIns > 0)
  774. effect->dispatcher(effect, effProcessEvents, 0, 0, &events, 0.0f);
  775. #if ! VST_FORCE_DEPRECATED
  776. if ((effect->flags & effFlagsCanReplacing) > 0 && effect->processReplacing != effect->process)
  777. effect->processReplacing(effect, bufferAudioIn, bufferAudioOut, bufferSize);
  778. else
  779. effect->process(effect, bufferAudioIn, bufferAudioOut, bufferSize);
  780. #else
  781. CARLA_ASSERT(effect->flags & effFlagsCanReplacing);
  782. if (effect->flags & effFlagsCanReplacing)
  783. effect->processReplacing(effect, bufferAudioIn, bufferAudioOut, bufferSize);
  784. #endif
  785. effect->dispatcher(effect, effStopProcess, 0, 0, nullptr, 0.0f);
  786. effect->dispatcher(effect, effMainsChanged, 0, 0, nullptr, 0.0f);
  787. for (int j=0; j < audioIns; j++)
  788. delete[] bufferAudioIn[j];
  789. for (int j=0; j < audioOuts; j++)
  790. delete[] bufferAudioOut[j];
  791. delete[] bufferAudioIn;
  792. delete[] bufferAudioOut;
  793. }
  794. // end crash-free plugin test
  795. // -----------------------------------------------------------------------
  796. DISCOVERY_OUT("init", "-----------");
  797. DISCOVERY_OUT("name", cName);
  798. DISCOVERY_OUT("label", cProduct);
  799. DISCOVERY_OUT("maker", cVendor);
  800. DISCOVERY_OUT("copyright", cVendor);
  801. DISCOVERY_OUT("unique_id", vstCurrentUniqueId);
  802. DISCOVERY_OUT("hints", hints);
  803. DISCOVERY_OUT("audio.ins", audioIns);
  804. DISCOVERY_OUT("audio.outs", audioOuts);
  805. DISCOVERY_OUT("audio.total", audioTotal);
  806. DISCOVERY_OUT("midi.ins", midiIns);
  807. DISCOVERY_OUT("midi.outs", midiOuts);
  808. DISCOVERY_OUT("midi.total", midiTotal);
  809. DISCOVERY_OUT("parameters.ins", parametersIns);
  810. DISCOVERY_OUT("parameters.total", parametersTotal);
  811. DISCOVERY_OUT("programs.total", programsTotal);
  812. DISCOVERY_OUT("build", BINARY_NATIVE);
  813. DISCOVERY_OUT("end", "------------");
  814. if (vstCategory != kPlugCategShell)
  815. break;
  816. strBuf[0] = 0;
  817. vstWantsMidi = false;
  818. vstCurrentUniqueId = effect->dispatcher(effect, effShellGetNextPlugin, 0, 0, strBuf, 0.0f);
  819. if (vstCurrentUniqueId != 0)
  820. {
  821. free((void*)cName);
  822. cName = strdup((strBuf[0] != 0) ? strBuf : "");
  823. }
  824. else
  825. break;
  826. }
  827. effect->dispatcher(effect, effClose, 0, 0, nullptr, 0.0f);
  828. free((void*)cName);
  829. free((void*)cProduct);
  830. free((void*)cVendor);
  831. #else
  832. DISCOVERY_OUT("error", "VST support not available");
  833. Q_UNUSED(libHandle);
  834. Q_UNUSED(init);
  835. #endif
  836. }
  837. void do_fluidsynth_check(const char* const filename, const bool init)
  838. {
  839. #ifdef WANT_FLUIDSYNTH
  840. if (! fluid_is_soundfont(filename))
  841. {
  842. DISCOVERY_OUT("error", "Not a SF2 file");
  843. return;
  844. }
  845. int programs = 0;
  846. if (init)
  847. {
  848. fluid_settings_t* const f_settings = new_fluid_settings();
  849. fluid_synth_t* const f_synth = new_fluid_synth(f_settings);
  850. const int f_id = fluid_synth_sfload(f_synth, filename, 0);
  851. if (f_id < 0)
  852. {
  853. DISCOVERY_OUT("error", "Failed to load SF2 file");
  854. return;
  855. }
  856. fluid_sfont_t* f_sfont;
  857. fluid_preset_t f_preset;
  858. f_sfont = fluid_synth_get_sfont_by_id(f_synth, f_id);
  859. f_sfont->iteration_start(f_sfont);
  860. while (f_sfont->iteration_next(f_sfont, &f_preset))
  861. programs += 1;
  862. delete_fluid_synth(f_synth);
  863. delete_fluid_settings(f_settings);
  864. }
  865. DISCOVERY_OUT("init", "-----------");
  866. DISCOVERY_OUT("name", "");
  867. DISCOVERY_OUT("label", "");
  868. DISCOVERY_OUT("maker", "");
  869. DISCOVERY_OUT("copyright", "");
  870. DISCOVERY_OUT("hints", PLUGIN_IS_SYNTH);
  871. DISCOVERY_OUT("audio.outs", 2);
  872. DISCOVERY_OUT("audio.total", 2);
  873. DISCOVERY_OUT("midi.ins", 1);
  874. DISCOVERY_OUT("midi.total", 1);
  875. DISCOVERY_OUT("programs.total", programs);
  876. DISCOVERY_OUT("parameters.ins", 13); // defined in Carla
  877. DISCOVERY_OUT("parameters.outs", 1);
  878. DISCOVERY_OUT("parameters.total", 14);
  879. DISCOVERY_OUT("build", BINARY_NATIVE);
  880. DISCOVERY_OUT("end", "------------");
  881. #else
  882. DISCOVERY_OUT("error", "SF2 support not available");
  883. Q_UNUSED(filename);
  884. Q_UNUSED(init);
  885. #endif
  886. }
  887. void do_linuxsampler_check(const char* const filename, const char* const stype, const bool init)
  888. {
  889. #ifdef WANT_LINUXSAMPLER
  890. const QFileInfo file(filename);
  891. if (! file.exists())
  892. {
  893. DISCOVERY_OUT("error", "Requested file does not exist");
  894. return;
  895. }
  896. if (! file.isFile())
  897. {
  898. DISCOVERY_OUT("error", "Requested file is not valid");
  899. return;
  900. }
  901. if (! file.isReadable())
  902. {
  903. DISCOVERY_OUT("error", "Requested file is not readable");
  904. return;
  905. }
  906. using namespace LinuxSampler;
  907. class LinuxSamplerScopedEngine {
  908. public:
  909. LinuxSamplerScopedEngine(const char* const filename, const char* const stype)
  910. {
  911. engine = nullptr;
  912. try {
  913. engine = EngineFactory::Create(stype);
  914. }
  915. catch (const Exception& e)
  916. {
  917. DISCOVERY_OUT("error", e.what());
  918. return;
  919. }
  920. if (! engine)
  921. return;
  922. ins = engine->GetInstrumentManager();
  923. if (! ins)
  924. {
  925. DISCOVERY_OUT("error", "Failed to get LinuxSampler instrument manager");
  926. return;
  927. }
  928. std::vector<InstrumentManager::instrument_id_t> ids;
  929. try {
  930. ids = ins->GetInstrumentFileContent(filename);
  931. }
  932. catch (const Exception& e)
  933. {
  934. DISCOVERY_OUT("error", e.what());
  935. return;
  936. }
  937. if (ids.size() > 0)
  938. {
  939. InstrumentManager::instrument_info_t info = ins->GetInstrumentInfo(ids[0]);
  940. outputInfo(&info, ids.size());
  941. }
  942. }
  943. ~LinuxSamplerScopedEngine()
  944. {
  945. if (engine)
  946. EngineFactory::Destroy(engine);
  947. }
  948. static void outputInfo(InstrumentManager::instrument_info_t* const info, const int programs, const char* const basename = nullptr)
  949. {
  950. DISCOVERY_OUT("init", "-----------");
  951. if (info)
  952. {
  953. DISCOVERY_OUT("name", info->InstrumentName);
  954. DISCOVERY_OUT("label", info->Product);
  955. DISCOVERY_OUT("maker", info->Artists);
  956. DISCOVERY_OUT("copyright", info->Artists);
  957. }
  958. else
  959. {
  960. DISCOVERY_OUT("name", basename);
  961. DISCOVERY_OUT("label", basename);
  962. }
  963. DISCOVERY_OUT("hints", PLUGIN_IS_SYNTH);
  964. DISCOVERY_OUT("audio.outs", 2);
  965. DISCOVERY_OUT("audio.total", 2);
  966. DISCOVERY_OUT("midi.ins", 1);
  967. DISCOVERY_OUT("midi.total", 1);
  968. DISCOVERY_OUT("programs.total", programs);
  969. //DISCOVERY_OUT("parameters.ins", 13); // defined in Carla - TODO
  970. //DISCOVERY_OUT("parameters.outs", 1);
  971. //DISCOVERY_OUT("parameters.total", 14);
  972. DISCOVERY_OUT("build", BINARY_NATIVE);
  973. DISCOVERY_OUT("end", "------------");
  974. }
  975. private:
  976. Engine* engine;
  977. InstrumentManager* ins;
  978. };
  979. if (init)
  980. const LinuxSamplerScopedEngine engine(filename, stype);
  981. else
  982. LinuxSamplerScopedEngine::outputInfo(nullptr, 0, file.baseName().toUtf8().constData());
  983. #else
  984. DISCOVERY_OUT("error", stype << " support not available");
  985. Q_UNUSED(filename);
  986. Q_UNUSED(init);
  987. #endif
  988. }
  989. // ------------------------------ main entry point ------------------------------
  990. int main(int argc, char* argv[])
  991. {
  992. if (argc == 2 && strcmp(argv[1], "-formats") == 0)
  993. {
  994. printf("Available plugin formats:\n");
  995. printf("LADSPA: ");
  996. #ifdef WANT_LADSPA
  997. printf("yes\n");
  998. #else
  999. printf("no\n");
  1000. #endif
  1001. printf("DSSI: ");
  1002. #ifdef WANT_DSSI
  1003. printf("yes\n");
  1004. #else
  1005. printf("no\n");
  1006. #endif
  1007. printf("LV2: ");
  1008. #ifdef WANT_LV2
  1009. printf("yes\n");
  1010. #else
  1011. printf("no\n");
  1012. #endif
  1013. printf("VST: ");
  1014. #ifdef WANT_VST
  1015. printf("yes\n");
  1016. #else
  1017. printf("no\n");
  1018. #endif
  1019. printf("\n");
  1020. printf("Available sampler formats:\n");
  1021. printf("GIG (LinuxSampler): ");
  1022. #ifdef WANT_LINUXSAMPLER
  1023. printf("yes\n");
  1024. #else
  1025. printf("no\n");
  1026. #endif
  1027. printf("SF2 (FluidSynth): ");
  1028. #ifdef WANT_FLUIDSYNTH
  1029. printf("yes\n");
  1030. #else
  1031. printf("no\n");
  1032. #endif
  1033. printf("SFZ (LinuxSampler): ");
  1034. #ifdef WANT_LINUXSAMPLER
  1035. printf("yes\n");
  1036. #else
  1037. printf("no\n");
  1038. #endif
  1039. return 0;
  1040. }
  1041. if (argc != 3)
  1042. {
  1043. qWarning("usage: %s <type> </path/to/plugin>", argv[0]);
  1044. return 1;
  1045. }
  1046. const char* const stype = argv[1];
  1047. const char* const filename = argv[2];
  1048. bool openLib;
  1049. PluginType type;
  1050. void* handle = nullptr;
  1051. if (strcmp(stype, "LADSPA") == 0)
  1052. {
  1053. openLib = true;
  1054. type = PLUGIN_LADSPA;
  1055. }
  1056. else if (strcmp(stype, "DSSI") == 0)
  1057. {
  1058. openLib = true;
  1059. type = PLUGIN_DSSI;
  1060. }
  1061. else if (strcmp(stype, "LV2") == 0)
  1062. {
  1063. openLib = false;
  1064. type = PLUGIN_LV2;
  1065. }
  1066. else if (strcmp(stype, "VST") == 0)
  1067. {
  1068. openLib = true;
  1069. type = PLUGIN_VST;
  1070. }
  1071. else if (strcmp(stype, "GIG") == 0)
  1072. {
  1073. openLib = false;
  1074. type = PLUGIN_GIG;
  1075. }
  1076. else if (strcmp(stype, "SF2") == 0)
  1077. {
  1078. openLib = false;
  1079. type = PLUGIN_SF2;
  1080. }
  1081. else if (strcmp(stype, "SFZ") == 0)
  1082. {
  1083. openLib = false;
  1084. type = PLUGIN_SFZ;
  1085. }
  1086. else
  1087. {
  1088. DISCOVERY_OUT("error", "Invalid plugin type");
  1089. return 1;
  1090. }
  1091. if (openLib)
  1092. {
  1093. handle = lib_open(filename);
  1094. if (! handle)
  1095. {
  1096. print_lib_error(filename);
  1097. return 1;
  1098. }
  1099. }
  1100. bool doInit = ! QString(filename).endsWith("dssi-vst.so", Qt::CaseInsensitive);
  1101. if (doInit && getenv("CARLA_DISCOVERY_NO_PROCESSING_CHECKS"))
  1102. doInit = false;
  1103. switch (type)
  1104. {
  1105. case PLUGIN_LADSPA:
  1106. do_ladspa_check(handle, doInit);
  1107. break;
  1108. case PLUGIN_DSSI:
  1109. do_dssi_check(handle, doInit);
  1110. break;
  1111. case PLUGIN_LV2:
  1112. do_lv2_check(filename, doInit);
  1113. break;
  1114. case PLUGIN_VST:
  1115. do_vst_check(handle, doInit);
  1116. break;
  1117. case PLUGIN_GIG:
  1118. do_linuxsampler_check(filename, "gig", doInit);
  1119. break;
  1120. case PLUGIN_SF2:
  1121. do_fluidsynth_check(filename, doInit);
  1122. break;
  1123. case PLUGIN_SFZ:
  1124. do_linuxsampler_check(filename, "sfz", doInit);
  1125. break;
  1126. default:
  1127. break;
  1128. }
  1129. if (openLib)
  1130. lib_close(handle);
  1131. return 0;
  1132. }