mirror of
https://github.com/robbert-vdh/yabridge.git
synced 2026-05-07 03:50:11 +02:00
2be41da9b6
This prevents Kush Audio REDDI from taking down the DAW when the host passes it denormalized audio to process. I've discovered that the issue with this plugin had to do with denormals in the issue linked below, but I didn't realize that we can just enable the FTZ flag for plugins that don't already do so. https://github.com/osxmidi/LinVst/issues/174
159 lines
6.8 KiB
C++
159 lines
6.8 KiB
C++
// yabridge: a Wine VST bridge
|
|
// Copyright (C) 2020-2021 Robbert van der Helm
|
|
//
|
|
// 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 3 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 <https://www.gnu.org/licenses/>.
|
|
|
|
#include "configuration.h"
|
|
|
|
// tomlplusplus recently got some Windows fixes, but they cause compilation
|
|
// errors and we don't need them so we'll just disable them outright
|
|
#define TOML_WINDOWS_COMPAT 0
|
|
|
|
#include <fnmatch.h>
|
|
#include <toml++/toml.h>
|
|
#include <fstream>
|
|
|
|
namespace fs = boost::filesystem;
|
|
|
|
Configuration::Configuration() {}
|
|
|
|
Configuration::Configuration(const fs::path& config_path,
|
|
const fs::path& yabridge_path)
|
|
: Configuration() {
|
|
// Will throw a `toml::parsing_error` if the file cannot be parsed. Better
|
|
// to throw here rather than failing silently since syntax errors would
|
|
// otherwise be impossible to spot. We'll also have to sort all tables by
|
|
// the location in the file since tomlplusplus internally uses ordered maps
|
|
// so otherwise we'll get the tables sorted by key instead.
|
|
toml::table table = toml::parse_file(config_path.string());
|
|
|
|
// I wasn't able to wade through the template soup to come up with a better
|
|
// way to sort this by location, so please feel free to correct this if you
|
|
// know of a better way! The source locations has to be stored inside of the
|
|
// vector itself because the `node.source()` on the copies stored in this
|
|
// vector won't contain the proper location after we've iterated through
|
|
// `table`.
|
|
std::vector<std::tuple<std::string, toml::source_region, toml::table>>
|
|
sorted_tables{};
|
|
for (auto [pattern, node] : table) {
|
|
if (const toml::table* config = node.as_table()) {
|
|
sorted_tables.push_back(
|
|
std::make_tuple(pattern, config->source(), *config));
|
|
}
|
|
}
|
|
std::sort(sorted_tables.begin(), sorted_tables.end(),
|
|
[](const auto& a, const auto& b) {
|
|
const auto& [a_pattern, a_source, a_table] = a;
|
|
const auto& [b_pattern, b_source, b_table] = b;
|
|
|
|
return a_source.begin.line < b_source.begin.line;
|
|
});
|
|
|
|
const fs::path relative_path =
|
|
yabridge_path.lexically_relative(config_path.parent_path());
|
|
for (const auto& [pattern, source, table] : sorted_tables) {
|
|
// First try to match the glob pattern, allow matching an entire
|
|
// directory for ease of use. If none of the patterns in the file match
|
|
// the plugin path then everything will be left at the defaults.
|
|
if (fnmatch(pattern.c_str(), relative_path.c_str(),
|
|
FNM_PATHNAME | FNM_LEADING_DIR) != 0) {
|
|
continue;
|
|
}
|
|
|
|
matched_file = config_path;
|
|
matched_pattern = pattern;
|
|
|
|
// If the table is missing some fields then they will simply be left at
|
|
// their defaults. At this point I'd really wish C++ could do pattern
|
|
// matching.
|
|
for (const auto& [key, value] : table) {
|
|
if (key == "group") {
|
|
if (const auto parsed_value = value.as_string()) {
|
|
group = parsed_value->get();
|
|
} else {
|
|
invalid_options.push_back(key);
|
|
}
|
|
} else if (key == "cache_time_info") {
|
|
if (const auto parsed_value = value.as_boolean()) {
|
|
cache_time_info = parsed_value->get();
|
|
} else {
|
|
invalid_options.push_back(key);
|
|
}
|
|
} else if (key == "editor_double_embed") {
|
|
if (const auto parsed_value = value.as_boolean()) {
|
|
editor_double_embed = parsed_value->get();
|
|
} else {
|
|
invalid_options.push_back(key);
|
|
}
|
|
} else if (key == "editor_force_dnd") {
|
|
if (const auto parsed_value = value.as_boolean()) {
|
|
editor_force_dnd = parsed_value->get();
|
|
} else {
|
|
invalid_options.push_back(key);
|
|
}
|
|
} else if (key == "editor_xembed") {
|
|
if (const auto parsed_value = value.as_boolean()) {
|
|
editor_xembed = parsed_value->get();
|
|
} else {
|
|
invalid_options.push_back(key);
|
|
}
|
|
} else if (key == "force_ftz") {
|
|
if (const auto parsed_value = value.as_boolean()) {
|
|
force_ftz = parsed_value->get();
|
|
} else {
|
|
invalid_options.push_back(key);
|
|
}
|
|
} else if (key == "frame_rate") {
|
|
if (const auto parsed_value = value.as_floating_point()) {
|
|
frame_rate = parsed_value->get();
|
|
} else if (const auto parsed_value = value.as_integer()) {
|
|
// For usability's sake we want to be a bit more lax than a
|
|
// normal TOML file would be and accept both floating point
|
|
// values and integers here
|
|
frame_rate = parsed_value->get();
|
|
} else {
|
|
invalid_options.push_back(key);
|
|
}
|
|
} else if (key == "hide_daw") {
|
|
if (const auto parsed_value = value.as_boolean()) {
|
|
hide_daw = parsed_value->get();
|
|
} else {
|
|
invalid_options.push_back(key);
|
|
}
|
|
} else if (key == "vst3_no_scaling") {
|
|
if (const auto parsed_value = value.as_boolean()) {
|
|
vst3_no_scaling = parsed_value->get();
|
|
} else {
|
|
invalid_options.push_back(key);
|
|
}
|
|
} else if (key == "vst3_prefer_32bit") {
|
|
if (const auto parsed_value = value.as_boolean()) {
|
|
vst3_prefer_32bit = parsed_value->get();
|
|
} else {
|
|
invalid_options.push_back(key);
|
|
}
|
|
} else {
|
|
unknown_options.push_back(key);
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
std::chrono::steady_clock::duration Configuration::event_loop_interval() const {
|
|
return std::chrono::duration_cast<std::chrono::steady_clock::duration>(
|
|
std::chrono::milliseconds(1000) / frame_rate.value_or(60.0));
|
|
}
|