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.

520 lines
16KB

  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. AudioFilePlugin(const NativeHostDescriptor* const host)
  43. : NativePluginWithMidiPrograms<FileAudio>(host, fPrograms, 2),
  44. fLoopMode(true),
  45. fDoProcess(false),
  46. fWasPlayingBefore(false),
  47. fMaxFrame(0),
  48. fPool(),
  49. fThread(),
  50. fPrograms(hostGetFilePath("audio"), audiofilesWildcard)
  51. #ifndef __MOD_DEVICES__
  52. , fInlineDisplay()
  53. #endif
  54. {
  55. }
  56. ~AudioFilePlugin() override
  57. {
  58. fThread.stopNow();
  59. fPool.destroy();
  60. }
  61. protected:
  62. // -------------------------------------------------------------------
  63. // Plugin parameter calls
  64. uint32_t getParameterCount() const override
  65. {
  66. return 1;
  67. }
  68. const NativeParameter* getParameterInfo(const uint32_t index) const override
  69. {
  70. if (index != 0)
  71. return nullptr;
  72. static NativeParameter param;
  73. param.name = "Loop Mode";
  74. param.unit = nullptr;
  75. param.hints = static_cast<NativeParameterHints>(NATIVE_PARAMETER_IS_AUTOMABLE|NATIVE_PARAMETER_IS_ENABLED|NATIVE_PARAMETER_IS_BOOLEAN);
  76. param.ranges.def = 1.0f;
  77. param.ranges.min = 0.0f;
  78. param.ranges.max = 1.0f;
  79. param.ranges.step = 1.0f;
  80. param.ranges.stepSmall = 1.0f;
  81. param.ranges.stepLarge = 1.0f;
  82. param.scalePointCount = 0;
  83. param.scalePoints = nullptr;
  84. return &param;
  85. }
  86. float getParameterValue(const uint32_t index) const override
  87. {
  88. if (index != 0)
  89. return 0.0f;
  90. return fLoopMode ? 1.0f : 0.0f;
  91. }
  92. // -------------------------------------------------------------------
  93. // Plugin state calls
  94. void setParameterValue(const uint32_t index, const float value) override
  95. {
  96. if (index != 0)
  97. return;
  98. bool b = (value > 0.5f);
  99. if (b == fLoopMode)
  100. return;
  101. fLoopMode = b;
  102. fThread.setLoopingMode(b);
  103. }
  104. void setCustomData(const char* const key, const char* const value) override
  105. {
  106. if (std::strcmp(key, "file") != 0)
  107. return;
  108. invalidateNextFilename();
  109. loadFilename(value);
  110. }
  111. // -------------------------------------------------------------------
  112. // Plugin process calls
  113. void process2(const float* const*, float** const outBuffer, const uint32_t frames,
  114. const NativeMidiEvent*, uint32_t) override
  115. {
  116. const NativeTimeInfo* const timePos(getTimeInfo());
  117. float* out1 = outBuffer[0];
  118. float* out2 = outBuffer[1];
  119. if (! fDoProcess)
  120. {
  121. // carla_stderr("P: no process");
  122. carla_zeroFloats(out1, frames);
  123. carla_zeroFloats(out2, frames);
  124. return;
  125. }
  126. // not playing
  127. if (! timePos->playing)
  128. {
  129. // carla_stderr("P: not playing");
  130. if (timePos->frame == 0 && fWasPlayingBefore)
  131. fThread.setNeedsRead(timePos->frame);
  132. carla_zeroFloats(out1, frames);
  133. carla_zeroFloats(out2, frames);
  134. fWasPlayingBefore = false;
  135. return;
  136. }
  137. else
  138. {
  139. fWasPlayingBefore = true;
  140. }
  141. // out of reach
  142. if ((timePos->frame < fPool.startFrame || timePos->frame >= fMaxFrame) && !fLoopMode)
  143. {
  144. if (timePos->frame < fPool.startFrame)
  145. fThread.setNeedsRead(timePos->frame);
  146. carla_zeroFloats(out1, frames);
  147. carla_zeroFloats(out2, frames);
  148. #ifndef __MOD_DEVICES__
  149. if (fInlineDisplay.writtenValues < 32)
  150. {
  151. fInlineDisplay.lastValuesL[fInlineDisplay.writtenValues] = 0.0f;
  152. fInlineDisplay.lastValuesR[fInlineDisplay.writtenValues] = 0.0f;
  153. ++fInlineDisplay.writtenValues;
  154. }
  155. if (! fInlineDisplay.pending)
  156. {
  157. fInlineDisplay.pending = true;
  158. hostQueueDrawInlineDisplay();
  159. }
  160. #endif
  161. return;
  162. }
  163. if (fThread.isEntireFileLoaded())
  164. {
  165. // NOTE: timePos->frame is always < fMaxFrame (or looping)
  166. uint32_t targetStartFrame = static_cast<uint32_t>(fLoopMode ? timePos->frame % fMaxFrame : timePos->frame);
  167. for (uint32_t framesDone=0, framesToDo=frames, remainingFrames; framesDone < frames;)
  168. {
  169. if (targetStartFrame + framesToDo <= fMaxFrame)
  170. {
  171. // everything fits together
  172. carla_copyFloats(out1+framesDone, fPool.buffer[0]+targetStartFrame, framesToDo);
  173. carla_copyFloats(out2+framesDone, fPool.buffer[1]+targetStartFrame, framesToDo);
  174. break;
  175. }
  176. remainingFrames = std::min(fMaxFrame - targetStartFrame, framesToDo);
  177. carla_copyFloats(out1+framesDone, fPool.buffer[0]+targetStartFrame, remainingFrames);
  178. carla_copyFloats(out2+framesDone, fPool.buffer[1]+targetStartFrame, remainingFrames);
  179. framesDone += remainingFrames;
  180. framesToDo -= remainingFrames;
  181. if (! fLoopMode)
  182. {
  183. // not looping, stop here
  184. if (framesToDo != 0)
  185. {
  186. carla_zeroFloats(out1+framesDone, framesToDo);
  187. carla_zeroFloats(out2+framesDone, framesToDo);
  188. }
  189. break;
  190. }
  191. // reset for next loop
  192. targetStartFrame = 0;
  193. }
  194. }
  195. else
  196. {
  197. if (! fThread.tryPutData(out1, out2, timePos->frame, frames))
  198. {
  199. carla_zeroFloats(out1, frames);
  200. carla_zeroFloats(out2, frames);
  201. }
  202. }
  203. #ifndef __MOD_DEVICES__
  204. if (fInlineDisplay.writtenValues < 32)
  205. {
  206. fInlineDisplay.lastValuesL[fInlineDisplay.writtenValues] = carla_findMaxNormalizedFloat(out1, frames);
  207. fInlineDisplay.lastValuesR[fInlineDisplay.writtenValues] = carla_findMaxNormalizedFloat(out2, frames);
  208. ++fInlineDisplay.writtenValues;
  209. }
  210. if (! fInlineDisplay.pending)
  211. {
  212. fInlineDisplay.pending = true;
  213. // FIXME this is not supposed to be here, but in some idle callback
  214. hostQueueDrawInlineDisplay();
  215. }
  216. #endif
  217. }
  218. // -------------------------------------------------------------------
  219. // Plugin UI calls
  220. void uiShow(const bool show) override
  221. {
  222. if (! show)
  223. return;
  224. if (const char* const filename = uiOpenFile(false, "Open Audio File", ""))
  225. uiCustomDataChanged("file", filename);
  226. uiClosed();
  227. }
  228. // -------------------------------------------------------------------
  229. // Plugin state calls
  230. void setStateFromFile(const char* const filename) override
  231. {
  232. loadFilename(filename);
  233. }
  234. // -------------------------------------------------------------------
  235. // Plugin dispatcher calls
  236. #ifndef __MOD_DEVICES__
  237. const NativeInlineDisplayImageSurface* renderInlineDisplay(const uint32_t rwidth, const uint32_t height) override
  238. {
  239. CARLA_SAFE_ASSERT_RETURN(height > 4, nullptr);
  240. const uint32_t width = rwidth == height ? height * 4 : rwidth;
  241. /* NOTE the code is this function is not optimized, still learning my way through pixels...
  242. */
  243. const size_t stride = width * 4;
  244. const size_t dataSize = stride * height;
  245. const uint pxToMove = fDoProcess ? fInlineDisplay.writtenValues : 0;
  246. uchar* data = fInlineDisplay.data;
  247. if (fInlineDisplay.dataSize != dataSize || data == nullptr)
  248. {
  249. delete[] data;
  250. data = new uchar[dataSize];
  251. std::memset(data, 0, dataSize);
  252. fInlineDisplay.data = data;
  253. fInlineDisplay.dataSize = dataSize;
  254. }
  255. else if (pxToMove != 0)
  256. {
  257. // shift all previous values to the left
  258. for (uint w=0; w < width - pxToMove; ++w)
  259. for (uint h=0; h < height; ++h)
  260. std::memmove(&data[h * stride + w * 4], &data[h * stride + (w+pxToMove) * 4], 4);
  261. }
  262. fInlineDisplay.width = static_cast<int>(width);
  263. fInlineDisplay.height = static_cast<int>(height);
  264. fInlineDisplay.stride = static_cast<int>(stride);
  265. if (pxToMove != 0)
  266. {
  267. const uint h2 = height / 2;
  268. // clear current line
  269. for (uint w=width-pxToMove; w < width; ++w)
  270. for (uint h=0; h < height; ++h)
  271. memset(&data[h * stride + w * 4], 0, 4);
  272. // draw upper/left
  273. for (uint i=0; i < pxToMove && i < 32; ++i)
  274. {
  275. const float valueL = fInlineDisplay.lastValuesL[i];
  276. const float valueR = fInlineDisplay.lastValuesR[i];
  277. const uint h2L = static_cast<uint>(valueL * (float)h2);
  278. const uint h2R = static_cast<uint>(valueR * (float)h2);
  279. const uint w = width - pxToMove + i;
  280. for (uint h=0; h < h2L; ++h)
  281. {
  282. // -30dB
  283. //if (valueL < 0.032f)
  284. // continue;
  285. data[(h2 - h) * stride + w * 4 + 3] = 160;
  286. // -12dB
  287. if (valueL < 0.25f)
  288. {
  289. data[(h2 - h) * stride + w * 4 + 1] = 255;
  290. }
  291. // -3dB
  292. else if (valueL < 0.70f)
  293. {
  294. data[(h2 - h) * stride + w * 4 + 2] = 255;
  295. data[(h2 - h) * stride + w * 4 + 1] = 255;
  296. }
  297. else
  298. {
  299. data[(h2 - h) * stride + w * 4 + 2] = 255;
  300. }
  301. }
  302. for (uint h=0; h < h2R; ++h)
  303. {
  304. // -30dB
  305. //if (valueR < 0.032f)
  306. // continue;
  307. data[(h2 + h) * stride + w * 4 + 3] = 160;
  308. // -12dB
  309. if (valueR < 0.25f)
  310. {
  311. data[(h2 + h) * stride + w * 4 + 1] = 255;
  312. }
  313. // -3dB
  314. else if (valueR < 0.70f)
  315. {
  316. data[(h2 + h) * stride + w * 4 + 2] = 255;
  317. data[(h2 + h) * stride + w * 4 + 1] = 255;
  318. }
  319. else
  320. {
  321. data[(h2 + h) * stride + w * 4 + 2] = 255;
  322. }
  323. }
  324. }
  325. }
  326. fInlineDisplay.writtenValues = 0;
  327. fInlineDisplay.pending = false;
  328. return (NativeInlineDisplayImageSurface*)(NativeInlineDisplayImageSurfaceCompat*)&fInlineDisplay;
  329. }
  330. #endif
  331. // -------------------------------------------------------------------
  332. private:
  333. bool fLoopMode;
  334. bool fDoProcess;
  335. bool fWasPlayingBefore;
  336. uint32_t fMaxFrame;
  337. AudioFilePool fPool;
  338. AudioFileThread fThread;
  339. NativeMidiPrograms fPrograms;
  340. #ifndef __MOD_DEVICES__
  341. struct InlineDisplay : NativeInlineDisplayImageSurfaceCompat {
  342. float lastValuesL[32];
  343. float lastValuesR[32];
  344. volatile uint8_t writtenValues;
  345. volatile bool pending;
  346. InlineDisplay()
  347. : NativeInlineDisplayImageSurfaceCompat(),
  348. # ifdef CARLA_PROPER_CPP11_SUPPORT
  349. lastValuesL{0.0f},
  350. lastValuesR{0.0f},
  351. # endif
  352. writtenValues(0),
  353. pending(false)
  354. {
  355. # ifndef CARLA_PROPER_CPP11_SUPPORT
  356. carla_zeroFloats(lastValuesL, 32);
  357. carla_zeroFloats(lastValuesR, 32);
  358. # endif
  359. }
  360. ~InlineDisplay()
  361. {
  362. if (data != nullptr)
  363. {
  364. delete[] data;
  365. data = nullptr;
  366. }
  367. }
  368. CARLA_DECLARE_NON_COPY_STRUCT(InlineDisplay)
  369. CARLA_PREVENT_HEAP_ALLOCATION
  370. } fInlineDisplay;
  371. #endif
  372. void loadFilename(const char* const filename)
  373. {
  374. CARLA_ASSERT(filename != nullptr);
  375. carla_debug("AudioFilePlugin::loadFilename(\"%s\")", filename);
  376. fThread.stopNow();
  377. fPool.destroy();
  378. if (filename == nullptr || *filename == '\0')
  379. {
  380. fDoProcess = false;
  381. fMaxFrame = 0;
  382. return;
  383. }
  384. if (fThread.loadFilename(filename, static_cast<uint32_t>(getSampleRate())))
  385. {
  386. fPool.create(fThread.getPoolNumFrames());
  387. fMaxFrame = fThread.getMaxFrame();
  388. if (fThread.isEntireFileLoaded())
  389. fThread.putAllData(fPool);
  390. else
  391. fThread.startNow();
  392. fDoProcess = true;
  393. }
  394. else
  395. {
  396. fDoProcess = false;
  397. fMaxFrame = 0;
  398. }
  399. }
  400. PluginClassEND(AudioFilePlugin)
  401. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(AudioFilePlugin)
  402. };
  403. // -----------------------------------------------------------------------
  404. static const NativePluginDescriptor audiofileDesc = {
  405. /* category */ NATIVE_PLUGIN_CATEGORY_UTILITY,
  406. /* hints */ static_cast<NativePluginHints>(NATIVE_PLUGIN_IS_RTSAFE
  407. |NATIVE_PLUGIN_HAS_UI
  408. #ifndef __MOD_DEVICES__
  409. |NATIVE_PLUGIN_HAS_INLINE_DISPLAY
  410. #endif
  411. |NATIVE_PLUGIN_REQUESTS_IDLE
  412. |NATIVE_PLUGIN_NEEDS_UI_OPEN_SAVE
  413. |NATIVE_PLUGIN_USES_TIME),
  414. /* supports */ NATIVE_PLUGIN_SUPPORTS_NOTHING,
  415. /* audioIns */ 0,
  416. /* audioOuts */ 2,
  417. /* midiIns */ 0,
  418. /* midiOuts */ 0,
  419. /* paramIns */ 1,
  420. /* paramOuts */ 0,
  421. /* name */ "Audio File",
  422. /* label */ "audiofile",
  423. /* maker */ "falkTX",
  424. /* copyright */ "GNU GPL v2+",
  425. PluginDescriptorFILL(AudioFilePlugin)
  426. };
  427. // -----------------------------------------------------------------------
  428. CARLA_EXPORT
  429. void carla_register_native_plugin_audiofile();
  430. CARLA_EXPORT
  431. void carla_register_native_plugin_audiofile()
  432. {
  433. carla_register_native_plugin(&audiofileDesc);
  434. }
  435. // -----------------------------------------------------------------------