VSTPlugin.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  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 = std::max(effect->numInputs, effect->numOutputs);
  144. // sanity check
  145. if (maxchans < 0 || maxchans > 256) {
  146. blog(LOG_WARNING,
  147. "VST Plug-in has invalid number of channels");
  148. return;
  149. }
  150. createChannelBuffers(maxchans);
  151. // It is better to invoke this code after checking magic number
  152. effect->dispatcher(effect, effGetEffectName, 0, 0, effectName,
  153. 0);
  154. effect->dispatcher(effect, effGetVendorString, 0, 0,
  155. vendorString, 0);
  156. // This check logic is refer to open source project : Audacity
  157. if ((effect->flags & effFlagsIsSynth) ||
  158. !(effect->flags & effFlagsCanReplacing)) {
  159. blog(LOG_WARNING,
  160. "VST Plug-in can't support replacing. '%s'",
  161. path.c_str());
  162. return;
  163. }
  164. // Ask the plugin to identify itself...might be needed for older plugins
  165. effect->dispatcher(effect, effIdentify, 0, 0, nullptr, 0.0f);
  166. effect->dispatcher(effect, effOpen, 0, 0, nullptr, 0.0f);
  167. // Set some default properties
  168. size_t sampleRate =
  169. audio_output_get_sample_rate(obs_get_audio());
  170. // Initialize time info
  171. memset(&mTimeInfo, 0, sizeof(mTimeInfo));
  172. mTimeInfo.sampleRate = sampleRate;
  173. mTimeInfo.nanoSeconds = os_gettime_ns() / 1000000;
  174. mTimeInfo.tempo = 120.0;
  175. mTimeInfo.timeSigNumerator = 4;
  176. mTimeInfo.timeSigDenominator = 4;
  177. mTimeInfo.flags = kVstTempoValid | kVstNanosValid |
  178. kVstTransportPlaying;
  179. effect->dispatcher(effect, effSetSampleRate, 0, 0, nullptr,
  180. sampleRate);
  181. int blocksize = BLOCK_SIZE;
  182. effect->dispatcher(effect, effSetBlockSize, 0, blocksize,
  183. nullptr, 0.0f);
  184. effect->dispatcher(effect, effMainsChanged, 0, 1, nullptr, 0);
  185. effectReady = true;
  186. if (openInterfaceWhenActive) {
  187. openEditor();
  188. }
  189. }
  190. }
  191. static void silenceChannel(float **channelData, size_t numChannels,
  192. long numFrames)
  193. {
  194. for (size_t channel = 0; channel < numChannels; ++channel) {
  195. for (long frame = 0; frame < numFrames; ++frame) {
  196. channelData[channel][frame] = 0.0f;
  197. }
  198. }
  199. }
  200. obs_audio_data *VSTPlugin::process(struct obs_audio_data *audio)
  201. {
  202. // Here we check the status firstly,
  203. // which help avoid waiting for lock while unloadEffect() is running.
  204. bool effectValid = (effect && effectReady && numChannels > 0);
  205. if (!effectValid)
  206. return audio;
  207. std::lock_guard<std::recursive_mutex> lock(lockEffect);
  208. if (effect && effectReady && numChannels > 0) {
  209. uint passes = (audio->frames + BLOCK_SIZE - 1) / BLOCK_SIZE;
  210. uint extra = audio->frames % BLOCK_SIZE;
  211. for (uint pass = 0; pass < passes; pass++) {
  212. uint frames = pass == passes - 1 && extra ? extra
  213. : BLOCK_SIZE;
  214. silenceChannel(outputs, numChannels, BLOCK_SIZE);
  215. for (size_t d = 0; d < numChannels; d++) {
  216. if (d < MAX_AV_PLANES &&
  217. audio->data[d] != nullptr) {
  218. channelrefs[d] =
  219. ((float *)audio->data[d]) +
  220. (pass * BLOCK_SIZE);
  221. } else {
  222. channelrefs[d] = inputs[d];
  223. }
  224. };
  225. effect->processReplacing(effect, channelrefs, outputs,
  226. frames);
  227. // only copy back the channels the plugin may have generated
  228. for (size_t c = 0; c < (size_t)effect->numOutputs &&
  229. c < MAX_AV_PLANES;
  230. c++) {
  231. if (audio->data[c]) {
  232. for (size_t i = 0; i < frames; i++) {
  233. channelrefs[c][i] =
  234. outputs[c][i];
  235. }
  236. }
  237. }
  238. }
  239. }
  240. return audio;
  241. }
  242. void VSTPlugin::unloadEffect()
  243. {
  244. closeEditor();
  245. {
  246. std::lock_guard<std::recursive_mutex> lock(lockEffect);
  247. // Reset the status firstly to avoid VSTPlugin::process is blocked
  248. effectReady = false;
  249. if (effect) {
  250. effect->dispatcher(effect, effMainsChanged, 0, 0,
  251. nullptr, 0);
  252. effect->dispatcher(effect, effClose, 0, 0, nullptr,
  253. 0.0f);
  254. }
  255. effect = nullptr;
  256. }
  257. unloadLibrary();
  258. pluginPath.clear();
  259. }
  260. bool VSTPlugin::isEditorOpen()
  261. {
  262. return editorWidget ? true : false;
  263. }
  264. void VSTPlugin::onEditorClosed()
  265. {
  266. if (!editorWidget)
  267. return;
  268. editorWidget->deleteLater();
  269. editorWidget = nullptr;
  270. if (effect && editorOpened) {
  271. editorOpened = false;
  272. effect->dispatcher(effect, effEditClose, 0, 0, nullptr, 0);
  273. }
  274. }
  275. void VSTPlugin::openEditor()
  276. {
  277. if (effect && !editorWidget) {
  278. // This check logic is refer to open source project : Audacity
  279. if (!(effect->flags & effFlagsHasEditor)) {
  280. blog(LOG_WARNING,
  281. "VST Plug-in: Can't support edit feature. '%s'",
  282. pluginPath.c_str());
  283. return;
  284. }
  285. editorOpened = true;
  286. editorWidget = new EditorWidget(nullptr, this);
  287. editorWidget->buildEffectContainer(effect);
  288. if (sourceName.empty()) {
  289. sourceName = "VST 2.x";
  290. }
  291. if (filterName.empty()) {
  292. editorWidget->setWindowTitle(QString("%1 - %2").arg(
  293. sourceName.c_str(), effectName));
  294. } else {
  295. editorWidget->setWindowTitle(
  296. QString("%1: %2 - %3")
  297. .arg(sourceName.c_str(),
  298. filterName.c_str(), effectName));
  299. }
  300. editorWidget->show();
  301. }
  302. }
  303. void VSTPlugin::closeEditor()
  304. {
  305. if (editorWidget)
  306. editorWidget->close();
  307. }
  308. std::string VSTPlugin::getEffectPath()
  309. {
  310. return pluginPath;
  311. }
  312. std::string VSTPlugin::getChunk()
  313. {
  314. if (!effect) {
  315. return "";
  316. }
  317. if (effect->flags & effFlagsProgramChunks) {
  318. void *buf = nullptr;
  319. intptr_t chunkSize = effect->dispatcher(effect, effGetChunk, 1,
  320. 0, &buf, 0.0);
  321. QByteArray data = QByteArray((char *)buf, chunkSize);
  322. return QString(data.toBase64()).toStdString();
  323. } else {
  324. std::vector<float> params;
  325. for (int i = 0; i < effect->numParams; i++) {
  326. float parameter = effect->getParameter(effect, i);
  327. params.push_back(parameter);
  328. }
  329. const char *bytes = reinterpret_cast<const char *>(&params[0]);
  330. QByteArray data =
  331. QByteArray(bytes, (int)(sizeof(float) * params.size()));
  332. std::string encoded = QString(data.toBase64()).toStdString();
  333. return encoded;
  334. }
  335. }
  336. void VSTPlugin::setChunk(const std::string &data)
  337. {
  338. if (!effect) {
  339. return;
  340. }
  341. if (effect->flags & effFlagsProgramChunks) {
  342. QByteArray base64Data =
  343. QByteArray(data.c_str(), (int)data.length());
  344. QByteArray chunkData = QByteArray::fromBase64(base64Data);
  345. void *buf = nullptr;
  346. buf = chunkData.data();
  347. effect->dispatcher(effect, effSetChunk, 1, chunkData.length(),
  348. buf, 0);
  349. } else {
  350. QByteArray base64Data =
  351. QByteArray(data.c_str(), (int)data.length());
  352. QByteArray paramData = QByteArray::fromBase64(base64Data);
  353. const char *p_chars = paramData.data();
  354. const float *p_floats =
  355. reinterpret_cast<const float *>(p_chars);
  356. const size_t size = paramData.length() / sizeof(float);
  357. std::vector<float> params(p_floats, p_floats + size);
  358. if (params.size() != (size_t)effect->numParams) {
  359. return;
  360. }
  361. for (int i = 0; i < effect->numParams; i++) {
  362. effect->setParameter(effect, i, params[i]);
  363. }
  364. }
  365. }
  366. void VSTPlugin::setProgram(const int programNumber)
  367. {
  368. if (programNumber < effect->numPrograms) {
  369. effect->dispatcher(effect, effSetProgram, 0, programNumber,
  370. NULL, 0.0f);
  371. } else {
  372. blog(LOG_ERROR,
  373. "Failed to load program, number was outside possible program range.");
  374. }
  375. }
  376. int VSTPlugin::getProgram()
  377. {
  378. return effect->dispatcher(effect, effGetProgram, 0, 0, NULL, 0.0f);
  379. }
  380. void VSTPlugin::getSourceNames()
  381. {
  382. /* Only call inside the vst_filter_audio function! */
  383. sourceName = obs_source_get_name(obs_filter_get_parent(sourceContext));
  384. filterName = obs_source_get_name(sourceContext);
  385. }