mirror of
https://github.com/robbert-vdh/yabridge.git
synced 2026-05-10 04:30:12 +02:00
Extend the lifetime of MIDI events, fixing Kontakt
This commit is contained in:
@@ -20,7 +20,6 @@ There are a few things that should be done before releasing this, including:
|
|||||||
that do this. Not sure if this is fixable in yabridge.
|
that do this. Not sure if this is fixable in yabridge.
|
||||||
- Serum crashed and audio engine froze while browsing through Serum presets in
|
- Serum crashed and audio engine froze while browsing through Serum presets in
|
||||||
the browser?
|
the browser?
|
||||||
- No midi input in Kontakt.
|
|
||||||
- Polish GUIs even further. There are some todos left in
|
- Polish GUIs even further. There are some todos left in
|
||||||
`src/wine-host/editor.{h,cpp}`.
|
`src/wine-host/editor.{h,cpp}`.
|
||||||
- Add missing details if any to the architecture section.
|
- Add missing details if any to the architecture section.
|
||||||
|
|||||||
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
#include "plugin-bridge.h"
|
#include "plugin-bridge.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
|
|
||||||
#include "../common/communication.h"
|
#include "../common/communication.h"
|
||||||
@@ -157,8 +159,43 @@ void PluginBridge::handle_dispatch() {
|
|||||||
|
|
||||||
[[noreturn]] void PluginBridge::handle_dispatch_midi_events() {
|
[[noreturn]] void PluginBridge::handle_dispatch_midi_events() {
|
||||||
while (true) {
|
while (true) {
|
||||||
passthrough_event(host_vst_dispatch_midi_events, std::nullopt, plugin,
|
// TODO: Refactor `passthrough_event()` to factor out the data
|
||||||
plugin->dispatcher);
|
// conversion specifically for this case so we don't have to do
|
||||||
|
// this `DynamicVstEvents -> VstEvents -> void* ->
|
||||||
|
// DynamicVstEvents -> VstEvents -> void*` conversion dance
|
||||||
|
passthrough_event(
|
||||||
|
host_vst_dispatch_midi_events, std::nullopt, plugin,
|
||||||
|
[&](AEffect* plugin, int opcode, int index, intptr_t value,
|
||||||
|
void* data, float option) {
|
||||||
|
if (BOOST_LIKELY(opcode == effProcessEvents)) {
|
||||||
|
// For 99% of the plugins we can just call
|
||||||
|
// `effProcessReplacing()` and be done with it, but a select
|
||||||
|
// few plugins (I could only find Kontakt that does this)
|
||||||
|
// don't actually make copies of the events they receive and
|
||||||
|
// only store pointers, meaning that they have to live at
|
||||||
|
// least until the next audio buffer gets processed. This
|
||||||
|
// does mean that we have to reconstruct the
|
||||||
|
// `DynamicVstEvents` object first.
|
||||||
|
// HACK: Is there a cleaner way to do this, or a way to
|
||||||
|
// avoid having to store temporary copies of this?
|
||||||
|
DynamicVstEvents* events;
|
||||||
|
{
|
||||||
|
std::lock_guard lock(next_buffer_midi_events_mutex);
|
||||||
|
events = &next_audio_buffer_midi_events.emplace_back(
|
||||||
|
*static_cast<const VstEvents*>(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
return plugin->dispatcher(plugin, opcode, index, value,
|
||||||
|
&events->as_c_events(), option);
|
||||||
|
} else {
|
||||||
|
std::cerr << "[Warning] Received non-midi "
|
||||||
|
"event on midi processing thread"
|
||||||
|
<< std::endl;
|
||||||
|
|
||||||
|
return dispatch_wrapper(plugin, opcode, index, value, data,
|
||||||
|
option);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,9 +247,16 @@ void PluginBridge::handle_dispatch() {
|
|||||||
outputs.push_back(buffer.data());
|
outputs.push_back(buffer.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Let the plugin process the midi events that were received since the
|
||||||
|
// last buffer, and then clean up those events. This approach should not
|
||||||
|
// be needed but Kontakt only stores pointers to rather than copies of
|
||||||
|
// the events.
|
||||||
|
{
|
||||||
|
std::lock_guard lock(next_buffer_midi_events_mutex);
|
||||||
|
|
||||||
// Any plugin made in the last fifteen years or so should support
|
// Any plugin made in the last fifteen years or so should support
|
||||||
// `processReplacing`. In the off chance it does not we can just emulate
|
// `processReplacing`. In the off chance it does not we can just
|
||||||
// this behavior ourselves.
|
// emulate this behavior ourselves.
|
||||||
if (plugin->processReplacing != nullptr) {
|
if (plugin->processReplacing != nullptr) {
|
||||||
plugin->processReplacing(plugin, inputs.data(), outputs.data(),
|
plugin->processReplacing(plugin, inputs.data(), outputs.data(),
|
||||||
request.sample_frames);
|
request.sample_frames);
|
||||||
@@ -227,6 +271,9 @@ void PluginBridge::handle_dispatch() {
|
|||||||
request.sample_frames);
|
request.sample_frames);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
next_audio_buffer_midi_events.clear();
|
||||||
|
}
|
||||||
|
|
||||||
AudioBuffers response{output_buffers, request.sample_frames};
|
AudioBuffers response{output_buffers, request.sample_frames};
|
||||||
write_object(host_vst_process_replacing, response, process_buffer);
|
write_object(host_vst_process_replacing, response, process_buffer);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -168,6 +168,20 @@ class PluginBridge {
|
|||||||
*/
|
*/
|
||||||
std::vector<uint8_t> process_buffer;
|
std::vector<uint8_t> process_buffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The MIDI events that have been received **and processed** since the last
|
||||||
|
* call to `processReplacing()`. 99% of plugins make a copy of the MIDI
|
||||||
|
* events they receive but some plugins such as Kontakt only store pointers
|
||||||
|
* to these events, which means that the actual `VstEvent` objects must live
|
||||||
|
* at least until the next audio buffer gets processed.
|
||||||
|
*/
|
||||||
|
std::vector<DynamicVstEvents> next_audio_buffer_midi_events;
|
||||||
|
/**
|
||||||
|
* Mutex for locking the above event queue, since recieving and processing
|
||||||
|
* now happens in two different threads.
|
||||||
|
*/
|
||||||
|
std::mutex next_buffer_midi_events_mutex;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The plugin editor window. Allows embedding the plugin's editor into a
|
* The plugin editor window. Allows embedding the plugin's editor into a
|
||||||
* Wine window, and embedding that Wine window into a window provided by the
|
* Wine window, and embedding that Wine window into a window provided by the
|
||||||
|
|||||||
Reference in New Issue
Block a user