mirror of
https://github.com/obsproject/obs-studio.git
synced 2026-05-11 16:59:43 -04:00
v1 of obs_properties_add_button will be deprecated soon. Also fixes UNUSED_PARAMETER calls in the callbacks for parameters that are not actually unused, and moves the other ones to the top (as in most of the rest of the project).
348 lines
11 KiB
C++
348 lines
11 KiB
C++
/*****************************************************************************
|
|
Copyright (C) 2016-2017 by Colin Edwards.
|
|
Additional Code Copyright (C) 2016-2017 by c3r1c3 <c3r1c3@nevermindonline.com>
|
|
|
|
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)
|
|
{
|
|
UNUSED_PARAMETER(property);
|
|
|
|
VSTPlugin *vstPlugin = (VSTPlugin *)data;
|
|
|
|
if (vstPlugin && vstPlugin->vstLoaded()) {
|
|
|
|
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);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool close_editor_button_clicked(obs_properties_t *props, obs_property_t *property, void *data)
|
|
{
|
|
UNUSED_PARAMETER(property);
|
|
|
|
VSTPlugin *vstPlugin = (VSTPlugin *)data;
|
|
|
|
if (vstPlugin && vstPlugin->vstLoaded() && vstPlugin->isEditorOpen()) {
|
|
|
|
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);
|
|
}
|
|
|
|
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");
|
|
|
|
#ifdef __linux__
|
|
// Migrate freedesktop.org Flatpak runtime 21.08 VST paths to 22.08.
|
|
if (QFile::exists("/.flatpak-info") && QString(path).startsWith("/app/extensions/Plugins/lxvst")) {
|
|
QString newPath(path);
|
|
newPath.replace("/app/extensions/Plugins/lxvst", "/app/extensions/Plugins/vst");
|
|
obs_data_set_string(settings, "plugin_path", newPath.toStdString().c_str());
|
|
path = obs_data_get_string(settings, "plugin_path");
|
|
}
|
|
#endif
|
|
|
|
if (!*path) {
|
|
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 && *chunkHash && hash.compare(chunkHash) == 0;
|
|
if (chunkData && *chunkData && (chunkHashesMatch || !chunkHash || !*chunkHash)) {
|
|
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 | QDirIterator::FollowSymlinks);
|
|
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 bool vst_changed(void *data, obs_properties_t *props, obs_property_t *list, obs_data_t *settings)
|
|
{
|
|
UNUSED_PARAMETER(settings);
|
|
UNUSED_PARAMETER(list);
|
|
|
|
bool open_settings_vis = true;
|
|
bool close_settings_vis = false;
|
|
if (data) {
|
|
VSTPlugin *vstPlugin = (VSTPlugin *)data;
|
|
if (!vstPlugin->vstLoaded()) {
|
|
close_settings_vis = false;
|
|
open_settings_vis = false;
|
|
} else {
|
|
if (vstPlugin->isEditorOpen()) {
|
|
close_settings_vis = true;
|
|
open_settings_vis = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
obs_property_set_visible(obs_properties_get(props, OPEN_VST_SETTINGS), open_settings_vis);
|
|
obs_property_set_visible(obs_properties_get(props, CLOSE_VST_SETTINGS), close_settings_vis);
|
|
|
|
return true;
|
|
}
|
|
|
|
static obs_properties_t *vst_properties(void *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_button2(props, OPEN_VST_SETTINGS, OPEN_VST_TEXT, open_editor_button_clicked, data);
|
|
obs_properties_add_button2(props, CLOSE_VST_SETTINGS, CLOSE_VST_TEXT, close_editor_button_clicked, data);
|
|
|
|
bool open_settings_vis = true;
|
|
bool close_settings_vis = false;
|
|
if (data) {
|
|
VSTPlugin *vstPlugin = (VSTPlugin *)data;
|
|
if (!vstPlugin->vstLoaded()) {
|
|
close_settings_vis = false;
|
|
open_settings_vis = false;
|
|
} else {
|
|
if (vstPlugin->isEditorOpen()) {
|
|
close_settings_vis = true;
|
|
open_settings_vis = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
obs_property_set_visible(obs_properties_get(props, OPEN_VST_SETTINGS), open_settings_vis);
|
|
obs_property_set_visible(obs_properties_get(props, CLOSE_VST_SETTINGS), close_settings_vis);
|
|
|
|
obs_properties_add_bool(props, OPEN_WHEN_ACTIVE_VST_SETTINGS, OPEN_WHEN_ACTIVE_VST_TEXT);
|
|
|
|
obs_property_set_modified_callback2(list, vst_changed, data);
|
|
|
|
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;
|
|
}
|