Browse Source

obs-vst: Remove submodule in favour of direct merge 2/2

Please visit the submodule repo for blame history:
https://github.com/obsproject/obs-vst

This also replaces the obs-vst .clang-format with obs-studio's.

This commit depends on the previous commit, which removes the submodule
separately as Git complains otherwise.

Co-authored-by: Alex Anderson <[email protected]>
Co-authored-by: Anton <[email protected]>
Co-authored-by: Blue Cat Audio <[email protected]>
Co-authored-by: Cephas Reis <[email protected]>
Co-authored-by: Colin Edwards <[email protected]>
Co-authored-by: Florian Zwoch <[email protected]>
Co-authored-by: Fogmoon <[email protected]>
Co-authored-by: Gol-D-Ace <[email protected]>
Co-authored-by: Igor Bochkariov <[email protected]>
Co-authored-by: Jesse Chappell <[email protected]>
Co-authored-by: Keen <[email protected]>
Co-authored-by: Kurt Kartaltepe <[email protected]>
Co-authored-by: Matt Gajownik <[email protected]>
Co-authored-by: Matt Gajownik <[email protected]>
Co-authored-by: Richard Stanway <[email protected]>
Co-authored-by: Ryan Foster <[email protected]>
Co-authored-by: follower <[email protected]>
Co-authored-by: gxalpha <[email protected]>
Co-authored-by: jp9000 <[email protected]>
Co-authored-by: jpark37 <[email protected]>
Co-authored-by: mntone <[email protected]>
Co-authored-by: tytan652 <[email protected]>
Co-authored-by: wangshaohui <[email protected]>
Co-authored-by: wangshaohui <[email protected]>
Matt Gajownik 3 years ago
parent
commit
a7c3d94e4c
66 changed files with 2110 additions and 2 deletions
  1. 1 2
      CI/check-format.sh
  2. 3 0
      plugins/CMakeLists.txt
  3. 88 0
      plugins/obs-vst/CMakeLists.txt
  4. 32 0
      plugins/obs-vst/EditorWidget.cpp
  5. 464 0
      plugins/obs-vst/VSTPlugin.cpp
  6. 4 0
      plugins/obs-vst/data/locale/an-ES.ini
  7. 4 0
      plugins/obs-vst/data/locale/ar-SA.ini
  8. 4 0
      plugins/obs-vst/data/locale/bg-BG.ini
  9. 4 0
      plugins/obs-vst/data/locale/bn-BD.ini
  10. 4 0
      plugins/obs-vst/data/locale/ca-ES.ini
  11. 3 0
      plugins/obs-vst/data/locale/cs-CZ.ini
  12. 4 0
      plugins/obs-vst/data/locale/da-DK.ini
  13. 4 0
      plugins/obs-vst/data/locale/de-DE.ini
  14. 3 0
      plugins/obs-vst/data/locale/el-GR.ini
  15. 4 0
      plugins/obs-vst/data/locale/en-US.ini
  16. 4 0
      plugins/obs-vst/data/locale/es-ES.ini
  17. 3 0
      plugins/obs-vst/data/locale/et-EE.ini
  18. 4 0
      plugins/obs-vst/data/locale/eu-ES.ini
  19. 4 0
      plugins/obs-vst/data/locale/fa-IR.ini
  20. 3 0
      plugins/obs-vst/data/locale/fi-FI.ini
  21. 4 0
      plugins/obs-vst/data/locale/fil-PH.ini
  22. 4 0
      plugins/obs-vst/data/locale/fr-FR.ini
  23. 4 0
      plugins/obs-vst/data/locale/gd-GB.ini
  24. 4 0
      plugins/obs-vst/data/locale/gl-ES.ini
  25. 4 0
      plugins/obs-vst/data/locale/he-IL.ini
  26. 4 0
      plugins/obs-vst/data/locale/hi-IN.ini
  27. 4 0
      plugins/obs-vst/data/locale/hu-HU.ini
  28. 3 0
      plugins/obs-vst/data/locale/id-ID.ini
  29. 4 0
      plugins/obs-vst/data/locale/it-IT.ini
  30. 4 0
      plugins/obs-vst/data/locale/ja-JP.ini
  31. 4 0
      plugins/obs-vst/data/locale/ka-GE.ini
  32. 4 0
      plugins/obs-vst/data/locale/kab-KAB.ini
  33. 4 0
      plugins/obs-vst/data/locale/kmr-TR.ini
  34. 4 0
      plugins/obs-vst/data/locale/ko-KR.ini
  35. 4 0
      plugins/obs-vst/data/locale/ms-MY.ini
  36. 4 0
      plugins/obs-vst/data/locale/nb-NO.ini
  37. 4 0
      plugins/obs-vst/data/locale/nl-NL.ini
  38. 4 0
      plugins/obs-vst/data/locale/pl-PL.ini
  39. 4 0
      plugins/obs-vst/data/locale/pt-BR.ini
  40. 4 0
      plugins/obs-vst/data/locale/pt-PT.ini
  41. 4 0
      plugins/obs-vst/data/locale/ro-RO.ini
  42. 4 0
      plugins/obs-vst/data/locale/ru-RU.ini
  43. 3 0
      plugins/obs-vst/data/locale/sk-SK.ini
  44. 4 0
      plugins/obs-vst/data/locale/sl-SI.ini
  45. 4 0
      plugins/obs-vst/data/locale/sr-CS.ini
  46. 4 0
      plugins/obs-vst/data/locale/sr-SP.ini
  47. 4 0
      plugins/obs-vst/data/locale/sv-SE.ini
  48. 4 0
      plugins/obs-vst/data/locale/szl-PL.ini
  49. 4 0
      plugins/obs-vst/data/locale/tl-PH.ini
  50. 4 0
      plugins/obs-vst/data/locale/tr-TR.ini
  51. 4 0
      plugins/obs-vst/data/locale/uk-UA.ini
  52. 4 0
      plugins/obs-vst/data/locale/vi-VN.ini
  53. 4 0
      plugins/obs-vst/data/locale/zh-CN.ini
  54. 4 0
      plugins/obs-vst/data/locale/zh-TW.ini
  55. 63 0
      plugins/obs-vst/headers/EditorWidget.h
  56. 110 0
      plugins/obs-vst/headers/VSTPlugin.h
  57. 21 0
      plugins/obs-vst/headers/vst-plugin-callbacks.hpp
  58. 37 0
      plugins/obs-vst/linux/EditorWidget-linux.cpp
  59. 65 0
      plugins/obs-vst/linux/VSTPlugin-linux.cpp
  60. 62 0
      plugins/obs-vst/mac/EditorWidget-osx.mm
  61. 89 0
      plugins/obs-vst/mac/VSTPlugin-osx.mm
  62. 332 0
      plugins/obs-vst/obs-vst.cpp
  63. 3 0
      plugins/obs-vst/vst_header/.clang-format
  64. 382 0
      plugins/obs-vst/vst_header/aeffectx.h
  65. 77 0
      plugins/obs-vst/win/EditorWidget-win.cpp
  66. 91 0
      plugins/obs-vst/win/VSTPlugin-win.cpp

+ 1 - 2
CI/check-format.sh

@@ -47,8 +47,7 @@ find . -type d \( \
     -path ./plugins/decklink/\*/decklink-sdk -o \
     -path ./plugins/enc-amf -o \
     -path ./plugins/mac-syphon/syphon-framework -o \
-    -path ./plugins/obs-outputs/ftl-sdk -o \
-    -path ./plugins/obs-vst \
+    -path ./plugins/obs-outputs/ftl-sdk \
 \) -prune -false -type f -o \
     -name '*.h' -or \
     -name '*.hpp' -or \

+ 3 - 0
plugins/CMakeLists.txt

@@ -24,6 +24,7 @@ if(OS_WINDOWS)
   add_subdirectory(obs-qsv11)
   add_subdirectory(obs-text)
   add_subdirectory(vlc-video)
+  add_subdirectory(obs-vst)
 
   if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/enc-amf/CMakeLists.txt")
     add_subdirectory(enc-amf)
@@ -45,6 +46,7 @@ elseif(OS_MACOS)
   add_subdirectory(decklink)
   add_subdirectory(vlc-video)
   add_subdirectory(linux-jack)
+  add_subdirectory(obs-vst)
 
   check_obs_browser()
 elseif(OS_LINUX)
@@ -57,6 +59,7 @@ elseif(OS_LINUX)
   add_subdirectory(decklink)
   add_subdirectory(vlc-video)
   add_subdirectory(sndio)
+  add_subdirectory(obs-vst)
 
   check_obs_browser()
 elseif(OS_FREEBSD)

+ 88 - 0
plugins/obs-vst/CMakeLists.txt

@@ -0,0 +1,88 @@
+project(obs-vst)
+
+option(ENABLE_VST "Enable building OBS with VST plugin" ON)
+
+if(NOT ENABLE_VST)
+  message(STATUS "OBS:  DISABLED   obs-vst")
+  return()
+endif()
+
+option(ENABLE_VST_BUNDLED_HEADERS "Build with Bundled Headers" ON)
+mark_as_advanced(ENABLE_VST_BUNDLED_HEADERS)
+
+add_library(obs-vst MODULE)
+add_library(OBS::vst ALIAS obs-vst)
+
+find_qt(COMPONENTS Widgets)
+
+set_target_properties(
+  obs-vst
+  PROPERTIES AUTOMOC ON
+             AUTOUIC ON
+             AUTORCC ON)
+
+target_include_directories(
+  obs-vst PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
+                  ${CMAKE_CURRENT_BINARY_DIR})
+
+target_sources(
+  obs-vst
+  PRIVATE obs-vst.cpp VSTPlugin.cpp EditorWidget.cpp
+          headers/vst-plugin-callbacks.hpp headers/EditorWidget.h
+          headers/VSTPlugin.h)
+
+target_link_libraries(obs-vst PRIVATE OBS::libobs Qt::Widgets)
+
+target_include_directories(
+  obs-vst PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/headers)
+
+target_compile_features(obs-vst PRIVATE cxx_std_17)
+
+if(ENABLE_VST_BUNDLED_HEADERS)
+  message(STATUS "OBS:    -        obs-vst uses bundled VST headers")
+
+  target_sources(obs-vst PRIVATE vst_header/aeffectx.h)
+
+  target_include_directories(obs-vst
+                             PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/vst_header)
+else()
+  set(VST_INCLUDE_DIR
+      ""
+      CACHE PATH
+            "Path to Steinburg headers (e.g. C:/VST3 SDK/pluginterfaces/vst2.x)"
+            FORCE)
+  mark_as_advanced(VST_INCLUDE_DIR)
+
+  message(
+    WARNING
+      "OBS: You should only use the Steinburg headers for debugging or local builds. "
+      "It is illegal to distribute the Steinburg headers with anything, and "
+      "possibly against the GPL to distribute the binaries from the resultant compile."
+  )
+
+  target_sources(obs-vst PRIVATE ${VST_INCLUDE_DIR}/aeffectx.h)
+endif()
+
+if(OS_MACOS)
+  find_library(FOUNDATION Foundation)
+  find_library(COCOA Cocoa)
+  mark_as_advanced(COCOA FOUNDATION)
+
+  target_sources(obs-vst PRIVATE mac/VSTPlugin-osx.mm mac/EditorWidget-osx.mm)
+
+  target_link_libraries(obs-vst PRIVATE ${COCOA} ${FOUNDATION})
+
+elseif(OS_WINDOWS)
+  target_sources(obs-vst PRIVATE win/VSTPlugin-win.cpp win/EditorWidget-win.cpp)
+
+  target_compile_definitions(obs-vst PRIVATE UNICODE _UNICODE)
+
+elseif(OS_POSIX)
+  target_sources(obs-vst PRIVATE linux/VSTPlugin-linux.cpp
+                                 linux/EditorWidget-linux.cpp)
+
+endif()
+
+set_target_properties(obs-vst PROPERTIES FOLDER "plugins" PREFIX "")
+
+setup_plugin_target(obs-vst)

