VSTPlugin.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464
  1. /*****************************************************************************
  2. Copyright (C) 2016-2017 by Colin Edwards.
  3. Additional Code Copyright (C) 2016-2017 by c3r1c3 <[email protected]>
  4. This program is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation, either version 2 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. *****************************************************************************/
  15. #include "headers/VSTPlugin.h"
  16. #include <util/platform.h>
  17. intptr_t VSTPlugin::hostCallback_static(AEffect *effect, int32_t opcode,
  18. int32_t index, intptr_t value,
  19. void *ptr, float opt)
  20. {
  21. UNUSED_PARAMETER(opt);
  22. UNUSED_PARAMETER(ptr);
  23. VSTPlugin *plugin = nullptr;
  24. if (effect && effect->user) {
  25. plugin = static_cast<VSTPlugin *>(effect->user);
  26. }
  27. switch (opcode) {
  28. case audioMasterVersion:
  29. return (intptr_t)2400;
  30. case audioMasterGetCurrentProcessLevel:
  31. return 1;
  32. // We always replace, never accumulate
  33. case audioMasterWillReplaceOrAccumulate:
  34. return 1;
  35. case audioMasterGetSampleRate:
  36. if (plugin) {
  37. return (intptr_t)plugin->GetSampleRate();
  38. }
  39. return 0;
  40. case audioMasterGetTime:
  41. if (plugin) {
  42. return (intptr_t)plugin->GetTimeInfo();
  43. }
  44. return 0;
  45. // index: width, value: height
  46. case audioMasterSizeWindow:
  47. if (plugin && plugin->editorWidget) {
  48. plugin->editorWidget->handleResizeRequest(index, value);
  49. }
  50. return 1;
  51. default:
  52. return 0;
  53. }
  54. }
  55. VstTimeInfo *VSTPlugin::GetTimeInfo()
  56. {
  57. mTimeInfo.nanoSeconds = os_gettime_ns() / 1000000;
  58. return &mTimeInfo;
  59. }
  60. float VSTPlugin::GetSampleRate()
  61. {
  62. return mTimeInfo.sampleRate;
  63. }
  64. VSTPlugin::VSTPlugin(obs_source_t *sourceContext) : sourceContext{sourceContext}
  65. {
  66. }
  67. VSTPlugin::~VSTPlugin()
  68. {
  69. unloadEffect();
  70. cleanupChannelBuffers();
  71. }
  72. void VSTPlugin::createChannelBuffers(size_t count)
  73. {
  74. cleanupChannelBuffers();
  75. int blocksize = BLOCK_SIZE;
  76. numChannels = (std::max)((size_t)0, count);
  77. if (numChannels > 0) {
  78. inputs = (float **)bmalloc(sizeof(float *) * numChannels);
  79. outputs = (float **)bmalloc(sizeof(float *) * numChannels);
  80. channelrefs = (float **)bmalloc(sizeof(float *) * numChannels);
  81. for (size_t channel = 0; channel < numChannels; channel++) {
  82. inputs[channel] =
  83. (float *)bmalloc(sizeof(float) * blocksize);
  84. outputs[channel] =
  85. (float *)bmalloc(sizeof(float) * blocksize);
  86. }
  87. }
  88. }
  89. void VSTPlugin::cleanupChannelBuffers()
  90. {
  91. for (size_t channel = 0; channel < numChannels; channel++) {
  92. if (inputs && inputs[channel]) {
  93. bfree(inputs[channel]);
  94. inputs[channel] = NULL;
  95. }
  96. if (outputs && outputs[channel]) {
  97. bfree(outputs[channel]);
  98. outputs[channel] = NULL;
  99. }
  100. }
  101. if (inputs) {
  102. bfree(inputs);
  103. inputs = NULL;
  104. }
  105. if (outputs) {
  106. bfree(outputs);
  107. outputs = NULL;
  108. }
  109. if (channelrefs) {
  110. bfree(channelrefs);
  111. channelrefs = NULL;
  112. }
  113. numChannels = 0;
  114. }
  115. void VSTPlugin::loadEffectFromPath(const std::string &path)
  116. {
  117. if (this->pluginPath.compare(path) != 0) {
  118. unloadEffect();
  119. blog(LOG_INFO, "User selected new VST plugin: '%s'",
  120. path.c_str());
  121. }
  122. if (!effect) {
  123. // TODO: alert user of error if VST is not available.
  124. pluginPath = path;
  125. AEffect *effectTemp = loadEffect();
  126. if (!effectTemp) {
  127. blog(LOG_WARNING, "VST Plug-in: Can't load effect!");
  128. return;
  129. }
  130. {
  131. std::lock_guard<std::recursive_mutex> lock(lockEffect);
  132. effect = effectTemp;
  133. }
  134. // Check plug-in's magic number
  135. // If incorrect, then the file either was not loaded properly,
  136. // is not a real VST plug-in, or is otherwise corrupt.
  137. if (effect->magic != kEffectMagic) {
  138. blog(LOG_WARNING, "VST Plug-in's magic number is bad");
  139. return;
  140. }
  141. int maxchans =
  142. (std::max)(effect->numInputs, effect->numOutputs);
  143. // sanity check
  144. if (maxchans < 0 || maxchans > 256) {
  145. blog(LOG_WARNING,
  146. "VST Plug-in has invalid number of channels");
  147. return;
  148. }
  149. createChannelBuffers(maxchans);
  150. // It is better to invoke this code after checking magic number
  151. effect->dispatcher(effect, effGetEffectName, 0, 0, effectName,
  152. 0);
  153. effect->dispatcher(effect, effGetVendorString, 0, 0,
  154. vendorString, 0);
  155. // This check logic is refer to open source project : Audacity
  156. if ((effect->flags & effFlagsIsSynth) ||
  157. !(effect->flags & effFlagsCanReplacing)) {
  158. blog(LOG_WARNING,
  159. "VST Plug-in can't support replacing. '%s'",
  160. path.c_str());
  161. return;
  162. }
  163. // Ask the plugin to identify itself...might be needed for older plugins
  164. effect->dispatcher(effect, effIdentify, 0, 0, nullptr, 0.0f);
  165. effect->dispatcher(effect, effOpen, 0, 0, nullptr, 0.0f);
  166. // Set some default properties
  167. size_t sampleRate =
  168. audio_output_get_sample_rate(obs_get_audio());
  169. // Initialize time info
  170. memset(&mTimeInfo, 0, sizeof(mTimeInfo));
  171. mTimeInfo.sampleRate = sampleRate;
  172. mTimeInfo.nanoSeconds = os_gettime_ns() / 1000000;
  173. mTimeInfo.tempo = 120.0;
  174. mTimeInfo.timeSigNumerator = 4;
  175. mTimeInfo.timeSigDenominator = 4;
  176. mTimeInfo.flags = kVstTempoValid | kVstNanosValid |
  177. kVstTransportPlaying;
  178. effect->dispatcher(effect, effSetSampleRate, 0, 0, nullptr,
  179. sampleRate);
  180. int blocksize = BLOCK_SIZE;
  181. effect->dispatcher(effect, effSetBlockSize, 0, blocksize,
  182. nullptr, 0.0f);
  183. effect->dispatcher(effect, effMainsChanged, 0, 1, nullptr, 0);
  184. effectReady = true;
  185. if (openInterfaceWhenActive) {
  186. openEditor();
  187. }
  188. }
  189. }
  190. static void silenceChannel(float **channelData, size_t numChannels,
  191. long numFrames)
  192. {
  193. for (size_t channel = 0; channel < numChannels; ++channel) {
  194. for (long frame = 0; frame < numFrames; ++frame) {
  195. channelData[channel][frame] = 0.0f;
  196. }
  197. }
  198. }
  199. obs_audio_data *VSTPlugin::process(struct obs_audio_data *audio)
  200. {
  201. // Here we check the status firstly,
  202. // which help avoid waiting for lock while unloadEffect() is running.
  203. bool effectValid = (effect && effectReady && numChannels > 0);
  204. if (!effectValid)
  205. return audio;
  206. std::lock_guard<std::recursive_mutex> lock(lockEffect);
  207. if (effect && effectReady && numChannels > 0) {
  208. uint passes = (audio->frames + BLOCK_SIZE - 1) / BLOCK_SIZE;
  209. uint extra = audio->frames % BLOCK_SIZE;
  210. for (uint pass = 0; pass < passes; pass++) {
  211. uint frames = pass == passes - 1 && extra ? extra
  212. : BLOCK_SIZE;
  213. silenceChannel(outputs, numChannels, BLOCK_SIZE);
  214. for (size_t d = 0; d < numChannels; d++) {
  215. if (d < MAX_AV_PLANES &&
  216. audio->data[d] != nullptr) {
  217. channelrefs[d] =
  218. ((float *)audio->data[d]) +
  219. (pass * BLOCK_SIZE);
  220. } else {
  221. channelrefs[d] = inputs[d];
  222. }
  223. };
  224. effect->processReplacing(effect, channelrefs, outputs,
  225. frames);
  226. // only copy back the channels the plugin may have generated
  227. for (size_t c = 0; c < (size_t)effect->numOutputs &&
  228. c < MAX_AV_PLANES;
  229. c++) {
  230. if (audio->data[c]) {
  231. for (size_t i = 0; i < frames; i++) {
  232. channelrefs[c][i] =
  233. outputs[c][i];
  234. }
  235. }
  236. }
  237. }
  238. }
  239. return audio;
  240. }
  241. void VSTPlugin::unloadEffect()
  242. {
  243. closeEditor();
  244. {
  245. std::lock_guard<std::recursive_mutex> lock(lockEffect);
  246. // Reset the status firstly to avoid VSTPlugin::process is blocked
  247. effectReady = false;
  248. if (effect) {
  249. effect->dispatcher(effect, effMainsChanged, 0, 0,
  250. nullptr, 0);
  251. effect->dispatcher(effect, effClose, 0, 0, nullptr,
  252. 0.0f);
  253. }
  254. effect = nullptr;
  255. }
  256. unloadLibrary();
  257. pluginPath.clear();
  258. }
  259. bool VSTPlugin::isEditorOpen()
  260. {
  261. return editorWidget ? true : false;
  262. }
  263. void VSTPlugin::onEditorClosed()
  264. {
  265. if (!editorWidget)
  266. return;
  267. editorWidget->deleteLater();
  268. editorWidget = nullptr;
  269. if (effect && editorOpened) {
  270. editorOpened = false;
  271. effect->dispatcher(effect, effEditClose, 0, 0, nullptr, 0);
  272. }
  273. }
  274. void VSTPlugin::openEditor()
  275. {
  276. if (effect && !editorWidget) {
  277. // This check logic is refer to open source project : Audacity
  278. if (!(effect->flags & effFlagsHasEditor)) {
  279. blog(LOG_WARNING,
  280. "VST Plug-in: Can't support edit feature. '%s'",
  281. pluginPath.c_str());
  282. return;
  283. }
  284. editorOpened = true;
  285. editorWidget = new EditorWidget(nullptr, this);
  286. editorWidget->buildEffectContainer(effect);
  287. if (sourceName.empty()) {
  288. sourceName = "VST 2.x";
  289. }
  290. if (filterName.empty()) {
  291. editorWidget->setWindowTitle(QString("%1 - %2").arg(
  292. sourceName.c_str(), effectName));
  293. } else {
  294. editorWidget->setWindowTitle(
  295. QString("%1: %2 - %3")
  296. .arg(sourceName.c_str(),
  297. filterName.c_str(), effectName));
  298. }
  299. editorWidget->show();
  300. }
  301. }
  302. void VSTPlugin::closeEditor()
  303. {
  304. if (editorWidget)
  305. editorWidget->close();
  306. }
  307. std::string VSTPlugin::getEffectPath()
  308. {
  309. return pluginPath;
  310. }
  311. std::string VSTPlugin::getChunk()
  312. {
  313. if (!effect) {
  314. return "";
  315. }
  316. if (effect->flags & effFlagsProgramChunks) {
  317. void *buf = nullptr;
  318. intptr_t chunkSize = effect->dispatcher(effect, effGetChunk, 1,
  319. 0, &buf, 0.0);
  320. QByteArray data = QByteArray((char *)buf, chunkSize);
  321. return QString(data.toBase64()).toStdString();
  322. } else {
  323. std::vector<float> params;
  324. for (int i = 0; i < effect->numParams; i++) {
  325. float parameter = effect->getParameter(effect, i);
  326. params.push_back(parameter);
  327. }
  328. const char *bytes = reinterpret_cast<const char *>(&params[0]);
  329. QByteArray data =
  330. QByteArray(bytes, (int)(sizeof(float) * params.size()));
  331. std::string encoded = QString(data.toBase64()).toStdString();
  332. return encoded;
  333. }
  334. }
  335. void VSTPlugin::setChunk(const std::string &data)
  336. {
  337. if (!effect) {
  338. return;
  339. }
  340. if (effect->flags & effFlagsProgramChunks) {
  341. QByteArray base64Data =
  342. QByteArray(data.c_str(), (int)data.length());
  343. QByteArray chunkData = QByteArray::fromBase64(base64Data);
  344. void *buf = nullptr;
  345. buf = chunkData.data();
  346. effect->dispatcher(effect, effSetChunk, 1, chunkData.length(),
  347. buf, 0);
  348. } else {
  349. QByteArray base64Data =
  350. QByteArray(data.c_str(), (int)data.length());
  351. QByteArray paramData = QByteArray::fromBase64(base64Data);
  352. const char *p_chars = paramData.data();
  353. const float *p_floats =
  354. reinterpret_cast<const float *>(p_chars);
  355. const size_t size = paramData.length() / sizeof(float);
  356. std::vector<float> params(p_floats, p_floats + size);
  357. if (params.size() != (size_t)effect->numParams) {
  358. return;
  359. }
  360. for (int i = 0; i < effect->numParams; i++) {
  361. effect->setParameter(effect, i, params[i]);
  362. }
  363. }
  364. }
  365. void VSTPlugin::setProgram(const int programNumber)
  366. {
  367. if (programNumber < effect->numPrograms) {
  368. effect->dispatcher(effect, effSetProgram, 0, programNumber,
  369. NULL, 0.0f);
  370. } else {
  371. blog(LOG_ERROR,
  372. "Failed to load program, number was outside possible program range.");
  373. }
  374. }
  375. int VSTPlugin::getProgram()
  376. {
  377. return effect->dispatcher(effect, effGetProgram, 0, 0, NULL, 0.0f);
  378. }
  379. void VSTPlugin::getSourceNames()
  380. {
  381. /* Only call inside the vst_filter_audio function! */
  382. sourceName = obs_source_get_name(obs_filter_get_parent(sourceContext));
  383. filterName = obs_source_get_name(sourceContext);
  384. }