VSTPlugin.cpp 11 KB

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