+ 32 - 0
plugins/obs-vst/EditorWidget.cpp

@@ -0,0 +1,32 @@
+/*****************************************************************************
+Copyright (C) 2016-2017 by Colin Edwards.
+Additional Code Copyright (C) 2016-2017 by c3r1c3 <[email protected]>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*****************************************************************************/
+
+#include "headers/EditorWidget.h"
+#include <QCloseEvent>
+
+EditorWidget::EditorWidget(QWidget *parent, VSTPlugin *plugin)
+	: QWidget(parent), plugin(plugin)
+{
+	setWindowFlags(this->windowFlags() |= Qt::MSWindowsFixedSizeDialogHint);
+}
+
+void EditorWidget::closeEvent(QCloseEvent *event)
+{
+	plugin->onEditorClosed();
+	UNUSED_PARAMETER(event);
+}

+ 464 - 0
plugins/obs-vst/VSTPlugin.cpp

@@ -0,0 +1,464 @@
+/*****************************************************************************
+Copyright (C) 2016-2017 by Colin Edwards.
+Additional Code Copyright (C) 2016-2017 by c3r1c3 <[email protected]>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*****************************************************************************/
+
+#include "headers/VSTPlugin.h"
+#include <util/platform.h>
+
+intptr_t VSTPlugin::hostCallback_static(AEffect *effect, int32_t opcode,
+					int32_t index, intptr_t value,
+					void *ptr, float opt)
+{
+	UNUSED_PARAMETER(opt);
+	UNUSED_PARAMETER(ptr);
+
+	VSTPlugin *plugin = nullptr;
+	if (effect && effect->user) {
+		plugin = static_cast<VSTPlugin *>(effect->user);
+	}
+
+	switch (opcode) {
+	case audioMasterVersion:
+		return (intptr_t)2400;
+
+	case audioMasterGetCurrentProcessLevel:
+		return 1;
+
+	// We always replace, never accumulate
+	case audioMasterWillReplaceOrAccumulate:
+		return 1;
+
+	case audioMasterGetSampleRate:
+		if (plugin) {
+			return (intptr_t)plugin->GetSampleRate();
+		}
+		return 0;
+
+	case audioMasterGetTime:
+		if (plugin) {
+			return (intptr_t)plugin->GetTimeInfo();
+		}
+		return 0;
+
+	// index: width, value: height
+	case audioMasterSizeWindow:
+		if (plugin && plugin->editorWidget) {
+			plugin->editorWidget->handleResizeRequest(index, value);
+		}
+		return 1;
+
+	default:
+		return 0;
+	}
+}
+
+VstTimeInfo *VSTPlugin::GetTimeInfo()
+{
+	mTimeInfo.nanoSeconds = os_gettime_ns() / 1000000;
+	return &mTimeInfo;
+}
+
+float VSTPlugin::GetSampleRate()
+{
+	return mTimeInfo.sampleRate;
+}
+
+VSTPlugin::VSTPlugin(obs_source_t *sourceContext) : sourceContext{sourceContext}
+{
+}
+
+VSTPlugin::~VSTPlugin()
+{
+	unloadEffect();
+
+	cleanupChannelBuffers();
+}
+
+void VSTPlugin::createChannelBuffers(size_t count)
+{
+	cleanupChannelBuffers();
+
+	int blocksize = BLOCK_SIZE;
+	numChannels = (std::max)((size_t)0, count);
+
+	if (numChannels > 0) {
+		inputs = (float **)malloc(sizeof(float *) * numChannels);
+		outputs = (float **)malloc(sizeof(float *) * numChannels);
+		channelrefs = (float **)malloc(sizeof(float *) * numChannels);
+		for (size_t channel = 0; channel < numChannels; channel++) {
+			inputs[channel] =
+				(float *)malloc(sizeof(float) * blocksize);
+			outputs[channel] =
+				(float *)malloc(sizeof(float) * blocksize);
+		}
+	}
+}
+
+void VSTPlugin::cleanupChannelBuffers()
+{
+	for (size_t channel = 0; channel < numChannels; channel++) {
+		if (inputs && inputs[channel]) {
+			free(inputs[channel]);
+			inputs[channel] = NULL;
+		}
+		if (outputs && outputs[channel]) {
+			free(outputs[channel]);
+			outputs[channel] = NULL;
+		}
+	}
+	if (inputs) {
+		free(inputs);
+		inputs = NULL;
+	}
+	if (outputs) {
+		free(outputs);
+		outputs = NULL;
+	}
+	if (channelrefs) {
+		free(channelrefs);
+		channelrefs = NULL;
+	}
+	numChannels = 0;
+}
+
+void VSTPlugin::loadEffectFromPath(std::string path)
+{
+	if (this->pluginPath.compare(path) != 0) {
+		unloadEffect();
+		blog(LOG_INFO, "User selected new VST plugin: '%s'",
+		     path.c_str());
+	}
+
+	if (!effect) {
+		// TODO: alert user of error if VST is not available.
+
+		pluginPath = path;
+
+		AEffect *effectTemp = loadEffect();
+		if (!effectTemp) {
+			blog(LOG_WARNING, "VST Plug-in: Can't load effect!");
+			return;
+		}
+
+		{
+			std::lock_guard<std::recursive_mutex> lock(lockEffect);
+			effect = effectTemp;
+		}
+
+		// Check plug-in's magic number
+		// If incorrect, then the file either was not loaded properly,
+		// is not a real VST plug-in, or is otherwise corrupt.
+		if (effect->magic != kEffectMagic) {
+			blog(LOG_WARNING, "VST Plug-in's magic number is bad");
+			return;
+		}
+
+		int maxchans =
+			(std::max)(effect->numInputs, effect->numOutputs);
+		// sanity check
+		if (maxchans < 0 || maxchans > 256) {
+			blog(LOG_WARNING,
+			     "VST Plug-in has invalid number of channels");
+			return;
+		}
+
+		createChannelBuffers(maxchans);
+
+		// It is better to invoke this code after checking magic number
+		effect->dispatcher(effect, effGetEffectName, 0, 0, effectName,
+				   0);
+		effect->dispatcher(effect, effGetVendorString, 0, 0,
+				   vendorString, 0);
+
+		// This check logic is refer to open source project : Audacity
+		if ((effect->flags & effFlagsIsSynth) ||
+		    !(effect->flags & effFlagsCanReplacing)) {
+			blog(LOG_WARNING,
+			     "VST Plug-in can't support replacing. '%s'",
+			     path.c_str());
+			return;
+		}
+
+		// Ask the plugin to identify itself...might be needed for older plugins
+		effect->dispatcher(effect, effIdentify, 0, 0, nullptr, 0.0f);
+
+		effect->dispatcher(effect, effOpen, 0, 0, nullptr, 0.0f);
+
+		// Set some default properties
+		size_t sampleRate =
+			audio_output_get_sample_rate(obs_get_audio());
+
+		// Initialize time info
+		memset(&mTimeInfo, 0, sizeof(mTimeInfo));
+		mTimeInfo.sampleRate = sampleRate;
+		mTimeInfo.nanoSeconds = os_gettime_ns() / 1000000;
+		mTimeInfo.tempo = 120.0;
+		mTimeInfo.timeSigNumerator = 4;
+		mTimeInfo.timeSigDenominator = 4;
+		mTimeInfo.flags = kVstTempoValid | kVstNanosValid |
+				  kVstTransportPlaying;
+
+		effect->dispatcher(effect, effSetSampleRate, 0, 0, nullptr,
+				   sampleRate);
+		int blocksize = BLOCK_SIZE;
+		effect->dispatcher(effect, effSetBlockSize, 0, blocksize,
+				   nullptr, 0.0f);
+
+		effect->dispatcher(effect, effMainsChanged, 0, 1, nullptr, 0);
+
+		effectReady = true;
+
+		if (openInterfaceWhenActive) {
+			openEditor();
+		}
+	}
+}
+
+static void silenceChannel(float **channelData, size_t numChannels,
+			   long numFrames)
+{
+	for (size_t channel = 0; channel < numChannels; ++channel) {
+		for (long frame = 0; frame < numFrames; ++frame) {
+			channelData[channel][frame] = 0.0f;
+		}
+	}
+}
+
+obs_audio_data *VSTPlugin::process(struct obs_audio_data *audio)
+{
+	// Here we check the status firstly,
+	// which help avoid waiting for lock while unloadEffect() is running.
+	bool effectValid = (effect && effectReady && numChannels > 0);
+	if (!effectValid)
+		return audio;
+
+	std::lock_guard<std::recursive_mutex> lock(lockEffect);
+
+	if (effect && effectReady && numChannels > 0) {
+		uint passes = (audio->frames + BLOCK_SIZE - 1) / BLOCK_SIZE;
+		uint extra = audio->frames % BLOCK_SIZE;
+		for (uint pass = 0; pass < passes; pass++) {
+			uint frames = pass == passes - 1 && extra ? extra
+								  : BLOCK_SIZE;
+			silenceChannel(outputs, numChannels, BLOCK_SIZE);
+
+			for (size_t d = 0; d < numChannels; d++) {
+				if (d < MAX_AV_PLANES &&
+				    audio->data[d] != nullptr) {
+					channelrefs[d] =
+						((float *)audio->data[d]) +
+						(pass * BLOCK_SIZE);
+				} else {
+					channelrefs[d] = inputs[d];
+				}
+			};
+
+			effect->processReplacing(effect, channelrefs, outputs,
+						 frames);
+
+			// only copy back the channels the plugin may have generated
+			for (size_t c = 0; c < (size_t)effect->numOutputs &&
+					   c < MAX_AV_PLANES;
+			     c++) {
+				if (audio->data[c]) {
+					for (size_t i = 0; i < frames; i++) {
+						channelrefs[c][i] =
+							outputs[c][i];
+					}
+				}
+			}
+		}
+	}
+
+	return audio;
+}
+
+void VSTPlugin::unloadEffect()
+{
+	closeEditor();
+
+	{
+		std::lock_guard<std::recursive_mutex> lock(lockEffect);
+
+		// Reset the status firstly to avoid VSTPlugin::process is blocked
+		effectReady = false;
+
+		if (effect) {
+			effect->dispatcher(effect, effMainsChanged, 0, 0,
+					   nullptr, 0);
+			effect->dispatcher(effect, effClose, 0, 0, nullptr,
+					   0.0f);
+		}
+
+		effect = nullptr;
+	}
+
+	unloadLibrary();
+
+	pluginPath = "";
+}
+
+bool VSTPlugin::isEditorOpen()
+{
+	return editorWidget ? true : false;
+}
+
+void VSTPlugin::onEditorClosed()
+{
+	if (!editorWidget)
+		return;
+
+	editorWidget->deleteLater();
+	editorWidget = nullptr;
+
+	if (effect && editorOpened) {
+		editorOpened = false;
+		effect->dispatcher(effect, effEditClose, 0, 0, nullptr, 0);
+	}
+}
+
+void VSTPlugin::openEditor()
+{
+	if (effect && !editorWidget) {
+		// This check logic is refer to open source project : Audacity
+		if (!(effect->flags & effFlagsHasEditor)) {
+			blog(LOG_WARNING,
+			     "VST Plug-in: Can't support edit feature. '%s'",
+			     pluginPath.c_str());
+			return;
+		}
+
+		editorOpened = true;
+		editorWidget = new EditorWidget(nullptr, this);
+		editorWidget->buildEffectContainer(effect);
+
+		if (sourceName.empty()) {
+			sourceName = "VST 2.x";
+		}
+
+		if (filterName.empty()) {
+			editorWidget->setWindowTitle(QString("%1 - %2").arg(
+				sourceName.c_str(), effectName));
+		} else {
+			editorWidget->setWindowTitle(
+				QString("%1: %2 - %3")
+					.arg(sourceName.c_str(),
+					     filterName.c_str(), effectName));
+		}
+		editorWidget->show();
+	}
+}
+
+void VSTPlugin::closeEditor()
+{
+	if (editorWidget)
+		editorWidget->close();
+}
+
+std::string VSTPlugin::getEffectPath()
+{
+	return pluginPath;
+}
+
+std::string VSTPlugin::getChunk()
+{
+	if (!effect) {
+		return "";
+	}
+
+	if (effect->flags & effFlagsProgramChunks) {
+		void *buf = nullptr;
+
+		intptr_t chunkSize = effect->dispatcher(effect, effGetChunk, 1,
+							0, &buf, 0.0);
+
+		QByteArray data = QByteArray((char *)buf, chunkSize);
+		return QString(data.toBase64()).toStdString();
+	} else {
+		std::vector<float> params;
+		for (int i = 0; i < effect->numParams; i++) {
+			float parameter = effect->getParameter(effect, i);
+			params.push_back(parameter);
+		}
+
+		const char *bytes = reinterpret_cast<const char *>(&params[0]);
+		QByteArray data =
+			QByteArray(bytes, (int)(sizeof(float) * params.size()));
+		std::string encoded = QString(data.toBase64()).toStdString();
+		return encoded;
+	}
+}
+
+void VSTPlugin::setChunk(std::string data)
+{
+	if (!effect) {
+		return;
+	}
+
+	if (effect->flags & effFlagsProgramChunks) {
+		QByteArray base64Data =
+			QByteArray(data.c_str(), (int)data.length());
+		QByteArray chunkData = QByteArray::fromBase64(base64Data);
+		void *buf = nullptr;
+		buf = chunkData.data();
+		effect->dispatcher(effect, effSetChunk, 1, chunkData.length(),
+				   buf, 0);
+	} else {
+		QByteArray base64Data =
+			QByteArray(data.c_str(), (int)data.length());
+		QByteArray paramData = QByteArray::fromBase64(base64Data);
+
+		const char *p_chars = paramData.data();
+		const float *p_floats =
+			reinterpret_cast<const float *>(p_chars);
+
+		const size_t size = paramData.length() / sizeof(float);
+
+		std::vector<float> params(p_floats, p_floats + size);
+
+		if (params.size() != (size_t)effect->numParams) {
+			return;
+		}
+
+		for (int i = 0; i < effect->numParams; i++) {
+			effect->setParameter(effect, i, params[i]);
+		}
+	}
+}
+
+void VSTPlugin::setProgram(const int programNumber)
+{
+	if (programNumber < effect->numPrograms) {
+		effect->dispatcher(effect, effSetProgram, 0, programNumber,
+				   NULL, 0.0f);
+	} else {
+		blog(LOG_ERROR,
+		     "Failed to load program, number was outside possible program range.");
+	}
+}
+
+int VSTPlugin::getProgram()
+{
+	return effect->dispatcher(effect, effGetProgram, 0, 0, NULL, 0.0f);
+}
+
+void VSTPlugin::getSourceNames()
+{
+	/* Only call inside the vst_filter_audio function! */
+	sourceName = obs_source_get_name(obs_filter_get_parent(sourceContext));
+	filterName = obs_source_get_name(sourceContext);
+}

