VSTPlugin.cpp 11 KB

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