mirror of
https://github.com/robbert-vdh/yabridge.git
synced 2026-06-15 16:03:55 +02:00
Clean up proxy object construction/destruction
This commit is contained in:
+166
-207
@@ -120,191 +120,7 @@ void Vst3Bridge::run() {
|
|||||||
return UniversalTResult(Steinberg::kResultFalse);
|
return UniversalTResult(Steinberg::kResultFalse);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::lock_guard lock(object_instances_mutex);
|
const size_t instance_id = register_object_instance(object);
|
||||||
|
|
||||||
const size_t instance_id = generate_instance_id();
|
|
||||||
object_instances.emplace(instance_id, std::move(object));
|
|
||||||
|
|
||||||
// If the object supports `IComponent` or `IAudioProcessor`,
|
|
||||||
// then we'll set up a dedicated thread for function calls for
|
|
||||||
// those interfaces.
|
|
||||||
if (object_instances[instance_id].audio_processor ||
|
|
||||||
object_instances[instance_id].component) {
|
|
||||||
std::promise<void> socket_listening_latch;
|
|
||||||
object_instances[instance_id]
|
|
||||||
.audio_processor_handler = Win32Thread([&,
|
|
||||||
instance_id]() {
|
|
||||||
// TODO: Move this somewhere else, because this is
|
|
||||||
// obviously ridiculous
|
|
||||||
sockets.add_audio_processor_and_listen(
|
|
||||||
instance_id, socket_listening_latch,
|
|
||||||
overload{
|
|
||||||
[&](YaAudioProcessor::SetBusArrangements&
|
|
||||||
request)
|
|
||||||
-> YaAudioProcessor::SetBusArrangements::
|
|
||||||
Response {
|
|
||||||
return object_instances
|
|
||||||
[request.instance_id]
|
|
||||||
.audio_processor
|
|
||||||
->setBusArrangements(
|
|
||||||
request.inputs.data(),
|
|
||||||
request.num_ins,
|
|
||||||
request.outputs.data(),
|
|
||||||
request.num_outs);
|
|
||||||
},
|
|
||||||
[&](YaAudioProcessor::GetBusArrangement&
|
|
||||||
request)
|
|
||||||
-> YaAudioProcessor::GetBusArrangement::
|
|
||||||
Response {
|
|
||||||
const tresult result =
|
|
||||||
object_instances
|
|
||||||
[request.instance_id]
|
|
||||||
.audio_processor
|
|
||||||
->getBusArrangement(
|
|
||||||
request.dir,
|
|
||||||
request.index,
|
|
||||||
request.arr);
|
|
||||||
|
|
||||||
return YaAudioProcessor::
|
|
||||||
GetBusArrangementResponse{
|
|
||||||
.result = result,
|
|
||||||
.updated_arr = request.arr};
|
|
||||||
},
|
|
||||||
[&](const YaAudioProcessor::
|
|
||||||
CanProcessSampleSize& request)
|
|
||||||
-> YaAudioProcessor::CanProcessSampleSize::
|
|
||||||
Response {
|
|
||||||
return object_instances
|
|
||||||
[request.instance_id]
|
|
||||||
.audio_processor
|
|
||||||
->canProcessSampleSize(
|
|
||||||
request
|
|
||||||
.symbolic_sample_size);
|
|
||||||
},
|
|
||||||
[&](const YaAudioProcessor::GetLatencySamples&
|
|
||||||
request)
|
|
||||||
-> YaAudioProcessor::GetLatencySamples::
|
|
||||||
Response {
|
|
||||||
return object_instances
|
|
||||||
[request.instance_id]
|
|
||||||
.audio_processor
|
|
||||||
->getLatencySamples();
|
|
||||||
},
|
|
||||||
[&](YaAudioProcessor::SetupProcessing& request)
|
|
||||||
-> YaAudioProcessor::SetupProcessing::
|
|
||||||
Response {
|
|
||||||
return object_instances
|
|
||||||
[request.instance_id]
|
|
||||||
.audio_processor
|
|
||||||
->setupProcessing(
|
|
||||||
request.setup);
|
|
||||||
},
|
|
||||||
[&](const YaAudioProcessor::SetProcessing&
|
|
||||||
request)
|
|
||||||
-> YaAudioProcessor::SetProcessing::
|
|
||||||
Response {
|
|
||||||
return object_instances
|
|
||||||
[request.instance_id]
|
|
||||||
.audio_processor
|
|
||||||
->setProcessing(
|
|
||||||
request.state);
|
|
||||||
},
|
|
||||||
[&](YaAudioProcessor::Process& request)
|
|
||||||
-> YaAudioProcessor::Process::Response {
|
|
||||||
const tresult result =
|
|
||||||
object_instances[request.instance_id]
|
|
||||||
.audio_processor->process(
|
|
||||||
request.data.get());
|
|
||||||
|
|
||||||
return YaAudioProcessor::ProcessResponse{
|
|
||||||
.result = result,
|
|
||||||
.output_data =
|
|
||||||
request.data
|
|
||||||
.move_outputs_to_response()};
|
|
||||||
},
|
|
||||||
[&](const YaAudioProcessor::GetTailSamples&
|
|
||||||
request)
|
|
||||||
-> YaAudioProcessor::GetTailSamples::
|
|
||||||
Response {
|
|
||||||
return object_instances
|
|
||||||
[request.instance_id]
|
|
||||||
.audio_processor
|
|
||||||
->getTailSamples();
|
|
||||||
},
|
|
||||||
[&](const YaComponent::GetControllerClassId&
|
|
||||||
request)
|
|
||||||
-> YaComponent::GetControllerClassId::
|
|
||||||
Response {
|
|
||||||
Steinberg::TUID cid;
|
|
||||||
const tresult result =
|
|
||||||
object_instances
|
|
||||||
[request.instance_id]
|
|
||||||
.component
|
|
||||||
->getControllerClassId(
|
|
||||||
cid);
|
|
||||||
|
|
||||||
return YaComponent::
|
|
||||||
GetControllerClassIdResponse{
|
|
||||||
.result = result,
|
|
||||||
.editor_cid =
|
|
||||||
std::to_array(cid)};
|
|
||||||
},
|
|
||||||
[&](const YaComponent::SetIoMode& request)
|
|
||||||
-> YaComponent::SetIoMode::Response {
|
|
||||||
return object_instances[request.instance_id]
|
|
||||||
.component->setIoMode(request.mode);
|
|
||||||
},
|
|
||||||
[&](const YaComponent::GetBusCount& request)
|
|
||||||
-> YaComponent::GetBusCount::Response {
|
|
||||||
return object_instances[request.instance_id]
|
|
||||||
.component->getBusCount(request.type,
|
|
||||||
request.dir);
|
|
||||||
},
|
|
||||||
[&](YaComponent::GetBusInfo& request)
|
|
||||||
-> YaComponent::GetBusInfo::Response {
|
|
||||||
const tresult result =
|
|
||||||
object_instances[request.instance_id]
|
|
||||||
.component->getBusInfo(
|
|
||||||
request.type, request.dir,
|
|
||||||
request.index, request.bus);
|
|
||||||
|
|
||||||
return YaComponent::GetBusInfoResponse{
|
|
||||||
.result = result,
|
|
||||||
.updated_bus = request.bus};
|
|
||||||
},
|
|
||||||
[&](YaComponent::GetRoutingInfo& request)
|
|
||||||
-> YaComponent::GetRoutingInfo::Response {
|
|
||||||
const tresult result =
|
|
||||||
object_instances[request.instance_id]
|
|
||||||
.component->getRoutingInfo(
|
|
||||||
request.in_info,
|
|
||||||
request.out_info);
|
|
||||||
|
|
||||||
return YaComponent::GetRoutingInfoResponse{
|
|
||||||
.result = result,
|
|
||||||
.updated_in_info = request.in_info,
|
|
||||||
.updated_out_info = request.out_info};
|
|
||||||
},
|
|
||||||
[&](const YaComponent::ActivateBus& request)
|
|
||||||
-> YaComponent::ActivateBus::Response {
|
|
||||||
return object_instances[request.instance_id]
|
|
||||||
.component->activateBus(
|
|
||||||
request.type, request.dir,
|
|
||||||
request.index, request.state);
|
|
||||||
},
|
|
||||||
[&](const YaComponent::SetActive& request)
|
|
||||||
-> YaComponent::SetActive::Response {
|
|
||||||
return object_instances[request.instance_id]
|
|
||||||
.component->setActive(request.state);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Wait for the new socket to be listening on before
|
|
||||||
// continuing. Otherwise the native plugin may try to
|
|
||||||
// connect to it before our thread is up and running.
|
|
||||||
socket_listening_latch.get_future().wait();
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is where the magic happens. Here we deduce which
|
// This is where the magic happens. Here we deduce which
|
||||||
// interfaces are supported by this object so we can create
|
// interfaces are supported by this object so we can create
|
||||||
@@ -314,28 +130,7 @@ void Vst3Bridge::run() {
|
|||||||
},
|
},
|
||||||
[&](const Vst3PluginProxy::Destruct& request)
|
[&](const Vst3PluginProxy::Destruct& request)
|
||||||
-> Vst3PluginProxy::Destruct::Response {
|
-> Vst3PluginProxy::Destruct::Response {
|
||||||
// Tear the dedicated audio processing socket down again if we
|
unregister_object_instance(request.instance_id);
|
||||||
// created one while handling `Vst3PluginProxy::Construct`
|
|
||||||
if (object_instances[request.instance_id].audio_processor ||
|
|
||||||
object_instances[request.instance_id].component) {
|
|
||||||
sockets.remove_audio_processor(request.instance_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove the instance from within the main IO context so
|
|
||||||
// removing it doesn't interfere with the Win32 message loop
|
|
||||||
main_context
|
|
||||||
.run_in_context([&]() {
|
|
||||||
std::lock_guard lock(object_instances_mutex);
|
|
||||||
object_instances.erase(request.instance_id);
|
|
||||||
})
|
|
||||||
.wait();
|
|
||||||
|
|
||||||
// XXX: I don't think we have to wait for the object to be
|
|
||||||
// deleted most of the time, but I can imagine a situation
|
|
||||||
// where the plugin does a host callback triggered by a
|
|
||||||
// Win32 timer in between where the above closure is being
|
|
||||||
// executed and when the actual host application context on
|
|
||||||
// the plugin side gets deallocated.
|
|
||||||
return Ack{};
|
return Ack{};
|
||||||
},
|
},
|
||||||
[&](Vst3PluginProxy::SetState& request)
|
[&](Vst3PluginProxy::SetState& request)
|
||||||
@@ -657,3 +452,167 @@ void Vst3Bridge::handle_win32_events() {
|
|||||||
size_t Vst3Bridge::generate_instance_id() {
|
size_t Vst3Bridge::generate_instance_id() {
|
||||||
return current_instance_id.fetch_add(1);
|
return current_instance_id.fetch_add(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t Vst3Bridge::register_object_instance(
|
||||||
|
Steinberg::IPtr<Steinberg::FUnknown> object) {
|
||||||
|
std::lock_guard lock(object_instances_mutex);
|
||||||
|
|
||||||
|
const size_t instance_id = generate_instance_id();
|
||||||
|
object_instances.emplace(instance_id, std::move(object));
|
||||||
|
|
||||||
|
// If the object supports `IComponent` or `IAudioProcessor`,
|
||||||
|
// then we'll set up a dedicated thread for function calls for
|
||||||
|
// those interfaces.
|
||||||
|
if (object_instances[instance_id].audio_processor ||
|
||||||
|
object_instances[instance_id].component) {
|
||||||
|
std::promise<void> socket_listening_latch;
|
||||||
|
|
||||||
|
object_instances[instance_id]
|
||||||
|
.audio_processor_handler = Win32Thread([&, instance_id]() {
|
||||||
|
sockets.add_audio_processor_and_listen(
|
||||||
|
instance_id, socket_listening_latch,
|
||||||
|
overload{
|
||||||
|
[&](YaAudioProcessor::SetBusArrangements& request)
|
||||||
|
-> YaAudioProcessor::SetBusArrangements::Response {
|
||||||
|
return object_instances[request.instance_id]
|
||||||
|
.audio_processor->setBusArrangements(
|
||||||
|
request.inputs.data(), request.num_ins,
|
||||||
|
request.outputs.data(), request.num_outs);
|
||||||
|
},
|
||||||
|
[&](YaAudioProcessor::GetBusArrangement& request)
|
||||||
|
-> YaAudioProcessor::GetBusArrangement::Response {
|
||||||
|
const tresult result =
|
||||||
|
object_instances[request.instance_id]
|
||||||
|
.audio_processor->getBusArrangement(
|
||||||
|
request.dir, request.index, request.arr);
|
||||||
|
|
||||||
|
return YaAudioProcessor::GetBusArrangementResponse{
|
||||||
|
.result = result, .updated_arr = request.arr};
|
||||||
|
},
|
||||||
|
[&](const YaAudioProcessor::CanProcessSampleSize& request)
|
||||||
|
-> YaAudioProcessor::CanProcessSampleSize::Response {
|
||||||
|
return object_instances[request.instance_id]
|
||||||
|
.audio_processor->canProcessSampleSize(
|
||||||
|
request.symbolic_sample_size);
|
||||||
|
},
|
||||||
|
[&](const YaAudioProcessor::GetLatencySamples& request)
|
||||||
|
-> YaAudioProcessor::GetLatencySamples::Response {
|
||||||
|
return object_instances[request.instance_id]
|
||||||
|
.audio_processor->getLatencySamples();
|
||||||
|
},
|
||||||
|
[&](YaAudioProcessor::SetupProcessing& request)
|
||||||
|
-> YaAudioProcessor::SetupProcessing::Response {
|
||||||
|
return object_instances[request.instance_id]
|
||||||
|
.audio_processor->setupProcessing(request.setup);
|
||||||
|
},
|
||||||
|
[&](const YaAudioProcessor::SetProcessing& request)
|
||||||
|
-> YaAudioProcessor::SetProcessing::Response {
|
||||||
|
return object_instances[request.instance_id]
|
||||||
|
.audio_processor->setProcessing(request.state);
|
||||||
|
},
|
||||||
|
[&](YaAudioProcessor::Process& request)
|
||||||
|
-> YaAudioProcessor::Process::Response {
|
||||||
|
const tresult result =
|
||||||
|
object_instances[request.instance_id]
|
||||||
|
.audio_processor->process(request.data.get());
|
||||||
|
|
||||||
|
return YaAudioProcessor::ProcessResponse{
|
||||||
|
.result = result,
|
||||||
|
.output_data =
|
||||||
|
request.data.move_outputs_to_response()};
|
||||||
|
},
|
||||||
|
[&](const YaAudioProcessor::GetTailSamples& request)
|
||||||
|
-> YaAudioProcessor::GetTailSamples::Response {
|
||||||
|
return object_instances[request.instance_id]
|
||||||
|
.audio_processor->getTailSamples();
|
||||||
|
},
|
||||||
|
[&](const YaComponent::GetControllerClassId& request)
|
||||||
|
-> YaComponent::GetControllerClassId::Response {
|
||||||
|
Steinberg::TUID cid;
|
||||||
|
const tresult result =
|
||||||
|
object_instances[request.instance_id]
|
||||||
|
.component->getControllerClassId(cid);
|
||||||
|
|
||||||
|
return YaComponent::GetControllerClassIdResponse{
|
||||||
|
.result = result, .editor_cid = std::to_array(cid)};
|
||||||
|
},
|
||||||
|
[&](const YaComponent::SetIoMode& request)
|
||||||
|
-> YaComponent::SetIoMode::Response {
|
||||||
|
return object_instances[request.instance_id]
|
||||||
|
.component->setIoMode(request.mode);
|
||||||
|
},
|
||||||
|
[&](const YaComponent::GetBusCount& request)
|
||||||
|
-> YaComponent::GetBusCount::Response {
|
||||||
|
return object_instances[request.instance_id]
|
||||||
|
.component->getBusCount(request.type, request.dir);
|
||||||
|
},
|
||||||
|
[&](YaComponent::GetBusInfo& request)
|
||||||
|
-> YaComponent::GetBusInfo::Response {
|
||||||
|
const tresult result =
|
||||||
|
object_instances[request.instance_id]
|
||||||
|
.component->getBusInfo(
|
||||||
|
request.type, request.dir, request.index,
|
||||||
|
request.bus);
|
||||||
|
|
||||||
|
return YaComponent::GetBusInfoResponse{
|
||||||
|
.result = result, .updated_bus = request.bus};
|
||||||
|
},
|
||||||
|
[&](YaComponent::GetRoutingInfo& request)
|
||||||
|
-> YaComponent::GetRoutingInfo::Response {
|
||||||
|
const tresult result =
|
||||||
|
object_instances[request.instance_id]
|
||||||
|
.component->getRoutingInfo(request.in_info,
|
||||||
|
request.out_info);
|
||||||
|
|
||||||
|
return YaComponent::GetRoutingInfoResponse{
|
||||||
|
.result = result,
|
||||||
|
.updated_in_info = request.in_info,
|
||||||
|
.updated_out_info = request.out_info};
|
||||||
|
},
|
||||||
|
[&](const YaComponent::ActivateBus& request)
|
||||||
|
-> YaComponent::ActivateBus::Response {
|
||||||
|
return object_instances[request.instance_id]
|
||||||
|
.component->activateBus(request.type, request.dir,
|
||||||
|
request.index,
|
||||||
|
request.state);
|
||||||
|
},
|
||||||
|
[&](const YaComponent::SetActive& request)
|
||||||
|
-> YaComponent::SetActive::Response {
|
||||||
|
return object_instances[request.instance_id]
|
||||||
|
.component->setActive(request.state);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Wait for the new socket to be listening on before
|
||||||
|
// continuing. Otherwise the native plugin may try to
|
||||||
|
// connect to it before our thread is up and running.
|
||||||
|
socket_listening_latch.get_future().wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
return instance_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Vst3Bridge::unregister_object_instance(size_t instance_id) {
|
||||||
|
// Tear the dedicated audio processing socket down again if we
|
||||||
|
// created one while handling `Vst3PluginProxy::Construct`
|
||||||
|
if (object_instances[instance_id].audio_processor ||
|
||||||
|
object_instances[instance_id].component) {
|
||||||
|
sockets.remove_audio_processor(instance_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the instance from within the main IO context so
|
||||||
|
// removing it doesn't interfere with the Win32 message loop
|
||||||
|
// XXX: I don't think we have to wait for the object to be
|
||||||
|
// deleted most of the time, but I can imagine a situation
|
||||||
|
// where the plugin does a host callback triggered by a
|
||||||
|
// Win32 timer in between where the above closure is being
|
||||||
|
// executed and when the actual host application context on
|
||||||
|
// the plugin side gets deallocated.
|
||||||
|
main_context
|
||||||
|
.run_in_context([&, instance_id]() {
|
||||||
|
std::lock_guard lock(object_instances_mutex);
|
||||||
|
object_instances.erase(instance_id);
|
||||||
|
})
|
||||||
|
.wait();
|
||||||
|
}
|
||||||
|
|||||||
@@ -148,6 +148,20 @@ class Vst3Bridge : public HostBridge {
|
|||||||
*/
|
*/
|
||||||
size_t generate_instance_id();
|
size_t generate_instance_id();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assign a unique identifier to an object and add it to `object_instances`.
|
||||||
|
* This will also set up listeners for `IAudioProcessor` and `IComponent`
|
||||||
|
* function calls.
|
||||||
|
*/
|
||||||
|
size_t register_object_instance(
|
||||||
|
Steinberg::IPtr<Steinberg::FUnknown> object);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an object from `object_instances`. Will also tear down the
|
||||||
|
* `IAudioProcessor`/`IComponent` socket if it had one.
|
||||||
|
*/
|
||||||
|
void unregister_object_instance(size_t instance_id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The IO context used for event handling so that all events and window
|
* The IO context used for event handling so that all events and window
|
||||||
* message handling can be performed from a single thread, even when hosting
|
* message handling can be performed from a single thread, even when hosting
|
||||||
|
|||||||
Reference in New Issue
Block a user