+ 4 - 0
plugins/obs-vst/data/locale/an-ES.ini

@@ -0,0 +1,4 @@
+OpenPluginInterface="Ubrir la interfaz d'a extensión"
+ClosePluginInterface="Zarrar la interfaz d'a extension"
+VstPlugin="Extensión VST 2.x"
+OpenInterfaceWhenActive="Ubrir la interfaz quan siga activo"

+ 4 - 0
plugins/obs-vst/data/locale/ar-SA.ini

@@ -0,0 +1,4 @@
+OpenPluginInterface="فتح واجهة الإضافة"
+ClosePluginInterface="إغلاق واجهة الإضافة"
+VstPlugin="إضافة VST 2.x"
+OpenInterfaceWhenActive="فتح الواجهة عند النشاط"

+ 4 - 0
plugins/obs-vst/data/locale/bg-BG.ini

@@ -0,0 +1,4 @@
+OpenPluginInterface="Отваряне на интерфейса на приставката"
+ClosePluginInterface="Затваряне на интерфейса на приставката"
+VstPlugin="Приставка VST 2.x"
+OpenInterfaceWhenActive="Отваряне на интерфейса при задействане"

+ 4 - 0
plugins/obs-vst/data/locale/bn-BD.ini

@@ -0,0 +1,4 @@
+OpenPluginInterface="প্লাগ-ইন ইন্টারফেসের খুলুন"
+ClosePluginInterface="প্লাগ-ইন ইন্টারফেস বন্ধ করুন"
+VstPlugin="ভিএসটি ২.x প্লাগ-ইন"
+OpenInterfaceWhenActive="সক্রিয় থাকাকালীন ইন্টারফেস খুলুন"

+ 4 - 0
plugins/obs-vst/data/locale/ca-ES.ini

@@ -0,0 +1,4 @@
+OpenPluginInterface="Obre l'interfície de l'extensió"
+ClosePluginInterface="Tanca la interfície de l'extensió"
+VstPlugin="Extensió VST 2.x"
+OpenInterfaceWhenActive="Obre l'interfície quan està actiu"

+ 3 - 0
plugins/obs-vst/data/locale/cs-CZ.ini

@@ -0,0 +1,3 @@
+OpenPluginInterface="Otevřít rozhraní Plug-inu"
+ClosePluginInterface="Zavřít rozhraní Plug-inu"
+OpenInterfaceWhenActive="Otevřít rozhraní, pokud je aktivní"

+ 4 - 0
plugins/obs-vst/data/locale/da-DK.ini

@@ -0,0 +1,4 @@
+OpenPluginInterface="Åbn plug-in grænseflade"
+ClosePluginInterface="Luk plug-in grænseflade"
+VstPlugin="VST 2.x plug-in"
+OpenInterfaceWhenActive="Åbn grænseflade, når aktiv"

+ 4 - 0
plugins/obs-vst/data/locale/de-DE.ini

@@ -0,0 +1,4 @@
+OpenPluginInterface="Pluginschnittstelle öffnen"
+ClosePluginInterface="Pluginschnittstelle schließen"
+VstPlugin="VST-2.x-Plugin"
+OpenInterfaceWhenActive="Schnittstelle öffnen, wenn aktiv"

+ 3 - 0
plugins/obs-vst/data/locale/el-GR.ini

@@ -0,0 +1,3 @@
+OpenPluginInterface="Άνοιγμα διεπαφής Plug-in"
+ClosePluginInterface="Κλείσιμο διεπαφής Plug-in"
+OpenInterfaceWhenActive="Άνοιγμα διεπαφής όταν είναι ενεργό"

+ 4 - 0
plugins/obs-vst/data/locale/en-US.ini

@@ -0,0 +1,4 @@
+OpenPluginInterface="Open Plug-in Interface"
+ClosePluginInterface="Close Plug-in Interface"
+VstPlugin="VST 2.x Plug-in"
+OpenInterfaceWhenActive="Open interface when active"

+ 4 - 0
plugins/obs-vst/data/locale/es-ES.ini

@@ -0,0 +1,4 @@
+OpenPluginInterface="Abrir la interfaz de la extensión"
+ClosePluginInterface="Cerrar la interfaz de la extensión"
+VstPlugin="Extensión VST 2.x"
+OpenInterfaceWhenActive="Abrir interfaz cuando está activo"

+ 3 - 0
plugins/obs-vst/data/locale/et-EE.ini

@@ -0,0 +1,3 @@
+OpenPluginInterface="Avatud Plug-in liides"
+ClosePluginInterface="Sulgege Plug-in liides"
+OpenInterfaceWhenActive="Ava liides kui aktiivne"

+ 4 - 0
plugins/obs-vst/data/locale/eu-ES.ini

@@ -0,0 +1,4 @@
+OpenPluginInterface="Ireki pluginaren interfazea"
+ClosePluginInterface="Itxi pluginaren interfazea"
+VstPlugin="VST 2.x Plugina"
+OpenInterfaceWhenActive="Ireki interfazea aktibatzen denean"

+ 4 - 0
plugins/obs-vst/data/locale/fa-IR.ini

@@ -0,0 +1,4 @@
+OpenPluginInterface="بازکردن رابط افزونه"
+ClosePluginInterface="بستن رابط افزونه"
+VstPlugin="VST 2.x افزونه"
+OpenInterfaceWhenActive="رابط کاربری باز زمانی که فعال"

+ 3 - 0
plugins/obs-vst/data/locale/fi-FI.ini

@@ -0,0 +1,3 @@
+OpenPluginInterface="Avaa liitännäisen käyttöliittymä"
+ClosePluginInterface="Sulje liitännäisen käyttöliittymä"
+OpenInterfaceWhenActive="Avaa käyttöliittymä kun aktiivinen"

+ 4 - 0
plugins/obs-vst/data/locale/fil-PH.ini

@@ -0,0 +1,4 @@
+OpenPluginInterface="Buksan ang interface at isaksak"
+ClosePluginInterface="Isarado ang interface at isaksak"
+VstPlugin="VST 2. x Isaksak"
+OpenInterfaceWhenActive="Buksan ang interface kapag aktibo"

+ 4 - 0
plugins/obs-vst/data/locale/fr-FR.ini

@@ -0,0 +1,4 @@
+OpenPluginInterface="Afficher l'interface graphique du plug-in"
+ClosePluginInterface="Fermer l'interface graphique du plug-in"
+VstPlugin="Plug-in VST 2.x"
+OpenInterfaceWhenActive="Ouvrir l'interface graphique du plug-in quand il est actif"

