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.

644 lines
21KB

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