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.

841 lines
27KB

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