+ 4 - 0
plugins/obs-vst/data/locale/gd-GB.ini

@@ -0,0 +1,4 @@
+OpenPluginInterface="Fosgail eadar-aghaidh a’ phlugain"
+ClosePluginInterface="Dùin eadar-aghaidh a’ phlugain"
+VstPlugin="Plugan VST 2.x"
+OpenInterfaceWhenActive="Fosgail an eadar-aghaidh nuair a bhios e gnìomhach"

+ 4 - 0
plugins/obs-vst/data/locale/gl-ES.ini

@@ -0,0 +1,4 @@
+OpenPluginInterface="Abrir a interface do engadido"
+ClosePluginInterface="Pechar a interface do engadido"
+VstPlugin="Engadido VST 2.x"
+OpenInterfaceWhenActive="Abrir a interface cando estea activo"

+ 4 - 0
plugins/obs-vst/data/locale/he-IL.ini

@@ -0,0 +1,4 @@
+OpenPluginInterface="פתיחת ממשק התוספים"
+ClosePluginInterface="סגירת ממשק התוספים"
+VstPlugin="תוסף VST 2.x"
+OpenInterfaceWhenActive="פתח ממשק כאשר פעיל"

+ 4 - 0
plugins/obs-vst/data/locale/hi-IN.ini

@@ -0,0 +1,4 @@
+OpenPluginInterface="प्लग-इन इंटरफ़ेस खोलें"
+ClosePluginInterface="प्लग-इन इंटरफ़ेस बंद करें"
+VstPlugin="VST 2.x प्लग-इन"
+OpenInterfaceWhenActive="सक्रिय होने पर इंटरफ़ेस खोलें"

+ 4 - 0
plugins/obs-vst/data/locale/hu-HU.ini

@@ -0,0 +1,4 @@
+OpenPluginInterface="Bővítmény interfész megnyitása"
+ClosePluginInterface="Bővítmény interfész bezárása"
+VstPlugin="VST 2.x Bővítmény"
+OpenInterfaceWhenActive="Interfész megnyitása, amikor aktív"

+ 3 - 0
plugins/obs-vst/data/locale/id-ID.ini

@@ -0,0 +1,3 @@
+OpenPluginInterface="Buka Antarmuka Plug-in"
+ClosePluginInterface="Tutup Antarmuka Plug-in"
+OpenInterfaceWhenActive="Buka antarmuka ketika aktif"

+ 4 - 0
plugins/obs-vst/data/locale/it-IT.ini

@@ -0,0 +1,4 @@
+OpenPluginInterface="Apri l'interfaccia del plugin"
+ClosePluginInterface="Chiudi l'interfaccia del plugin"
+VstPlugin="Plugin VST 2.x"
+OpenInterfaceWhenActive="Apri l'interfaccia quando attivo"

+ 4 - 0
plugins/obs-vst/data/locale/ja-JP.ini

@@ -0,0 +1,4 @@
+OpenPluginInterface="プラグインインターフェイスを開く"
+ClosePluginInterface="プラグインインターフェイスを閉じる"
+VstPlugin="VST 2.x プラグイン"
+OpenInterfaceWhenActive="アクティブな時にインターフェイスを開く"

+ 4 - 0
plugins/obs-vst/data/locale/ka-GE.ini

@@ -0,0 +1,4 @@
+OpenPluginInterface="მოდულის სამართავის გახსნა"
+ClosePluginInterface="მოდულის სამართავის დახურვა"
+VstPlugin="VST 2.x მოდული"
+OpenInterfaceWhenActive="სამართავის გახსნა ამოქმედებისას"

+ 4 - 0
plugins/obs-vst/data/locale/kab-KAB.ini

@@ -0,0 +1,4 @@
+OpenPluginInterface="Ldi agrudem n usiɣzef"
+ClosePluginInterface="Mdel agrudem n usiɣzef"
+VstPlugin="Asiɣzef VST 2.x"
+OpenInterfaceWhenActive="Ldi agrudemp ticki yermed"

+ 4 - 0
plugins/obs-vst/data/locale/kmr-TR.ini

@@ -0,0 +1,4 @@
+OpenPluginInterface="Navrûya pêvekê veke"
+ClosePluginInterface="Navrûya pêvekê bigire"
+VstPlugin="Pêveka VST 2.x"
+OpenInterfaceWhenActive="Dema çalak be navrûyê veke"

+ 4 - 0
plugins/obs-vst/data/locale/ko-KR.ini

@@ -0,0 +1,4 @@
+OpenPluginInterface="플러그인 설정 열기"
+ClosePluginInterface="플러그인 설정 닫기"
+VstPlugin="VST 2.x 플러그인"
+OpenInterfaceWhenActive="활성화 시 인터페이스 열기"

+ 4 - 0
plugins/obs-vst/data/locale/ms-MY.ini

@@ -0,0 +1,4 @@
+OpenPluginInterface="Buka Antara Muka Pemalam"
+ClosePluginInterface="Tutup Antara Muka Pemalam"
+VstPlugin="Pemalam VST 2.x"
+OpenInterfaceWhenActive="Buka antara muka bila aktif"

+ 4 - 0
plugins/obs-vst/data/locale/nb-NO.ini

@@ -0,0 +1,4 @@
+OpenPluginInterface="Åpne utvidelsesgrensesnittet"
+ClosePluginInterface="Lukk utvidelsesgrensesnittet"
+VstPlugin="VST 2.x-utvidelse"
+OpenInterfaceWhenActive="Åpne grensesnittet når den er aktiv"

+ 4 - 0
plugins/obs-vst/data/locale/nl-NL.ini

@@ -0,0 +1,4 @@
+OpenPluginInterface="Open Plugin Interface"
+ClosePluginInterface="Sluit Plugin Interface"
+VstPlugin="VST 2.x Plugin"
+OpenInterfaceWhenActive="Open interface indien actief"

+ 4 - 0
plugins/obs-vst/data/locale/pl-PL.ini

@@ -0,0 +1,4 @@
+OpenPluginInterface="Otwórz panel wtyczki"
+ClosePluginInterface="Zamknij panel wtyczki"
+VstPlugin="Wtyczka VST 2.x"
+OpenInterfaceWhenActive="Otwórz panel, gdy wtyczka aktywna"

+ 4 - 0
plugins/obs-vst/data/locale/pt-BR.ini

@@ -0,0 +1,4 @@
+OpenPluginInterface="Abrir Interface do Plugin"
+ClosePluginInterface="Fechar Interface do Plugin"
+VstPlugin="Plugin VST 2.x"
+OpenInterfaceWhenActive="Abrir interface quando ativo"

+ 4 - 0
plugins/obs-vst/data/locale/pt-PT.ini

@@ -0,0 +1,4 @@
+OpenPluginInterface="Abrir interface da extensão"
+ClosePluginInterface="Fechar interface da extensão"
+VstPlugin="Extensão VST 2.x"
+OpenInterfaceWhenActive="Abrir interface quando ativo"

+ 4 - 0
plugins/obs-vst/data/locale/ro-RO.ini

@@ -0,0 +1,4 @@
+OpenPluginInterface="Deschide interfața pluginului"
+ClosePluginInterface="Închide interfața pluginului"
+VstPlugin="Plug-in VST 2.x"
+OpenInterfaceWhenActive="Deschide interfața pluginului când este activ"

+ 4 - 0
plugins/obs-vst/data/locale/ru-RU.ini

@@ -0,0 +1,4 @@
+OpenPluginInterface="Открыть интерфейс плагина"
+ClosePluginInterface="Закрыть интерфейс плагина"
+VstPlugin="Плагин VST 2.x"
+OpenInterfaceWhenActive="Открыть интерфейс при активации"

+ 3 - 0
plugins/obs-vst/data/locale/sk-SK.ini

@@ -0,0 +1,3 @@
+OpenPluginInterface="Otvoriť rozhranie Plug-inu"
+ClosePluginInterface="Zavrieť rozhranie Plug-inu"
+OpenInterfaceWhenActive="Otvoriť rozhranie, keď je aktívny"

+ 4 - 0
plugins/obs-vst/data/locale/sl-SI.ini

@@ -0,0 +1,4 @@
+OpenPluginInterface="Odpri vmesnik vtičnikov"
+ClosePluginInterface="Zapri vmesnik vtičnikov"
+VstPlugin="Vtičnik VST 2.x"
+OpenInterfaceWhenActive="Odpri vmesnik, ko je dejaven"

+ 4 - 0
plugins/obs-vst/data/locale/sr-CS.ini

@@ -0,0 +1,4 @@
+OpenPluginInterface="Prikaži interfejs za priključak"
+ClosePluginInterface="Zatvori interfejs za priključak"
+VstPlugin="VST 2.x priključak"
+OpenInterfaceWhenActive="Prikaži interfejs kada je priključak aktivan"

+ 4 - 0
plugins/obs-vst/data/locale/sr-SP.ini

@@ -0,0 +1,4 @@
+OpenPluginInterface="Прикажи интерфејс за прикључак"
+ClosePluginInterface="Затвори интерфејс за прикључак"
+VstPlugin="VST 2.x прикључак"
+OpenInterfaceWhenActive="Прикажи интерфејс када је прикључак активан"

+ 4 - 0
plugins/obs-vst/data/locale/sv-SE.ini

@@ -0,0 +1,4 @@
+OpenPluginInterface="Öppna tilläggets gränssnitt"
+ClosePluginInterface="Stäng tilläggets gränssnitt"
+VstPlugin="VST 2.x-tillägg"
+OpenInterfaceWhenActive="Öppna gränssnitt vid aktiv"

+ 4 - 0
plugins/obs-vst/data/locale/szl-PL.ini

@@ -0,0 +1,4 @@
+OpenPluginInterface="Ôtwōrz panel pluginu"
+ClosePluginInterface="Zawrzij panel pluginu"
+VstPlugin="Plugin VST 2.x"
+OpenInterfaceWhenActive="Ôtwōrz panel, kej plugin je aktywny"

+ 4 - 0
plugins/obs-vst/data/locale/tl-PH.ini

@@ -0,0 +1,4 @@
+OpenPluginInterface="I-open ang Plug-in na Interface"
+ClosePluginInterface="I-close ang Plug-in na Interface"
+VstPlugin="VST 2.x na Plug-in"
+OpenInterfaceWhenActive="I-open ang interface kung itoy aktibo"

+ 4 - 0
plugins/obs-vst/data/locale/tr-TR.ini

@@ -0,0 +1,4 @@
+OpenPluginInterface="Eklenti Arabirimi Aç"
+ClosePluginInterface="Eklenti Arabirimi Kapa"
+VstPlugin="VST 2.x Eklentisi"
+OpenInterfaceWhenActive="Etkinken Arabirimi Aç"

+ 4 - 0
plugins/obs-vst/data/locale/uk-UA.ini

@@ -0,0 +1,4 @@
+OpenPluginInterface="Відкрити інтерфейс плагіна"
+ClosePluginInterface="Закрити інтерфейс плагіна"
+VstPlugin="Плагін VST 2.x"
+OpenInterfaceWhenActive="Щоразу відкривати інтерфейс плагіна"

