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.

509 lines
15KB

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