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.

733 lines
24KB

  1. /*
  2. * Carla Native Plugins
  3. * Copyright (C) 2013-2021 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 "CarlaNativePrograms.hpp"
  18. #include "CarlaString.hpp"
  19. #include "audio-base.hpp"
  20. static const char* const audiofilesWildcard =
  21. #ifdef HAVE_SNDFILE
  22. "*.aif;*.aifc;*.aiff;*.au;*.bwf;*.flac;*.htk;*.iff;*.mat4;*.mat5;*.oga;*.ogg;"
  23. "*.paf;*.pvf;*.pvf5;*.sd2;*.sf;*.snd;*.svx;*.vcc;*.w64;*.wav;*.xi;"
  24. #endif
  25. #ifdef HAVE_FFMPEG
  26. "*.3g2;*.3gp;*.aac;*.ac3;*.amr;*.ape;*.mp2;*.mp3;*.mpc;*.wma;"
  27. # ifndef HAVE_SNDFILE
  28. "*.flac;*.oga;*.ogg;*.w64;*.wav;"
  29. # endif
  30. #else
  31. "*.mp3;"
  32. #endif
  33. #if !defined(HAVE_SNDFILE) && !defined(HAVE_FFMPEG)
  34. ""
  35. # ifndef BUILDING_FOR_CI
  36. # warning sndfile and ffmpeg libraries missing, no audio file support will be available
  37. # endif
  38. #endif
  39. ;
  40. // -----------------------------------------------------------------------
  41. class AudioFilePlugin : public NativePluginWithMidiPrograms<FileAudio>
  42. {
  43. public:
  44. #ifndef __MOD_DEVICES__
  45. typedef enum _PendingInlineDisplay
  46. # ifdef CARLA_PROPER_CPP11_SUPPORT
  47. : uint8_t
  48. # endif
  49. {
  50. InlineDisplayNotPending,
  51. InlineDisplayNeedRequest,
  52. InlineDisplayRequesting
  53. } PendingInlineDisplay;
  54. #endif
  55. enum Parameters {
  56. kParameterLooping,
  57. kParameterInfoChannels,
  58. kParameterInfoBitRate,
  59. kParameterInfoBitDepth,
  60. kParameterInfoSampleRate,
  61. kParameterInfoLength,
  62. kParameterInfoPosition,
  63. kParameterInfoPoolFill,
  64. kParameterCount
  65. };
  66. AudioFilePlugin(const NativeHostDescriptor* const host)
  67. : NativePluginWithMidiPrograms<FileAudio>(host, fPrograms, 2),
  68. fLoopMode(true),
  69. fDoProcess(false),
  70. fWasPlayingBefore(false),
  71. fNeedsFileRead(false),
  72. fEntireFileLoaded(false),
  73. fMaxFrame(0),
  74. fLastPosition(0.0f),
  75. fLastPoolFill(0.0f),
  76. fPool(),
  77. fReader(),
  78. fPrograms(hostGetFilePath("audio"), audiofilesWildcard),
  79. fPreviewData()
  80. #ifndef __MOD_DEVICES__
  81. , fInlineDisplay()
  82. #endif
  83. {
  84. }
  85. ~AudioFilePlugin() override
  86. {
  87. fReader.destroy();
  88. fPool.destroy();
  89. }
  90. protected:
  91. // -------------------------------------------------------------------
  92. // Plugin parameter calls
  93. uint32_t getParameterCount() const override
  94. {
  95. return kParameterCount;
  96. }
  97. const NativeParameter* getParameterInfo(const uint32_t index) const override
  98. {
  99. static NativeParameter param;
  100. param.scalePointCount = 0;
  101. param.scalePoints = nullptr;
  102. param.unit = nullptr;
  103. param.ranges.step = 1.0f;
  104. param.ranges.stepSmall = 1.0f;
  105. param.ranges.stepLarge = 1.0f;
  106. switch (index)
  107. {
  108. case kParameterLooping:
  109. param.name = "Loop Mode";
  110. param.hints = static_cast<NativeParameterHints>(NATIVE_PARAMETER_IS_AUTOMABLE|
  111. NATIVE_PARAMETER_IS_ENABLED|
  112. NATIVE_PARAMETER_IS_BOOLEAN);
  113. param.ranges.def = 1.0f;
  114. param.ranges.min = 0.0f;
  115. param.ranges.max = 1.0f;
  116. break;
  117. case kParameterInfoChannels:
  118. param.name = "Num Channels";
  119. param.hints = static_cast<NativeParameterHints>(NATIVE_PARAMETER_IS_AUTOMABLE|
  120. NATIVE_PARAMETER_IS_ENABLED|
  121. NATIVE_PARAMETER_IS_INTEGER|
  122. NATIVE_PARAMETER_IS_OUTPUT);
  123. param.ranges.def = 0.0f;
  124. param.ranges.min = 0.0f;
  125. param.ranges.max = 2.0f;
  126. break;
  127. case kParameterInfoBitRate:
  128. param.name = "Bit Rate";
  129. param.hints = static_cast<NativeParameterHints>(NATIVE_PARAMETER_IS_AUTOMABLE|
  130. NATIVE_PARAMETER_IS_ENABLED|
  131. NATIVE_PARAMETER_IS_INTEGER|
  132. NATIVE_PARAMETER_IS_OUTPUT);
  133. param.ranges.def = 0.0f;
  134. param.ranges.min = -1.0f;
  135. param.ranges.max = 384000.0f * 64.0f * 2.0f;
  136. break;
  137. case kParameterInfoBitDepth:
  138. param.name = "Bit Depth";
  139. param.hints = static_cast<NativeParameterHints>(NATIVE_PARAMETER_IS_AUTOMABLE|
  140. NATIVE_PARAMETER_IS_ENABLED|
  141. NATIVE_PARAMETER_IS_INTEGER|
  142. NATIVE_PARAMETER_IS_OUTPUT);
  143. param.ranges.def = 0.0f;
  144. param.ranges.min = 0.0f;
  145. param.ranges.max = 64.0f;
  146. break;
  147. case kParameterInfoSampleRate:
  148. param.name = "Sample Rate";
  149. param.hints = static_cast<NativeParameterHints>(NATIVE_PARAMETER_IS_AUTOMABLE|
  150. NATIVE_PARAMETER_IS_ENABLED|
  151. NATIVE_PARAMETER_IS_INTEGER|
  152. NATIVE_PARAMETER_IS_OUTPUT);
  153. param.ranges.def = 0.0f;
  154. param.ranges.min = 0.0f;
  155. param.ranges.max = 384000.0f;
  156. break;
  157. case kParameterInfoLength:
  158. param.name = "Length";
  159. param.hints = static_cast<NativeParameterHints>(NATIVE_PARAMETER_IS_AUTOMABLE|
  160. NATIVE_PARAMETER_IS_ENABLED|
  161. NATIVE_PARAMETER_IS_OUTPUT);
  162. param.ranges.def = 0.0f;
  163. param.ranges.min = 0.0f;
  164. param.ranges.max = (float)INT64_MAX;
  165. param.unit = "s";
  166. break;
  167. case kParameterInfoPosition:
  168. param.name = "Position";
  169. param.hints = static_cast<NativeParameterHints>(NATIVE_PARAMETER_IS_AUTOMABLE|
  170. NATIVE_PARAMETER_IS_ENABLED|
  171. NATIVE_PARAMETER_IS_OUTPUT);
  172. param.ranges.def = 0.0f;
  173. param.ranges.min = 0.0f;
  174. param.ranges.max = 100.0f;
  175. param.unit = "%";
  176. break;
  177. case kParameterInfoPoolFill:
  178. param.name = "Pool Fill";
  179. param.hints = static_cast<NativeParameterHints>(NATIVE_PARAMETER_IS_AUTOMABLE|
  180. NATIVE_PARAMETER_IS_ENABLED|
  181. NATIVE_PARAMETER_IS_OUTPUT);
  182. param.ranges.def = 0.0f;
  183. param.ranges.min = 0.0f;
  184. param.ranges.max = 100.0f;
  185. param.unit = "%";
  186. break;
  187. default:
  188. return nullptr;
  189. }
  190. return &param;
  191. }
  192. float getParameterValue(const uint32_t index) const override
  193. {
  194. if (index == kParameterLooping)
  195. return fLoopMode ? 1.0f : 0.0f;
  196. if (index == kParameterInfoBitRate)
  197. return static_cast<float>(fReader.getCurrentBitRate());
  198. const ADInfo nfo = fReader.getFileInfo();
  199. switch (index)
  200. {
  201. case kParameterInfoChannels:
  202. return static_cast<float>(nfo.channels);
  203. case kParameterInfoBitDepth:
  204. return static_cast<float>(nfo.bit_depth);
  205. case kParameterInfoSampleRate:
  206. return static_cast<float>(nfo.sample_rate);
  207. case kParameterInfoLength:
  208. return static_cast<float>(nfo.length)/1000.0f;
  209. case kParameterInfoPosition:
  210. return fLastPosition;
  211. case kParameterInfoPoolFill:
  212. return fLastPoolFill;
  213. default:
  214. return 0.0f;
  215. }
  216. }
  217. // -------------------------------------------------------------------
  218. // Plugin state calls
  219. void setParameterValue(const uint32_t index, const float value) override
  220. {
  221. if (index != kParameterLooping)
  222. return;
  223. bool b = (value > 0.5f);
  224. if (b == fLoopMode)
  225. return;
  226. fLoopMode = b;
  227. fReader.setLoopingMode(b);
  228. }
  229. void setCustomData(const char* const key, const char* const value) override
  230. {
  231. if (std::strcmp(key, "file") != 0)
  232. return;
  233. invalidateNextFilename();
  234. loadFilename(value);
  235. }
  236. // -------------------------------------------------------------------
  237. // Plugin process calls
  238. void process2(const float* const*, float** const outBuffer, const uint32_t frames,
  239. const NativeMidiEvent*, uint32_t) override
  240. {
  241. const NativeTimeInfo* const timePos(getTimeInfo());
  242. const bool loopMode = fLoopMode;
  243. float* out1 = outBuffer[0];
  244. float* out2 = outBuffer[1];
  245. bool needsIdleRequest = false;
  246. const water::GenericScopedLock<water::SpinLock> gsl(fPool.mutex);
  247. if (! fDoProcess)
  248. {
  249. // carla_stderr("P: no process");
  250. carla_zeroFloats(out1, frames);
  251. carla_zeroFloats(out2, frames);
  252. fLastPosition = 0.0f;
  253. return;
  254. }
  255. // not playing
  256. if (! timePos->playing)
  257. {
  258. // carla_stderr("P: not playing");
  259. if (timePos->frame == 0 && fWasPlayingBefore)
  260. fReader.setNeedsRead(timePos->frame);
  261. carla_zeroFloats(out1, frames);
  262. carla_zeroFloats(out2, frames);
  263. fWasPlayingBefore = false;
  264. return;
  265. }
  266. else
  267. {
  268. fWasPlayingBefore = true;
  269. }
  270. // out of reach
  271. if ((timePos->frame < fPool.startFrame || timePos->frame >= fMaxFrame) && !loopMode)
  272. {
  273. if (timePos->frame < fPool.startFrame)
  274. {
  275. needsIdleRequest = true;
  276. fNeedsFileRead = true;
  277. fReader.setNeedsRead(timePos->frame);
  278. }
  279. carla_zeroFloats(out1, frames);
  280. carla_zeroFloats(out2, frames);
  281. #ifndef __MOD_DEVICES__
  282. if (fInlineDisplay.writtenValues < 32)
  283. {
  284. fInlineDisplay.lastValuesL[fInlineDisplay.writtenValues] = 0.0f;
  285. fInlineDisplay.lastValuesR[fInlineDisplay.writtenValues] = 0.0f;
  286. ++fInlineDisplay.writtenValues;
  287. }
  288. if (fInlineDisplay.pending == InlineDisplayNotPending)
  289. {
  290. needsIdleRequest = true;
  291. fInlineDisplay.pending = InlineDisplayNeedRequest;
  292. }
  293. #endif
  294. if (needsIdleRequest)
  295. hostRequestIdle();
  296. if (timePos->frame == 0)
  297. fLastPosition = 0.0f;
  298. else if (timePos->frame >= fMaxFrame)
  299. fLastPosition = 100.0f;
  300. else
  301. fLastPosition = static_cast<float>(timePos->frame) / static_cast<float>(fMaxFrame) * 100.0f;
  302. return;
  303. }
  304. if (fEntireFileLoaded)
  305. {
  306. // NOTE: timePos->frame is always < fMaxFrame (or looping)
  307. uint32_t targetStartFrame = static_cast<uint32_t>(loopMode ? timePos->frame % fMaxFrame : timePos->frame);
  308. for (uint32_t framesDone=0, framesToDo=frames, remainingFrames; framesDone < frames;)
  309. {
  310. if (targetStartFrame + framesToDo <= fMaxFrame)
  311. {
  312. // everything fits together
  313. carla_copyFloats(out1+framesDone, fPool.buffer[0]+targetStartFrame, framesToDo);
  314. carla_copyFloats(out2+framesDone, fPool.buffer[1]+targetStartFrame, framesToDo);
  315. break;
  316. }
  317. remainingFrames = std::min(fMaxFrame - targetStartFrame, framesToDo);
  318. carla_copyFloats(out1+framesDone, fPool.buffer[0]+targetStartFrame, remainingFrames);
  319. carla_copyFloats(out2+framesDone, fPool.buffer[1]+targetStartFrame, remainingFrames);
  320. framesDone += remainingFrames;
  321. framesToDo -= remainingFrames;
  322. if (! loopMode)
  323. {
  324. // not looping, stop here
  325. if (framesToDo != 0)
  326. {
  327. carla_zeroFloats(out1+framesDone, framesToDo);
  328. carla_zeroFloats(out2+framesDone, framesToDo);
  329. }
  330. break;
  331. }
  332. // reset for next loop
  333. targetStartFrame = 0;
  334. }
  335. fLastPosition = static_cast<float>(targetStartFrame) / static_cast<float>(fMaxFrame) * 100.0f;
  336. }
  337. else
  338. {
  339. const bool offline = isOffline();
  340. if (! fReader.tryPutData(fPool, out1, out2, timePos->frame, frames, loopMode, offline, needsIdleRequest))
  341. {
  342. carla_zeroFloats(out1, frames);
  343. carla_zeroFloats(out2, frames);
  344. }
  345. if (needsIdleRequest)
  346. {
  347. fNeedsFileRead = true;
  348. if (isOffline())
  349. {
  350. needsIdleRequest = false;
  351. fReader.readPoll();
  352. if (! fReader.tryPutData(fPool, out1, out2, timePos->frame, frames, loopMode, offline, needsIdleRequest))
  353. {
  354. carla_zeroFloats(out1, frames);
  355. carla_zeroFloats(out2, frames);
  356. }
  357. if (needsIdleRequest)
  358. fNeedsFileRead = true;
  359. }
  360. }
  361. const uint32_t modframe = static_cast<uint32_t>(timePos->frame % fMaxFrame);
  362. fLastPosition = static_cast<float>(modframe) / static_cast<float>(fMaxFrame) * 100.0f;
  363. if (modframe > fPool.startFrame)
  364. fLastPoolFill = static_cast<float>(modframe - fPool.startFrame) / static_cast<float>(fPool.numFrames) * 100.0f;
  365. else
  366. fLastPoolFill = 100.0f;
  367. }
  368. #ifndef __MOD_DEVICES__
  369. if (fInlineDisplay.writtenValues < 32)
  370. {
  371. fInlineDisplay.lastValuesL[fInlineDisplay.writtenValues] = carla_findMaxNormalizedFloat(out1, frames);
  372. fInlineDisplay.lastValuesR[fInlineDisplay.writtenValues] = carla_findMaxNormalizedFloat(out2, frames);
  373. ++fInlineDisplay.writtenValues;
  374. }
  375. if (fInlineDisplay.pending == InlineDisplayNotPending)
  376. {
  377. needsIdleRequest = true;
  378. fInlineDisplay.pending = InlineDisplayNeedRequest;
  379. }
  380. #endif
  381. if (needsIdleRequest)
  382. hostRequestIdle();
  383. }
  384. // -------------------------------------------------------------------
  385. // Plugin UI calls
  386. void uiShow(const bool show) override
  387. {
  388. if (! show)
  389. return;
  390. if (const char* const filename = uiOpenFile(false, "Open Audio File", ""))
  391. uiCustomDataChanged("file", filename);
  392. uiClosed();
  393. }
  394. // -------------------------------------------------------------------
  395. // Plugin state calls
  396. void setStateFromFile(const char* const filename) override
  397. {
  398. loadFilename(filename);
  399. }
  400. // -------------------------------------------------------------------
  401. // Plugin dispatcher calls
  402. void idle() override
  403. {
  404. NativePluginWithMidiPrograms<FileAudio>::idle();
  405. if (fNeedsFileRead)
  406. {
  407. fReader.readPoll();
  408. fNeedsFileRead = false;
  409. }
  410. #ifndef __MOD_DEVICES__
  411. if (fInlineDisplay.pending == InlineDisplayNeedRequest)
  412. {
  413. fInlineDisplay.pending = InlineDisplayRequesting;
  414. hostQueueDrawInlineDisplay();
  415. }
  416. #endif
  417. }
  418. #ifndef __MOD_DEVICES__
  419. const NativeInlineDisplayImageSurface* renderInlineDisplay(const uint32_t rwidth, const uint32_t height) override
  420. {
  421. CARLA_SAFE_ASSERT_RETURN(height > 4, nullptr);
  422. const uint32_t width = rwidth == height ? height * 4 : rwidth;
  423. /* NOTE the code is this function is not optimized, still learning my way through pixels...
  424. */
  425. const size_t stride = width * 4;
  426. const size_t dataSize = stride * height;
  427. const uint pxToMove = fDoProcess ? fInlineDisplay.writtenValues : 0;
  428. uchar* data = fInlineDisplay.data;
  429. if (fInlineDisplay.dataSize != dataSize || data == nullptr)
  430. {
  431. delete[] data;
  432. data = new uchar[dataSize];
  433. std::memset(data, 0, dataSize);
  434. fInlineDisplay.data = data;
  435. fInlineDisplay.dataSize = dataSize;
  436. }
  437. else if (pxToMove != 0)
  438. {
  439. // shift all previous values to the left
  440. for (uint w=0; w < width - pxToMove; ++w)
  441. for (uint h=0; h < height; ++h)
  442. std::memmove(&data[h * stride + w * 4], &data[h * stride + (w+pxToMove) * 4], 4);
  443. }
  444. fInlineDisplay.width = static_cast<int>(width);
  445. fInlineDisplay.height = static_cast<int>(height);
  446. fInlineDisplay.stride = static_cast<int>(stride);
  447. if (pxToMove != 0)
  448. {
  449. const uint h2 = height / 2;
  450. // clear current line
  451. for (uint w=width-pxToMove; w < width; ++w)
  452. for (uint h=0; h < height; ++h)
  453. memset(&data[h * stride + w * 4], 0, 4);
  454. // draw upper/left
  455. for (uint i=0; i < pxToMove && i < 32; ++i)
  456. {
  457. const float valueL = fInlineDisplay.lastValuesL[i];
  458. const float valueR = fInlineDisplay.lastValuesR[i];
  459. const uint h2L = static_cast<uint>(valueL * (float)h2);
  460. const uint h2R = static_cast<uint>(valueR * (float)h2);
  461. const uint w = width - pxToMove + i;
  462. for (uint h=0; h < h2L; ++h)
  463. {
  464. // -30dB
  465. //if (valueL < 0.032f)
  466. // continue;
  467. data[(h2 - h) * stride + w * 4 + 3] = 160;
  468. // -12dB
  469. if (valueL < 0.25f)
  470. {
  471. data[(h2 - h) * stride + w * 4 + 1] = 255;
  472. }
  473. // -3dB
  474. else if (valueL < 0.70f)
  475. {
  476. data[(h2 - h) * stride + w * 4 + 2] = 255;
  477. data[(h2 - h) * stride + w * 4 + 1] = 255;
  478. }
  479. else
  480. {
  481. data[(h2 - h) * stride + w * 4 + 2] = 255;
  482. }
  483. }
  484. for (uint h=0; h < h2R; ++h)
  485. {
  486. // -30dB
  487. //if (valueR < 0.032f)
  488. // continue;
  489. data[(h2 + h) * stride + w * 4 + 3] = 160;
  490. // -12dB
  491. if (valueR < 0.25f)
  492. {
  493. data[(h2 + h) * stride + w * 4 + 1] = 255;
  494. }
  495. // -3dB
  496. else if (valueR < 0.70f)
  497. {
  498. data[(h2 + h) * stride + w * 4 + 2] = 255;
  499. data[(h2 + h) * stride + w * 4 + 1] = 255;
  500. }
  501. else
  502. {
  503. data[(h2 + h) * stride + w * 4 + 2] = 255;
  504. }
  505. }
  506. }
  507. }
  508. fInlineDisplay.writtenValues = 0;
  509. fInlineDisplay.pending = InlineDisplayNotPending;
  510. return (NativeInlineDisplayImageSurface*)(NativeInlineDisplayImageSurfaceCompat*)&fInlineDisplay;
  511. }
  512. #endif
  513. // -------------------------------------------------------------------
  514. private:
  515. bool fLoopMode;
  516. bool fDoProcess;
  517. bool fWasPlayingBefore;
  518. volatile bool fNeedsFileRead;
  519. bool fEntireFileLoaded;
  520. uint32_t fMaxFrame;
  521. float fLastPosition;
  522. float fLastPoolFill;
  523. AudioFilePool fPool;
  524. AudioFileReader fReader;
  525. NativeMidiPrograms fPrograms;
  526. float fPreviewData[300];
  527. #ifndef __MOD_DEVICES__
  528. struct InlineDisplay : NativeInlineDisplayImageSurfaceCompat {
  529. float lastValuesL[32];
  530. float lastValuesR[32];
  531. volatile PendingInlineDisplay pending;
  532. volatile uint8_t writtenValues;
  533. InlineDisplay()
  534. : NativeInlineDisplayImageSurfaceCompat(),
  535. # ifdef CARLA_PROPER_CPP11_SUPPORT
  536. lastValuesL{0.0f},
  537. lastValuesR{0.0f},
  538. # endif
  539. pending(InlineDisplayNotPending),
  540. writtenValues(0)
  541. {
  542. # ifndef CARLA_PROPER_CPP11_SUPPORT
  543. carla_zeroFloats(lastValuesL, 32);
  544. carla_zeroFloats(lastValuesR, 32);
  545. # endif
  546. }
  547. ~InlineDisplay()
  548. {
  549. if (data != nullptr)
  550. {
  551. delete[] data;
  552. data = nullptr;
  553. }
  554. }
  555. CARLA_DECLARE_NON_COPY_STRUCT(InlineDisplay)
  556. CARLA_PREVENT_HEAP_ALLOCATION
  557. } fInlineDisplay;
  558. #endif
  559. void loadFilename(const char* const filename)
  560. {
  561. CARLA_ASSERT(filename != nullptr);
  562. carla_debug("AudioFilePlugin::loadFilename(\"%s\")", filename);
  563. fDoProcess = false;
  564. fLastPoolFill = 0.0f;
  565. fPool.destroy();
  566. fReader.destroy();
  567. if (filename == nullptr || *filename == '\0')
  568. {
  569. fMaxFrame = 0;
  570. return;
  571. }
  572. const uint32_t previewDataSize = sizeof(fPreviewData)/sizeof(float);
  573. if (fReader.loadFilename(filename, static_cast<uint32_t>(getSampleRate()), previewDataSize, fPreviewData))
  574. {
  575. fEntireFileLoaded = fReader.isEntireFileLoaded();
  576. fMaxFrame = fReader.getMaxFrame();
  577. if (fEntireFileLoaded)
  578. {
  579. fReader.putAndSwapAllData(fPool);
  580. fLastPoolFill = 100.0f;
  581. }
  582. else
  583. {
  584. fReader.createSwapablePool(fPool);
  585. fReader.readPoll();
  586. }
  587. fDoProcess = true;
  588. hostSendPreviewBufferData('f', previewDataSize, fPreviewData);
  589. }
  590. else
  591. {
  592. fEntireFileLoaded = false;
  593. fMaxFrame = 0;
  594. }
  595. }
  596. PluginClassEND(AudioFilePlugin)
  597. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(AudioFilePlugin)
  598. };
  599. // -----------------------------------------------------------------------
  600. static const NativePluginDescriptor audiofileDesc = {
  601. /* category */ NATIVE_PLUGIN_CATEGORY_UTILITY,
  602. /* hints */ static_cast<NativePluginHints>(NATIVE_PLUGIN_IS_RTSAFE
  603. |NATIVE_PLUGIN_HAS_UI
  604. #ifndef __MOD_DEVICES__
  605. |NATIVE_PLUGIN_HAS_INLINE_DISPLAY
  606. #endif
  607. |NATIVE_PLUGIN_REQUESTS_IDLE
  608. |NATIVE_PLUGIN_NEEDS_UI_OPEN_SAVE
  609. |NATIVE_PLUGIN_USES_TIME),
  610. /* supports */ NATIVE_PLUGIN_SUPPORTS_NOTHING,
  611. /* audioIns */ 0,
  612. /* audioOuts */ 2,
  613. /* midiIns */ 0,
  614. /* midiOuts */ 0,
  615. /* paramIns */ 1,
  616. /* paramOuts */ 0,
  617. /* name */ "Audio File",
  618. /* label */ "audiofile",
  619. /* maker */ "falkTX",
  620. /* copyright */ "GNU GPL v2+",
  621. PluginDescriptorFILL(AudioFilePlugin)
  622. };
  623. // -----------------------------------------------------------------------
  624. CARLA_EXPORT
  625. void carla_register_native_plugin_audiofile();
  626. CARLA_EXPORT
  627. void carla_register_native_plugin_audiofile()
  628. {
  629. carla_register_native_plugin(&audiofileDesc);
  630. }
  631. // -----------------------------------------------------------------------