+ 4 - 0
plugins/obs-vst/data/locale/vi-VN.ini

@@ -0,0 +1,4 @@
+OpenPluginInterface="Mở giao diện bổ trợ"
+ClosePluginInterface="Đóng giao diện bổ trợ"
+VstPlugin="Bổ trợ VST 2.x"
+OpenInterfaceWhenActive="Mở giao diện khi hoạt động"

+ 4 - 0
plugins/obs-vst/data/locale/zh-CN.ini

@@ -0,0 +1,4 @@
+OpenPluginInterface="打开插件界面"
+ClosePluginInterface="关闭插件界面"
+VstPlugin="VST 2.x 插件"
+OpenInterfaceWhenActive="活跃时打开界面"

+ 4 - 0
plugins/obs-vst/data/locale/zh-TW.ini

@@ -0,0 +1,4 @@
+OpenPluginInterface="開啟外掛程式界面"
+ClosePluginInterface="關閉外掛程式介面"
+VstPlugin="VST 2.x 外掛程式"
+OpenInterfaceWhenActive="啟動時開啟界面"

+ 63 - 0
plugins/obs-vst/headers/EditorWidget.h

@@ -0,0 +1,63 @@
+/*****************************************************************************
+Copyright (C) 2016-2017 by Colin Edwards.
+Additional Code Copyright (C) 2016-2017 by c3r1c3 <[email protected]>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*****************************************************************************/
+
+#ifndef OBS_STUDIO_EDITORDIALOG_H
+#define OBS_STUDIO_EDITORDIALOG_H
+
+#include <QWidget>
+
+#if defined(_WIN32)
+#include <QWindow>
+#include <Windows.h>
+#elif defined(__linux__)
+#include <QWindow>
+#include <xcb/xcb.h>
+#endif
+
+#include "aeffectx.h"
+#include "VSTPlugin.h"
+
+class VSTPlugin;
+
+class VstRect {
+
+public:
+	short top;
+	short left;
+	short bottom;
+	short right;
+};
+
+class EditorWidget : public QWidget {
+
+	VSTPlugin *plugin;
+
+#if defined(__APPLE__)
+	QWidget *cocoaViewContainer = NULL;
+#elif defined(_WIN32)
+	HWND windowHandle = NULL;
+#endif
+
+public:
+	EditorWidget(QWidget *parent, VSTPlugin *plugin);
+	void buildEffectContainer(AEffect *effect);
+	void closeEvent(QCloseEvent *event) override;
+	void handleResizeRequest(int width, int height);
+};
+
+#endif // OBS_STUDIO_EDITORDIALOG_H

+ 110 - 0
plugins/obs-vst/headers/VSTPlugin.h

@@ -0,0 +1,110 @@
+/*****************************************************************************
+Copyright (C) 2016-2017 by Colin Edwards.
+Additional Code Copyright (C) 2016-2017 by c3r1c3 <[email protected]>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*****************************************************************************/
+
+#ifndef OBS_STUDIO_VSTPLUGIN_H
+#define OBS_STUDIO_VSTPLUGIN_H
+
+#define BLOCK_SIZE 512
+
+#include <mutex>
+#include <atomic>
+#include <string>
+#include <QDirIterator>
+#include <obs-module.h>
+#include "aeffectx.h"
+#include "vst-plugin-callbacks.hpp"
+#include "EditorWidget.h"
+
+#ifdef __APPLE__
+#include <CoreFoundation/CoreFoundation.h>
+#endif
+
+class EditorWidget;
+
+class VSTPlugin : public QObject {
+	Q_OBJECT
+
+	/* Because effect is always changed in UI thread, so lockEffect is only necessary for these situations:
+	1. access effect object outside of UI thread;
+	2. close/delete effect object any where. */
+	std::recursive_mutex lockEffect;
+	AEffect *effect = nullptr;
+	obs_source_t *sourceContext;
+	std::string pluginPath;
+
+	float **inputs = nullptr;
+	float **outputs = nullptr;
+	float **channelrefs = nullptr;
+	size_t numChannels = 0;
+	void createChannelBuffers(size_t count);
+	void cleanupChannelBuffers();
+
+	EditorWidget *editorWidget = nullptr;
+	bool editorOpened = false;
+
+	AEffect *loadEffect();
+
+	std::atomic_bool effectReady = false;
+
+	std::string sourceName;
+	std::string filterName;
+	char effectName[64];
+	// Remove below... or comment out
+	char vendorString[64];
+
+	VstTimeInfo mTimeInfo;
+
+#ifdef __APPLE__
+	CFBundleRef bundle = NULL;
+#elif WIN32
+	HINSTANCE dllHandle = nullptr;
+#elif __linux__
+	void *soHandle = nullptr;
+#endif
+
+	void unloadLibrary();
+
+	static intptr_t hostCallback_static(AEffect *effect, int32_t opcode,
+					    int32_t index, intptr_t value,
+					    void *ptr, float opt);
+	VstTimeInfo *GetTimeInfo();
+	float GetSampleRate();
+
+public:
+	VSTPlugin(obs_source_t *sourceContext);
+	~VSTPlugin();
+	void loadEffectFromPath(std::string path);
+	void unloadEffect();
+	std::string getEffectPath();
+	std::string getChunk();
+	void setChunk(std::string data);
+	void setProgram(const int programNumber);
+	int getProgram();
+	void getSourceNames();
+	obs_audio_data *process(struct obs_audio_data *audio);
+	bool openInterfaceWhenActive = false;
+
+	bool isEditorOpen();
+	void onEditorClosed();
+
+public slots:
+	void openEditor();
+	void closeEditor();
+};
+
+#endif // OBS_STUDIO_VSTPLUGIN_H

+ 21 - 0
plugins/obs-vst/headers/vst-plugin-callbacks.hpp

@@ -0,0 +1,21 @@
+/*****************************************************************************
+Copyright (C) 2016-2017 by Colin Edwards.
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*****************************************************************************/
+#pragma once
+
+#include "aeffectx.h"
+
+typedef AEffect *(*vstPluginMain)(audioMasterCallback audioMaster);

+ 37 - 0
plugins/obs-vst/linux/EditorWidget-linux.cpp

@@ -0,0 +1,37 @@
+/*****************************************************************************
+Copyright (C) 2016-2017 by Colin Edwards.
+Additional Code Copyright (C) 2016-2017 by c3r1c3 <[email protected]>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*****************************************************************************/
+
+#include "../headers/EditorWidget.h"
+
+void EditorWidget::buildEffectContainer(AEffect *effect)
+{
+	WId id = winId();
+	effect->dispatcher(effect, effEditOpen, 0, 0, (void *)id, 0);
+	VstRect *vstRect = nullptr;
+	effect->dispatcher(effect, effEditGetRect, 0, 0, &vstRect, 0);
+
+	if (vstRect) {
+		setFixedSize(vstRect->right - vstRect->left,
+			     vstRect->bottom - vstRect->top);
+	}
+}
+
+void EditorWidget::handleResizeRequest(int width, int height)
+{
+	setFixedSize(width, height);
+}

+ 65 - 0
plugins/obs-vst/linux/VSTPlugin-linux.cpp

@@ -0,0 +1,65 @@
+/*****************************************************************************
+Copyright (C) 2016-2017 by Colin Edwards.
+Additional Code Copyright (C) 2016-2017 by c3r1c3 <[email protected]>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*****************************************************************************/
+#include "../headers/VSTPlugin.h"
+
+#include <util/platform.h>
+
+AEffect *VSTPlugin::loadEffect()
+{
+	AEffect *plugin = nullptr;
+
+	soHandle = os_dlopen(pluginPath.c_str());
+	if (soHandle == nullptr) {
+		blog(LOG_WARNING,
+		     "Failed trying to load VST from '%s',"
+		     "error %d\n",
+		     pluginPath.c_str(), errno);
+		return nullptr;
+	}
+
+	vstPluginMain mainEntryPoint;
+
+	mainEntryPoint = (vstPluginMain)os_dlsym(soHandle, "VSTPluginMain");
+
+	if (mainEntryPoint == nullptr) {
+		mainEntryPoint =
+			(vstPluginMain)os_dlsym(soHandle, "VstPluginMain()");
+	}
+
+	if (mainEntryPoint == nullptr) {
+		mainEntryPoint = (vstPluginMain)os_dlsym(soHandle, "main");
+	}
+
+	if (mainEntryPoint == nullptr) {
+		blog(LOG_WARNING, "Couldn't get a pointer to plug-in's main()");
+		return nullptr;
+	}
+
+	// Instantiate the plug-in
+	plugin = mainEntryPoint(hostCallback_static);
+	plugin->user = this;
+	return plugin;
+}
+
+void VSTPlugin::unloadLibrary()
+{
+	if (soHandle) {
+		os_dlclose(soHandle);
+		soHandle = nullptr;
+	}
+}

+ 62 - 0
plugins/obs-vst/mac/EditorWidget-osx.mm

@@ -0,0 +1,62 @@
+/*****************************************************************************
+Copyright (C) 2016-2017 by Colin Edwards.
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*****************************************************************************/
+#import "../headers/EditorWidget.h"
+#import <Cocoa/Cocoa.h>
+#include <QLayout>
+#include <QWindow>
+
+#import "../headers/VSTPlugin.h"
+
+void EditorWidget::buildEffectContainer(AEffect *effect)
+{
+	NSView *view =
+		[[NSView alloc] initWithFrame:NSMakeRect(0, 0, 300, 300)];
+	cocoaViewContainer =
+		QWidget::createWindowContainer(QWindow::fromWinId(WId(view)));
+	cocoaViewContainer->move(0, 0);
+	cocoaViewContainer->resize(300, 300);
+	cocoaViewContainer->show();
+
+	QGridLayout *hblParams = new QGridLayout();
+	hblParams->setContentsMargins(0, 0, 0, 0);
+	hblParams->setSpacing(0);
+	hblParams->addWidget(cocoaViewContainer);
+
+	VstRect *vstRect = nullptr;
+	effect->dispatcher(effect, effEditGetRect, 0, 0, &vstRect, 0);
+	if (vstRect) {
+		NSRect frame = NSMakeRect(vstRect->left, vstRect->top,
+					  vstRect->right, vstRect->bottom);
+
+		[view setFrame:frame];
+
+		cocoaViewContainer->resize(vstRect->right - vstRect->left,
+					   vstRect->bottom - vstRect->top);
+
+		setFixedSize(vstRect->right - vstRect->left,
+			     vstRect->bottom - vstRect->top);
+	}
+
+	effect->dispatcher(effect, effEditOpen, 0, 0, view, 0);
+
+	setLayout(hblParams);
+}
+
+void EditorWidget::handleResizeRequest(int width, int height)
+{
+	setFixedSize(width, height);
+}

+ 89 - 0
plugins/obs-vst/mac/VSTPlugin-osx.mm

