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.

447 lines
13KB

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