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.

278 lines
7.4KB

  1. /*
  2. * Carla Native Plugins
  3. * Copyright (C) 2013 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(false),
  29. fDoProcess(false),
  30. fLastFrame(0),
  31. fMaxFrame(0),
  32. fThread(this, getSampleRate())
  33. {
  34. fPool.create(getSampleRate());
  35. }
  36. ~AudioFilePlugin() override
  37. {
  38. fPool.destroy();
  39. fThread.stopNow();
  40. }
  41. uint32_t getLastFrame() const override
  42. {
  43. return fLastFrame;
  44. }
  45. protected:
  46. // -------------------------------------------------------------------
  47. // Plugin parameter calls
  48. uint32_t getParameterCount() const override
  49. {
  50. return 0; // TODO - loopMode
  51. }
  52. const NativeParameter* getParameterInfo(const uint32_t index) const override
  53. {
  54. if (index != 0)
  55. return nullptr;
  56. static NativeParameter param;
  57. param.name = "Loop Mode";
  58. param.unit = nullptr;
  59. param.hints = static_cast<NativeParameterHints>(NATIVE_PARAMETER_IS_ENABLED|NATIVE_PARAMETER_IS_BOOLEAN);
  60. param.ranges.def = 1.0f;
  61. param.ranges.min = 0.0f;
  62. param.ranges.max = 1.0f;
  63. param.ranges.step = 1.0f;
  64. param.ranges.stepSmall = 1.0f;
  65. param.ranges.stepLarge = 1.0f;
  66. param.scalePointCount = 0;
  67. param.scalePoints = nullptr;
  68. return &param;
  69. }
  70. float getParameterValue(const uint32_t index) const override
  71. {
  72. if (index != 0)
  73. return 0.0f;
  74. return fLoopMode ? 1.0f : 0.0f;
  75. }
  76. // -------------------------------------------------------------------
  77. // Plugin state calls
  78. void setParameterValue(const uint32_t index, const float value) override
  79. {
  80. if (index != 0)
  81. return;
  82. bool b = (value > 0.5f);
  83. if (b == fLoopMode)
  84. return;
  85. fLoopMode = b;
  86. fThread.setNeedsRead();
  87. }
  88. void setCustomData(const char* const key, const char* const value) override
  89. {
  90. if (std::strcmp(key, "file") != 0)
  91. return;
  92. loadFilename(value);
  93. }
  94. // -------------------------------------------------------------------
  95. // Plugin process calls
  96. void process(float**, float** const outBuffer, const uint32_t frames, const NativeMidiEvent*, uint32_t) override
  97. {
  98. const NativeTimeInfo* const timePos(getTimeInfo());
  99. float* out1 = outBuffer[0];
  100. float* out2 = outBuffer[1];
  101. if (! fDoProcess)
  102. {
  103. //carla_stderr("P: no process");
  104. fLastFrame = timePos->frame;
  105. carla_zeroFloats(out1, frames);
  106. carla_zeroFloats(out2, frames);
  107. return;
  108. }
  109. // not playing
  110. if (! timePos->playing)
  111. {
  112. //carla_stderr("P: not playing");
  113. fLastFrame = timePos->frame;
  114. if (timePos->frame == 0 && fLastFrame > 0)
  115. fThread.setNeedsRead();
  116. carla_zeroFloats(out1, frames);
  117. carla_zeroFloats(out2, frames);
  118. return;
  119. }
  120. fThread.tryPutData(fPool);
  121. // out of reach
  122. if (timePos->frame + frames < fPool.startFrame || timePos->frame >= fMaxFrame) /*&& ! loopMode)*/
  123. {
  124. //carla_stderr("P: out of reach");
  125. fLastFrame = timePos->frame;
  126. if (timePos->frame + frames < fPool.startFrame)
  127. fThread.setNeedsRead();
  128. carla_zeroFloats(out1, frames);
  129. carla_zeroFloats(out2, frames);
  130. return;
  131. }
  132. int64_t poolFrame = (int64_t)timePos->frame - fPool.startFrame;
  133. int64_t poolSize = fPool.size;
  134. for (uint32_t i=0; i < frames; ++i, ++poolFrame)
  135. {
  136. if (poolFrame >= 0 && poolFrame < poolSize)
  137. {
  138. out1[i] = fPool.buffer[0][poolFrame];
  139. out2[i] = fPool.buffer[1][poolFrame];
  140. // reset
  141. fPool.buffer[0][poolFrame] = 0.0f;
  142. fPool.buffer[1][poolFrame] = 0.0f;
  143. }
  144. else
  145. {
  146. out1[i] = 0.0f;
  147. out2[i] = 0.0f;
  148. }
  149. }
  150. fLastFrame = timePos->frame;
  151. }
  152. // -------------------------------------------------------------------
  153. // Plugin UI calls
  154. void uiShow(const bool show) override
  155. {
  156. if (! show)
  157. return;
  158. if (const char* const filename = uiOpenFile(false, "Open Audio File", ""))
  159. uiCustomDataChanged("file", filename);
  160. uiClosed();
  161. }
  162. private:
  163. bool fLoopMode;
  164. bool fDoProcess;
  165. uint32_t fLastFrame;
  166. uint32_t fMaxFrame;
  167. AudioFilePool fPool;
  168. AudioFileThread fThread;
  169. void loadFilename(const char* const filename)
  170. {
  171. CARLA_ASSERT(filename != nullptr);
  172. carla_debug("AudioFilePlugin::loadFilename(\"%s\")", filename);
  173. fThread.stopNow();
  174. if (filename == nullptr || *filename == '\0')
  175. {
  176. fDoProcess = false;
  177. fMaxFrame = 0;
  178. return;
  179. }
  180. if (fThread.loadFilename(filename))
  181. {
  182. fThread.startNow();
  183. fMaxFrame = fThread.getMaxFrame();
  184. fDoProcess = true;
  185. }
  186. else
  187. {
  188. fDoProcess = false;
  189. fMaxFrame = 0;
  190. }
  191. }
  192. PluginClassEND(AudioFilePlugin)
  193. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(AudioFilePlugin)
  194. };
  195. // -----------------------------------------------------------------------
  196. static const NativePluginDescriptor audiofileDesc = {
  197. /* category */ NATIVE_PLUGIN_CATEGORY_UTILITY,
  198. /* hints */ static_cast<NativePluginHints>(NATIVE_PLUGIN_IS_RTSAFE
  199. |NATIVE_PLUGIN_HAS_UI
  200. |NATIVE_PLUGIN_NEEDS_UI_OPEN_SAVE
  201. |NATIVE_PLUGIN_USES_TIME),
  202. /* supports */ NATIVE_PLUGIN_SUPPORTS_NOTHING,
  203. /* audioIns */ 0,
  204. /* audioOuts */ 2,
  205. /* midiIns */ 0,
  206. /* midiOuts */ 0,
  207. /* paramIns */ 0, // TODO - loopMode
  208. /* paramOuts */ 0,
  209. /* name */ "Audio File",
  210. /* label */ "audiofile",
  211. /* maker */ "falkTX",
  212. /* copyright */ "GNU GPL v2+",
  213. PluginDescriptorFILL(AudioFilePlugin)
  214. };
  215. // -----------------------------------------------------------------------
  216. CARLA_EXPORT
  217. void carla_register_native_plugin_audiofile();
  218. CARLA_EXPORT
  219. void carla_register_native_plugin_audiofile()
  220. {
  221. carla_register_native_plugin(&audiofileDesc);
  222. }
  223. // -----------------------------------------------------------------------