@@ -0,0 +1,89 @@
+/*****************************************************************************
+Copyright (C) 2016-2017 by Colin Edwards.
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*****************************************************************************/
+
+#include "../headers/VSTPlugin.h"
+
+AEffect *VSTPlugin::loadEffect()
+{
+	AEffect *newEffect = NULL;
+
+	// Create a path to the bundle
+	CFStringRef pluginPathStringRef = CFStringCreateWithCString(
+		NULL, pluginPath.c_str(), kCFStringEncodingUTF8);
+	CFURLRef bundleUrl = CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
+							   pluginPathStringRef,
+							   kCFURLPOSIXPathStyle,
+							   true);
+
+	if (bundleUrl == NULL) {
+		blog(LOG_WARNING,
+		     "Couldn't make URL reference for VST plug-in");
+		return NULL;
+	}
+
+	// Open the bundle
+	bundle = CFBundleCreate(kCFAllocatorDefault, bundleUrl);
+	if (bundle == NULL) {
+		blog(LOG_WARNING, "Couldn't create VST bundle reference.");
+		CFRelease(pluginPathStringRef);
+		CFRelease(bundleUrl);
+		return NULL;
+	}
+
+	vstPluginMain mainEntryPoint = NULL;
+	mainEntryPoint = (vstPluginMain)CFBundleGetFunctionPointerForName(
+		bundle, CFSTR("VSTPluginMain"));
+
+	// VST plugins previous to the 2.4 SDK used main_macho for the
+	// entry point name.
+	if (mainEntryPoint == NULL) {
+		mainEntryPoint =
+			(vstPluginMain)CFBundleGetFunctionPointerForName(
+				bundle, CFSTR("main_macho"));
+	}
+
+	if (mainEntryPoint == NULL) {
+		blog(LOG_WARNING, "Couldn't get a pointer to plug-in's main()");
+		CFBundleUnloadExecutable(bundle);
+		CFRelease(bundle);
+		return NULL;
+	}
+
+	newEffect = mainEntryPoint(hostCallback_static);
+	if (newEffect == NULL) {
+		blog(LOG_WARNING, "VST Plug-in's main() returns null.");
+		CFBundleUnloadExecutable(bundle);
+		CFRelease(bundle);
+		return NULL;
+	}
+
+	newEffect->user = this;
+
+	// Clean up
+	CFRelease(pluginPathStringRef);
+	CFRelease(bundleUrl);
+
+	return newEffect;
+}
+
+void VSTPlugin::unloadLibrary()
+{
+	if (bundle) {
+		CFBundleUnloadExecutable(bundle);
+		CFRelease(bundle);
+	}
+}

+ 332 - 0
plugins/obs-vst/obs-vst.cpp

@@ -0,0 +1,332 @@
+/*****************************************************************************
+Copyright (C) 2016-2017 by Colin Edwards.
+Additional Code Copyright (C) 2016-2017 by c3r1c3 <[email protected]>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*****************************************************************************/
+
+#include "headers/VSTPlugin.h"
+#include <QCryptographicHash>
+
+#define OPEN_VST_SETTINGS "open_vst_settings"
+#define CLOSE_VST_SETTINGS "close_vst_settings"
+#define OPEN_WHEN_ACTIVE_VST_SETTINGS "open_when_active_vst_settings"
+
+#define PLUG_IN_NAME obs_module_text("VstPlugin")
+#define OPEN_VST_TEXT obs_module_text("OpenPluginInterface")
+#define CLOSE_VST_TEXT obs_module_text("ClosePluginInterface")
+#define OPEN_WHEN_ACTIVE_VST_TEXT obs_module_text("OpenInterfaceWhenActive")
+
+OBS_DECLARE_MODULE()
+OBS_MODULE_USE_DEFAULT_LOCALE("obs-vst", "en-US")
+MODULE_EXPORT const char *obs_module_description(void)
+{
+	return "VST 2.x Plug-in filter";
+}
+
+static bool open_editor_button_clicked(obs_properties_t *props,
+				       obs_property_t *property, void *data)
+{
+	VSTPlugin *vstPlugin = (VSTPlugin *)data;
+
+	QMetaObject::invokeMethod(vstPlugin, "openEditor");
+
+	obs_property_set_visible(obs_properties_get(props, OPEN_VST_SETTINGS),
+				 false);
+	obs_property_set_visible(obs_properties_get(props, CLOSE_VST_SETTINGS),
+				 true);
+
+	UNUSED_PARAMETER(props);
+	UNUSED_PARAMETER(property);
+	UNUSED_PARAMETER(data);
+
+	return true;
+}
+
+static bool close_editor_button_clicked(obs_properties_t *props,
+					obs_property_t *property, void *data)
+{
+	VSTPlugin *vstPlugin = (VSTPlugin *)data;
+
+	QMetaObject::invokeMethod(vstPlugin, "closeEditor");
+
+	obs_property_set_visible(obs_properties_get(props, OPEN_VST_SETTINGS),
+				 true);
+	obs_property_set_visible(obs_properties_get(props, CLOSE_VST_SETTINGS),
+				 false);
+
+	UNUSED_PARAMETER(property);
+
+	return true;
+}
+
+std::string getFileMD5(const char *file)
+{
+	QFile f(file);
+	if (f.open(QFile::ReadOnly)) {
+		QCryptographicHash hash(QCryptographicHash::Md5);
+		if (hash.addData(&f))
+			return std::string(hash.result().toHex());
+	}
+
+	return std::string();
+}
+
+static const char *vst_name(void *unused)
+{
+	UNUSED_PARAMETER(unused);
+	return PLUG_IN_NAME;
+}
+
+static void vst_destroy(void *data)
+{
+	VSTPlugin *vstPlugin = (VSTPlugin *)data;
+	QMetaObject::invokeMethod(vstPlugin, "closeEditor");
+	vstPlugin->deleteLater();
+}
+
+static void vst_update(void *data, obs_data_t *settings)
+{
+	VSTPlugin *vstPlugin = (VSTPlugin *)data;
+
+	vstPlugin->openInterfaceWhenActive =
+		obs_data_get_bool(settings, OPEN_WHEN_ACTIVE_VST_SETTINGS);
+
+	const char *path = obs_data_get_string(settings, "plugin_path");
+
+	if (strcmp(path, "") == 0) {
+		vstPlugin->unloadEffect();
+		return;
+	}
+	vstPlugin->loadEffectFromPath(std::string(path));
+
+	std::string hash = getFileMD5(path);
+	const char *chunkHash = obs_data_get_string(settings, "chunk_hash");
+	const char *chunkData = obs_data_get_string(settings, "chunk_data");
+
+	bool chunkHashesMatch = chunkHash && strlen(chunkHash) > 0 &&
+				hash.compare(chunkHash) == 0;
+	if (chunkData && strlen(chunkData) > 0 &&
+	    (chunkHashesMatch || !chunkHash || strlen(chunkHash) == 0)) {
+		vstPlugin->setChunk(std::string(chunkData));
+	}
+}
+
+static void *vst_create(obs_data_t *settings, obs_source_t *filter)
+{
+	VSTPlugin *vstPlugin = new VSTPlugin(filter);
+	vst_update(vstPlugin, settings);
+
+	return vstPlugin;
+}
+
+static void vst_save(void *data, obs_data_t *settings)
+{
+	VSTPlugin *vstPlugin = (VSTPlugin *)data;
+	obs_data_set_string(settings, "chunk_data",
+			    vstPlugin->getChunk().c_str());
+	obs_data_set_string(
+		settings, "chunk_hash",
+		getFileMD5(vstPlugin->getEffectPath().c_str()).c_str());
+}
+
+static struct obs_audio_data *vst_filter_audio(void *data,
+					       struct obs_audio_data *audio)
+{
+	VSTPlugin *vstPlugin = (VSTPlugin *)data;
+	vstPlugin->process(audio);
+
+	/*
+	 * OBS can only guarantee getting the filter source's parent and own name
+	 * in this call, so we grab it and return the results for processing
+	 * by the EditorWidget.
+	 */
+	vstPlugin->getSourceNames();
+
+	return audio;
+}
+
+static void fill_out_plugins(obs_property_t *list)
+{
+	QStringList dir_list;
+
+#ifdef __APPLE__
+	dir_list << "/Library/Audio/Plug-Ins/VST/"
+		 << "~/Library/Audio/Plug-ins/VST/";
+#elif WIN32
+#ifndef _WIN64
+	HANDLE hProcess = GetCurrentProcess();
+
+	BOOL isWow64;
+	IsWow64Process(hProcess, &isWow64);
+
+	if (!isWow64) {
+#endif
+		dir_list << qEnvironmentVariable("ProgramFiles") +
+				    "/Steinberg/VstPlugins/"
+			 << qEnvironmentVariable("CommonProgramFiles") +
+				    "/Steinberg/Shared Components/"
+			 << qEnvironmentVariable("CommonProgramFiles") + "/VST2"
+			 << qEnvironmentVariable("CommonProgramFiles") +
+				    "/Steinberg/VST2"
+			 << qEnvironmentVariable("CommonProgramFiles") +
+				    "/VSTPlugins/"
+			 << qEnvironmentVariable("ProgramFiles") +
+				    "/VSTPlugins/";
+#ifndef _WIN64
+	} else {
+		dir_list << qEnvironmentVariable("ProgramFiles(x86)") +
+				    "/Steinberg/VstPlugins/"
+			 << qEnvironmentVariable("CommonProgramFiles(x86)") +
+				    "/Steinberg/Shared Components/"
+			 << qEnvironmentVariable("CommonProgramFiles(x86)") +
+				    "/VST2"
+			 << qEnvironmentVariable("CommonProgramFiles(x86)") +
+				    "/VSTPlugins/"
+			 << qEnvironmentVariable("ProgramFiles(x86)") +
+				    "/VSTPlugins/";
+	}
+#endif
+#elif __linux__
+	// If the user has set the VST_PATH environmental
+	// variable, then use it. Else default to a list
+	// of common locations.
+	QString vstPathEnv(getenv("VST_PATH"));
+	if (!vstPathEnv.isNull()) {
+		dir_list.append(vstPathEnv.split(":"));
+	} else {
+		QString home(getenv("HOME"));
+		// Choose the most common locations
+		// clang-format off
+		dir_list << "/usr/lib/vst/"
+		         << "/usr/lib/lxvst/"
+		         << "/usr/lib/linux_vst/"
+		         << "/usr/lib64/vst/"
+		         << "/usr/lib64/lxvst/"
+		         << "/usr/lib64/linux_vst/"
+		         << "/usr/local/lib/vst/"
+		         << "/usr/local/lib/lxvst/"
+		         << "/usr/local/lib/linux_vst/"
+		         << "/usr/local/lib64/vst/"
+		         << "/usr/local/lib64/lxvst/"
+		         << "/usr/local/lib64/linux_vst/"
+		         << home + "/.vst/"
+		         << home + "/.lxvst/";
+		// clang-format on
+	}
+#endif
+
+	QStringList filters;
+
+#ifdef __APPLE__
+	filters << "*.vst";
+#elif WIN32
+	filters << "*.dll";
+#elif __linux__
+	filters << "*.so"
+		<< "*.o";
+#endif
+
+	QStringList vst_list;
+
+	// Read all plugins into a list...
+	for (int a = 0; a < dir_list.size(); ++a) {
+		QDir search_dir(dir_list[a]);
+		search_dir.setNameFilters(filters);
+		QDirIterator it(search_dir, QDirIterator::Subdirectories);
+		while (it.hasNext()) {
+			QString path = it.next();
+			QString name = it.fileName();
+
+#ifdef __APPLE__
+			name.remove(".vst", Qt::CaseInsensitive);
+#elif WIN32
+			name.remove(".dll", Qt::CaseInsensitive);
+#elif __linux__
+			name.remove(".so", Qt::CaseInsensitive);
+			name.remove(".o", Qt::CaseInsensitive);
+#endif
+
+			name.append("=").append(path);
+			vst_list << name;
+		}
+	}
+
+	// Now sort list alphabetically (still case-sensitive though).
+	std::stable_sort(vst_list.begin(), vst_list.end(),
+			 std::less<QString>());
+
+	// Now add said list to the plug-in list of OBS
+	obs_property_list_add_string(list, "{Please select a plug-in}",
+				     nullptr);
+	for (int b = 0; b < vst_list.size(); ++b) {
+		QString vst_sorted = vst_list[b];
+		obs_property_list_add_string(
+			list,
+			vst_sorted.left(vst_sorted.indexOf('='))
+				.toStdString()
+				.c_str(),
+			vst_sorted.mid(vst_sorted.indexOf('=') + 1)
+				.toStdString()
+				.c_str());
+	}
+}
+
+static obs_properties_t *vst_properties(void *data)
+{
+	VSTPlugin *vstPlugin = (VSTPlugin *)data;
+	obs_properties_t *props = obs_properties_create();
+	obs_property_t *list = obs_properties_add_list(props, "plugin_path",
+						       PLUG_IN_NAME,
+						       OBS_COMBO_TYPE_LIST,
+						       OBS_COMBO_FORMAT_STRING);
+
+	fill_out_plugins(list);
+
+	obs_properties_add_button(props, OPEN_VST_SETTINGS, OPEN_VST_TEXT,
+				  open_editor_button_clicked);
+	obs_properties_add_button(props, CLOSE_VST_SETTINGS, CLOSE_VST_TEXT,
+				  close_editor_button_clicked);
+
+	if (vstPlugin->isEditorOpen()) {
+		obs_property_set_visible(
+			obs_properties_get(props, OPEN_VST_SETTINGS), false);
+	} else {
+		obs_property_set_visible(
+			obs_properties_get(props, CLOSE_VST_SETTINGS), false);
+	}
+
+	obs_properties_add_bool(props, OPEN_WHEN_ACTIVE_VST_SETTINGS,
+				OPEN_WHEN_ACTIVE_VST_TEXT);
+
+	return props;
+}
+
+bool obs_module_load(void)
+{
+	struct obs_source_info vst_filter = {};
+	vst_filter.id = "vst_filter";
+	vst_filter.type = OBS_SOURCE_TYPE_FILTER;
+	vst_filter.output_flags = OBS_SOURCE_AUDIO;
+	vst_filter.get_name = vst_name;
+	vst_filter.create = vst_create;
+	vst_filter.destroy = vst_destroy;
+	vst_filter.update = vst_update;
+	vst_filter.filter_audio = vst_filter_audio;
+	vst_filter.get_properties = vst_properties;
+	vst_filter.save = vst_save;
+
+	obs_register_source(&vst_filter);
+	return true;
+}

