Add compatibility option to force flushing to zero

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
This commit is contained in:
Robbert van der Helm
2021-04-27 23:33:50 +02:00
parent 3aac8e3483
commit 2be41da9b6
8 changed files with 51 additions and 3 deletions
+9
View File
@@ -16,6 +16,15 @@ Versioning](https://semver.org/spec/v2.0.0.html).
that have undesirable or broken DAW-specific behaviour. See the [known
issues](https://github.com/robbert-vdh/yabridge#runtime-dependencies-and-known-issues)
section of the readme for more information on when this may be useful.
- Added another new [compatibility
option](https://github.com/robbert-vdh/yabridge#compatibility-options) to
force flushing denormals to zero during audio processing. Some plugins, such
as _Kush Audio REDDI_, have significantly increased DSP usage when processing
audio that's almost but not quite silent. When this happens, you will notice
that a plugin's DSP usage increases exponentially after playback has stopped
or when the plugin starts processing silence. Enabling this new option will
force your CPU to flush this almost silent audio to silence, potentially
fixing the issue.
### Changed
+8
View File
@@ -296,6 +296,7 @@ plugin._
| `editor_double_embed` | `{true,false}` | Compatibility option for plugins that rely on the absolute screen coordinates of the window they're embedded in. Since the Wine window gets embedded inside of a window provided by your DAW, these coordinates won't match up and the plugin would end up drawing in the wrong location without this option. Currently the only known plugins that require this option are _PSPaudioware_ plugins with expandable GUIs, such as E27. Defaults to `false`. |
| `editor_force_dnd` | `{true,false}` | This option forcefully enables drag-and-drop support in _REAPER_. Because REAPER's FX window supports drag-and-drop itself, dragging a file onto a plugin editor will cause the drop to be intercepted by the FX window. This makes it impossible to drag files onto plugins in REAPER under normal circumstances. Setting this option to `true` will strip drag-and-drop support from the FX window, thus allowing files to be dragged onto the plugin again. Defaults to `false`. |
| `editor_xembed` | `{true,false}` | Use Wine's XEmbed implementation instead of yabridge's normal window embedding method. Some plugins will have redrawing issues when using XEmbed and editor resizing won't always work properly with it, but it could be useful in certain setups. You may need to use [this Wine patch](https://github.com/psycha0s/airwave/blob/master/fix-xembed-wine-windows.patch) if you're getting blank editor windows. Defaults to `false`. |
| `force_ftz` | `{true,false}` | Enable the CPU's FTZ flag while processing audio, flushing denormals to zero. This can fix issues with plugins that start showing exponentially increasing DSP load when playback is stopped or when they start processing silence. Defaults to `false`. |
| `frame_rate` | `<number>` | The rate at which Win32 events are being handled and usually also the refresh rate of a plugin's editor GUI. When using plugin groups all plugins share the same event handling loop, so in those the last loaded plugin will set the refresh rate. Defaults to `60`. |
| `hide_daw` | `{true,false}` | Don't report the name of the actual DAW to the plugin. See the [known issues](#runtime-dependencies-and-known-issues) section for a list of situations where this may be useful. This affects both VST2 and VST3 plugins. Defaults to `false`. |
| `vst3_no_scaling` | `{true,false}` | Disable HiDPI scaling for VST3 plugins. Wine currently does not have proper fractional HiDPI support, so you might have to enable this option if you're using a HiDPI display. In most cases setting the font DPI in `winecfg`'s graphics tab to 192 will cause plugins to scale correctly at 200% size. Defaults to `false`. |
@@ -333,6 +334,9 @@ editor_xembed = true
["Chromaphone 3.so"]
hide_daw = true
["Kush Audio/REDDI.so"]
force_ftz = true
["SWAM Cello 64bit.so"]
cache_time_info = true
@@ -444,6 +448,10 @@ include:
_Bitwig Studio_, text entry will cause the plugin to crash because Chromaphone
uses a different text entry method when it detects Bitwig. You can use the
`hide_daw` [compatibility option](#compatibility-options) to work around this.
- **Kush Audio REDDI** suffers from exponentially increasing DSP usage when the
plugin starts processing silence, halting playback and potentially crashing
the DAW. You can enable the `force_ftz` [compatibility
option](#compatibility-options) for the plugin to work around this bug.
- The VST2 version of **SWAM Cello** has a bug where it asks the host for the
current buffer's time and tempo information for every sample it processes
instead of doing it only once per buffer, resulting in very bad performance.
+6
View File
@@ -108,6 +108,12 @@ Configuration::Configuration(const fs::path& config_path,
} 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();
+10
View File
@@ -126,6 +126,15 @@ class Configuration {
*/
bool editor_xembed = false;
/**
* When using this option, we'll enable the flush-to-zero flag during audio
* processing. This can be useful when the host passes denormals to a plugin
* that doesn't handle those well. An example of such a plugin is REDDI by
* Kush Audio. Some hosts, like Bitwig, will already snap denormals to zero
* for us so this may not be necessary with every host.
*/
bool force_ftz = false;
/**
* The number of times per second we'll handle the event loop. In most
* plugins this also controls the plugin editor GUI's refresh rate.
@@ -205,6 +214,7 @@ class Configuration {
s.value1b(editor_double_embed);
s.value1b(editor_force_dnd);
s.value1b(editor_xembed);
s.value1b(force_ftz);
s.ext(frame_rate, bitsery::ext::StdOptional(),
[](S& s, auto& v) { s.value4b(v); });
s.value1b(hide_daw);
+1 -3
View File
@@ -90,15 +90,13 @@ bool set_realtime_priority(bool sched_fifo, int priority = 5);
* previously when it drops out of scope.
*/
class ScopedFlushToZero {
public:
ScopedFlushToZero();
~ScopedFlushToZero();
ScopedFlushToZero(const ScopedFlushToZero&) = delete;
ScopedFlushToZero& operator=(const ScopedFlushToZero&) = delete;
ScopedFlushToZero(ScopedFlushToZero&&) = delete;
ScopedFlushToZero& operator=(ScopedFlushToZero&&) = delete;
private:
/**
* The previous FTZ mode. When we use this on the Wine side, this should
+3
View File
@@ -187,6 +187,9 @@ class PluginBridge {
if (config.editor_xembed) {
other_options.push_back("editor: XEmbed");
}
if (config.force_ftz) {
other_options.push_back("hack: force flush-to-zero");
}
if (config.frame_rate) {
std::ostringstream option;
option << "frame rate: " << std::setprecision(2)
+6
View File
@@ -180,6 +180,12 @@ Vst2Bridge::Vst2Bridge(MainContext& main_context,
sockets.host_vst_process_replacing.receive_multi<AudioBuffers>(
[&](AudioBuffers request, std::vector<uint8_t>& buffer) {
// HACK: When a plugin doesn't handle denormals properly, we can
// force the FTZ flag to be set to work around this
std::optional<ScopedFlushToZero> ftz_guard =
config.force_ftz ? std::make_optional<ScopedFlushToZero>()
: std::nullopt;
// As suggested by Jack Winter, we'll synchronize this thread's
// audio processing priority with that of the host's audio
// thread every once in a while
+8
View File
@@ -1211,6 +1211,14 @@ size_t Vst3Bridge::register_object_instance(
},
[&](YaAudioProcessor::Process& request)
-> YaAudioProcessor::Process::Response {
// HACK: When a plugin doesn't handle denormals
// properly, we can force the FTZ flag to be set
// to work around this
std::optional<ScopedFlushToZero> ftz_guard =
config.force_ftz
? std::make_optional<ScopedFlushToZero>()
: std::nullopt;
// As suggested by Jack Winter, we'll synchronize this
// thread's audio processing priority with that of the
// host's audio thread every once in a while