+ 3 - 0
plugins/obs-vst/vst_header/.clang-format

@@ -0,0 +1,3 @@
+Language: Cpp
+SortIncludes: false
+DisableFormat: true

+ 382 - 0
plugins/obs-vst/vst_header/aeffectx.h

@@ -0,0 +1,382 @@
+/*
+ * aeffectx.h - simple header to allow VeSTige compilation and eventually work
+ *
+ * Copyright (c) 2006 Javier Serrano Polo <jasp00/at/users.sourceforge.net>
+ *
+ * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program (see COPYING); if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdint.h>
+
+#ifndef _AEFFECTX_H
+#define _AEFFECTX_H
+
+#define CCONST(a, b, c, d)( ( ( (int) a ) << 24 ) |      \
+		( ( (int) b ) << 16 ) |    \
+		( ( (int) c ) << 8 ) |     \
+		( ( (int) d ) << 0 ) )
+
+
+const int audioMasterAutomate = 0;
+const int audioMasterVersion = 1;
+const int audioMasterCurrentId = 2;
+const int audioMasterIdle = 3;
+const int audioMasterPinConnected = 4;
+// unsupported? 5
+const int audioMasterWantMidi = 6;
+const int audioMasterGetTime = 7;
+const int audioMasterProcessEvents = 8;
+const int audioMasterSetTime = 9;
+const int audioMasterTempoAt = 10;
+const int audioMasterGetNumAutomatableParameters = 11;
+const int audioMasterGetParameterQuantization = 12;
+const int audioMasterIOChanged = 13;
+const int audioMasterNeedIdle = 14;
+const int audioMasterSizeWindow = 15;
+const int audioMasterGetSampleRate = 16;
+const int audioMasterGetBlockSize = 17;
+const int audioMasterGetInputLatency = 18;
+const int audioMasterGetOutputLatency = 19;
+const int audioMasterGetPreviousPlug = 20;
+const int audioMasterGetNextPlug = 21;
+const int audioMasterWillReplaceOrAccumulate = 22;
+const int audioMasterGetCurrentProcessLevel = 23;
+const int audioMasterGetAutomationState = 24;
+const int audioMasterOfflineStart = 25;
+const int audioMasterOfflineRead = 26;
+const int audioMasterOfflineWrite = 27;
+const int audioMasterOfflineGetCurrentPass = 28;
+const int audioMasterOfflineGetCurrentMetaPass = 29;
+const int audioMasterSetOutputSampleRate = 30;
+// unsupported? 31
+const int audioMasterGetSpeakerArrangement = 31; // deprecated in 2.4?
+const int audioMasterGetVendorString = 32;
+const int audioMasterGetProductString = 33;
+const int audioMasterGetVendorVersion = 34;
+const int audioMasterVendorSpecific = 35;
+const int audioMasterSetIcon = 36;
+const int audioMasterCanDo = 37;
+const int audioMasterGetLanguage = 38;
+const int audioMasterOpenWindow = 39;
+const int audioMasterCloseWindow = 40;
+const int audioMasterGetDirectory = 41;
+const int audioMasterUpdateDisplay = 42;
+const int audioMasterBeginEdit = 43;
+const int audioMasterEndEdit = 44;
+const int audioMasterOpenFileSelector = 45;
+const int audioMasterCloseFileSelector = 46; // currently unused
+const int audioMasterEditFile = 47; // currently unused
+const int audioMasterGetChunkFile = 48; // currently unused
+const int audioMasterGetInputSpeakerArrangement = 49; // currently unused
+
+const int effFlagsHasEditor = 1;
+const int effFlagsCanReplacing = 1 << 4; // very likely
+const int effFlagsProgramChunks = 1 << 5; // from Ardour
+const int effFlagsIsSynth = 1 << 8; // currently unused
+
+const int effOpen = 0;
+const int effClose = 1; // currently unused
+const int effSetProgram = 2; // currently unused
+const int effGetProgram = 3; // currently unused
+// The next one was gleaned from http://www.kvraudio.com/forum/viewtopic.php?p=1905347
+const int effSetProgramName = 4;
+const int effGetProgramName = 5; // currently unused
+// The next two were gleaned from http://www.kvraudio.com/forum/viewtopic.php?p=1905347
+const int effGetParamLabel = 6;
+const int effGetParamDisplay = 7;
+const int effGetParamName = 8; // currently unused
+const int effSetSampleRate = 10;
+const int effSetBlockSize = 11;
+const int effMainsChanged = 12;
+const int effEditGetRect = 13;
+const int effEditOpen = 14;
+const int effEditClose = 15;
+const int effEditIdle = 19;
+const int effEditTop = 20;
+const int effIdentify = 22; // from http://www.asseca.org/vst-24-specs/efIdentify.html
+const int effGetChunk = 23; // from Ardour
+const int effSetChunk = 24; // from Ardour
+const int effProcessEvents = 25;
+// The next one was gleaned from http://www.asseca.org/vst-24-specs/efCanBeAutomated.html
+const int effCanBeAutomated = 26;
+// The next one was gleaned from http://www.kvraudio.com/forum/viewtopic.php?p=1905347
+const int effGetProgramNameIndexed = 29;
+// The next one was gleaned from http://www.asseca.org/vst-24-specs/efGetPlugCategory.html
+const int effGetPlugCategory = 35;
+const int effGetEffectName = 45;
+const int effGetParameterProperties = 56; // missing
+const int effGetVendorString = 47;
+const int effGetProductString = 48;
+const int effGetVendorVersion = 49;
+const int effCanDo = 51; // currently unused
+// The next one was gleaned from http://www.asseca.org/vst-24-specs/efIdle.html
+const int effIdle = 53;
+const int effGetVstVersion = 58; // currently unused
+// The next one was gleaned from http://www.asseca.org/vst-24-specs/efBeginSetProgram.html
+const int effBeginSetProgram = 67;
+// The next one was gleaned from http://www.asseca.org/vst-24-specs/efEndSetProgram.html
+const int effEndSetProgram = 68;
+// The next one was gleaned from http://www.asseca.org/vst-24-specs/efShellGetNextPlugin.html
+const int effShellGetNextPlugin = 70;
+// The next one was gleaned from http://www.asseca.org/vst-24-specs/efBeginLoadBank.html
+const int effBeginLoadBank = 75;
+// The next one was gleaned from http://www.asseca.org/vst-24-specs/efBeginLoadProgram.html
+const int  effBeginLoadProgram = 76;
+
+// The next two were gleaned from http://www.kvraudio.com/forum/printview.php?t=143587&start=0
+const int effStartProcess = 71;
+const int effStopProcess = 72;
+
+const int kEffectMagic = CCONST( 'V', 's', 't', 'P' );
+const int kVstLangEnglish = 1;
+const int kVstMidiType = 1;
+
+const int kVstNanosValid = 1 << 8;
+const int kVstPpqPosValid = 1 << 9;
+const int kVstTempoValid = 1 << 10;
+const int kVstBarsValid = 1 << 11;
+const int kVstCyclePosValid = 1 << 12;
+const int kVstTimeSigValid = 1 << 13;
+// from Ardour
+const int kVstSmpteValid = 1 << 14;
+// from Ardour
+const int kVstClockValid = 1 << 15;
+
+const int kVstTransportPlaying = 1 << 1;
+const int kVstTransportCycleActive = 1 << 2;
+const int kVstTransportChanged = 1;
+
+
+class RemoteVstPlugin;
+
+
+class VstMidiEvent
+{
+	public:
+		// 00
+		int type;
+		// 04
+		int byteSize;
+		// 08
+		int deltaFrames;
+		// 0c?
+		int flags;
+		// 10?
+		int noteLength;
+		// 14?
+		int noteOffset;
+		// 18
+		char midiData[4];
+		// 1c?
+		char detune;
+		// 1d?
+		char noteOffVelocity;
+		// 1e?
+		char reserved1;
+		// 1f?
+		char reserved2;
+};
+
+
+class VstEvent
+{
+	char dump[sizeof( VstMidiEvent )];
+};
+
+
+class VstEvents
+{
+	public:
+		// 00
+		int numEvents;
+		// 04
+		void *reserved;
+		// 08
+		VstEvent* events[1];
+};
+
+
+// Not finished, neither really used
+class VstParameterProperties
+{
+	public:
+	/*
+		float stepFloat;
+		char label[64];
+		int flags;
+		int minInteger;
+		int maxInteger;
+		int stepInteger;
+		char shortLabel[8];
+		int category;
+		char categoryLabel[24];
+		char empty[128];
+	*/
+
+		float stepFloat;
+		float smallStepFloat;
+		float largeStepFloat;
+		char label[64];
+		unsigned int flags;
+		unsigned int minInteger;
+		unsigned int maxInteger;
+		unsigned int stepInteger;
+		unsigned int largeStepInteger;
+		char shortLabel[8];
+		unsigned short displayIndex;
+		unsigned short category;
+		unsigned short numParametersInCategory;
+		unsigned short reserved;
+		char categoryLabel[24];
+		char future[16];
+};
+
+
+class AEffect
+{
+	public:
+		// Never use virtual functions!!!
+		// 00-03
+		int magic;
+		// dispatcher 04-07
+		intptr_t (* dispatcher)( AEffect * , int , int , intptr_t, void * , float );
+		// process, quite sure 08-0b
+		void (* process)( AEffect * , float * * , float * * , int );
+		// setParameter 0c-0f
+		void (* setParameter)( AEffect * , int , float );
+		// getParameter 10-13
+		float (* getParameter)( AEffect * , int );
+		// programs 14-17
+		int numPrograms;
+		// Params 18-1b
+		int numParams;
+		// Input 1c-1f
+		int numInputs;
+		// Output 20-23
+		int numOutputs;
+		// flags 24-27
+		int flags;
+		// Fill somewhere 28-2b
+		void * ptr1;
+		void * ptr2;
+		int initialDelay;
+		// Zeroes 34-37 38-3b
+		int empty3a;
+		int empty3b;
+		// 1.0f 3c-3f
+		float unkown_float;
+		// An object? pointer 40-43
+		void *ptr3;
+		// Zeroes 44-47
+		void *user;
+		// Id 48-4b
+		int32_t uniqueID;
+		int32_t version;
+		// processReplacing 50-53
+		void (* processReplacing)( AEffect * , float * * , float * * , int );
+};
+
+typedef intptr_t (* audioMasterCallback)( AEffect * , int32_t, int32_t,
+		intptr_t, void * , float );
+
+class VstTimeInfo
+{
+	public:
+	// 00
+	double samplePos;
+	// 08
+	double sampleRate;
+	// 10
+	double nanoSeconds;
+	// 18
+	double ppqPos;
+	// 20?
+	double tempo;
+	// 28
+	double barStartPos;
+	// 30?
+	double cycleStartPos;
+	// 38?
+	double cycleEndPos;
+	// 40?
+	int timeSigNumerator;
+	// 44?
+	int timeSigDenominator;
+	// unconfirmed 48 4c 50
+	char empty3[4 + 4 + 4];
+	// 54
+	int flags;
+};
+
+// from http://www.asseca.org/vst-24-specs/efGetParameterProperties.html
+enum VstParameterFlags
+{
+	// parameter is a switch (on/off)
+	kVstParameterIsSwitch                = 1 << 0,
+
+	// minInteger, maxInteger valid
+	kVstParameterUsesIntegerMinMax       = 1 << 1,
+
+	// stepFloat, smallStepFloat, largeStepFloat valid
+	kVstParameterUsesFloatStep           = 1 << 2,
+
+	// stepInteger, largeStepInteger valid
+	kVstParameterUsesIntStep             = 1 << 3,
+
+	// displayIndex valid
+	kVstParameterSupportsDisplayIndex    = 1 << 4,
+
+	// category, etc. valid
+	kVstParameterSupportsDisplayCategory = 1 << 5,
+
+	// set if parameter value can ramp up/down
+	kVstParameterCanRamp                 = 1 << 6
+};
+
+// from http://www.asseca.org/vst-24-specs/efBeginLoadProgram.html
+struct VstPatchChunkInfo
+{
+	int32_t version;           // Format Version (should be 1)
+	int32_t pluginUniqueID;    // UniqueID of the plug-in
+	int32_t pluginVersion;     // Plug-in Version
+	int32_t numElements;       // Number of Programs (Bank) or
+				   // Parameters (Program)
+	char future[48];           // Reserved for future use
+};
+
+// from http://www.asseca.org/vst-24-specs/efGetPlugCategory.html
+enum VstPlugCategory
+{
+	kPlugCategUnknown = 0,    // 0=Unknown, category not implemented
+	kPlugCategEffect,         // 1=Simple Effect
+	kPlugCategSynth,          // 2=VST Instrument (Synths, samplers,...)
+	kPlugCategAnalysis,       // 3=Scope, Tuner, ...
+	kPlugCategMastering,      // 4=Dynamics, ...
+	kPlugCategSpacializer,    // 5=Panners, ...
+	kPlugCategRoomFx,         // 6=Delays and Reverbs
+	kPlugSurroundFx,          // 7=Dedicated surround processor
+	kPlugCategRestoration,    // 8=Denoiser, ...
+	kPlugCategOfflineProcess, // 9=Offline Process
+	kPlugCategShell,          // 10=Plug-in is container of other
+				  // plug-ins @see effShellGetNextPlugin()
+	kPlugCategGenerator,      // 11=ToneGenerator, ...
+	kPlugCategMaxCount        // 12=Marker to count the categories
+};
+
+#endif

+ 77 - 0
plugins/obs-vst/win/EditorWidget-win.cpp

@@ -0,0 +1,77 @@
+/*****************************************************************************
+Copyright (C) 2016-2017 by Colin Edwards.
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*****************************************************************************/
+
+#include <QGridLayout>
+#include "../headers/EditorWidget.h"
+
+void EditorWidget::buildEffectContainer(AEffect *effect)
+{
+	WNDCLASSEXW wcex{sizeof(wcex)};
+
+	wcex.lpfnWndProc = DefWindowProcW;
+	wcex.hInstance = GetModuleHandleW(nullptr);
+	wcex.lpszClassName = L"Minimal VST host - Guest VST Window Frame";
+	RegisterClassExW(&wcex);
+
+	const auto style = WS_CAPTION | WS_THICKFRAME | WS_OVERLAPPEDWINDOW;
+	windowHandle = CreateWindowW(wcex.lpszClassName, TEXT(""), style, 0, 0,
+				     0, 0, nullptr, nullptr, nullptr, nullptr);
+
+	// set pointer to vst effect for window long
+	LONG_PTR wndPtr = (LONG_PTR)effect;
+	SetWindowLongPtr(windowHandle, -21 /*GWLP_USERDATA*/, wndPtr);
+
+	QWidget *widget = QWidget::createWindowContainer(
+		QWindow::fromWinId((WId)windowHandle), nullptr);
+	widget->move(0, 0);
+	QGridLayout *layout = new QGridLayout();
+	layout->setContentsMargins(0, 0, 0, 0);
+	layout->setSpacing(0);
+	setLayout(layout);
+	layout->addWidget(widget);
+
+	effect->dispatcher(effect, effEditOpen, 0, 0, windowHandle, 0);
+
+	VstRect *vstRect = nullptr;
+	effect->dispatcher(effect, effEditGetRect, 0, 0, &vstRect, 0);
+	if (vstRect) {
+		widget->resize(vstRect->right - vstRect->left,
+			       vstRect->bottom - vstRect->top);
+		resize(vstRect->right - vstRect->left,
+		       vstRect->bottom - vstRect->top);
+	} else {
+		widget->resize(300, 300);
+	}
+}
+
+void EditorWidget::handleResizeRequest(int, int)
+{
+	// Some plugins can't resize automatically (like SPAN by Voxengo),
+	// so we must resize window manually
+
+	// get pointer to vst effect from window long
+	LONG_PTR wndPtr = (LONG_PTR)GetWindowLongPtrW(windowHandle,
+						      -21 /*GWLP_USERDATA*/);
+	AEffect *effect = (AEffect *)(wndPtr);
+	VstRect *rec = nullptr;
+
+	effect->dispatcher(effect, effEditGetRect, 0, 0, &rec, 0);
+
+	if (rec) {
+		resize(rec->right - rec->left, rec->bottom - rec->top);
+	}
+}

+ 91 - 0
plugins/obs-vst/win/VSTPlugin-win.cpp

@@ -0,0 +1,91 @@
+/*****************************************************************************
+Copyright (C) 2016-2017 by Colin Edwards.
+Additional Code Copyright (C) 2016-2017 by c3r1c3 <[email protected]>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*****************************************************************************/
+#include "../headers/VSTPlugin.h"
+#include "../headers/vst-plugin-callbacks.hpp"
+
+#include <util/platform.h>
+#include <windows.h>
+
+AEffect *VSTPlugin::loadEffect()
+{
+	AEffect *plugin = nullptr;
+
+	wchar_t *wpath;
+	os_utf8_to_wcs_ptr(pluginPath.c_str(), 0, &wpath);
+	dllHandle = LoadLibraryW(wpath);
+	bfree(wpath);
+	if (dllHandle == nullptr) {
+
+		DWORD errorCode = GetLastError();
+
+		// Display the error message and exit the process
+		if (errorCode == ERROR_BAD_EXE_FORMAT) {
+			blog(LOG_WARNING, "Could not open library, "
+					  "wrong architecture.");
+		} else {
+			blog(LOG_WARNING,
+			     "Failed trying to load VST from '%s'"
+			     ", error %d\n",
+			     pluginPath.c_str(), GetLastError());
+		}
+		return nullptr;
+	}
+
+	vstPluginMain mainEntryPoint =
+		(vstPluginMain)GetProcAddress(dllHandle, "VSTPluginMain");
+
+	if (mainEntryPoint == nullptr) {
+		mainEntryPoint = (vstPluginMain)GetProcAddress(
+			dllHandle, "VstPluginMain()");
+	}
+
+	if (mainEntryPoint == nullptr) {
+		mainEntryPoint =
+			(vstPluginMain)GetProcAddress(dllHandle, "main");
+	}
+
+	if (mainEntryPoint == nullptr) {
+		blog(LOG_WARNING, "Couldn't get a pointer to plug-in's main()");
+		return nullptr;
+	}
+
+	// Instantiate the plug-in
+	try {
+		plugin = mainEntryPoint(hostCallback_static);
+	} catch (...) {
+		blog(LOG_WARNING, "VST plugin initialization failed");
+		return nullptr;
+	}
+
+	if (plugin == nullptr) {
+		blog(LOG_WARNING, "Couldn't create instance for '%s'",
+		     pluginPath.c_str());
+		return nullptr;
+	}
+
+	plugin->user = this;
+	return plugin;
+}
+
+void VSTPlugin::unloadLibrary()
+{
+	if (dllHandle) {
+		FreeLibrary(dllHandle);
+		dllHandle = nullptr;
+	}
+}