mirror of
https://github.com/robbert-vdh/yabridge.git
synced 2026-05-07 03:50:11 +02:00
Finally implement eff{Set,Get}SpeakerConfiguration
As mentioned in #1. This also indirectly allows yabridge to work under Renoise.
This commit is contained in:
@@ -13,6 +13,9 @@ Versioning](https://semver.org/spec/v2.0.0.html).
|
|||||||
- Added support for plugins that send MIDI events back to the host. This plugins
|
- Added support for plugins that send MIDI events back to the host. This plugins
|
||||||
such as Cthulhu and Scaler to output notes and CC for another plugin to work
|
such as Cthulhu and Scaler to output notes and CC for another plugin to work
|
||||||
with.
|
with.
|
||||||
|
- Added support for querying and setting detailed information about speaker
|
||||||
|
configurations for use in advanced surround setups. This indirectly allowed
|
||||||
|
yabridge to work under **Renoise**.
|
||||||
- Added automated development builds for yabridge, available by clicking on the
|
- Added automated development builds for yabridge, available by clicking on the
|
||||||
'Automated builds' badge in the project readme.
|
'Automated builds' badge in the project readme.
|
||||||
|
|
||||||
|
|||||||
+29
-4
@@ -56,7 +56,17 @@ class DefaultDataConverter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write the reponse back to the data pointer.
|
* Read data from the `value` pointer into a an `EventPayload` value that
|
||||||
|
* can be serialized and conveys the meaning of the event. This is only used
|
||||||
|
* for the `effSetSpeakerArrangement` and `effGetSpeakerArrangement` events.
|
||||||
|
*/
|
||||||
|
virtual std::optional<EventPayload> read_value(const int /*opcode*/,
|
||||||
|
const intptr_t /*value*/) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write the reponse back to the `data` pointer.
|
||||||
*/
|
*/
|
||||||
virtual void write(const int /*opcode*/,
|
virtual void write(const int /*opcode*/,
|
||||||
void* data,
|
void* data,
|
||||||
@@ -76,6 +86,14 @@ class DefaultDataConverter {
|
|||||||
response.payload);
|
response.payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write the reponse back to the `value` pointer. This is only used during
|
||||||
|
* the `effGetSpeakerArrangement` event.
|
||||||
|
*/
|
||||||
|
virtual void write_value(const int /*opcode*/,
|
||||||
|
intptr_t /*value*/,
|
||||||
|
const EventResult& /*response*/) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function can override a callback's return value based on the opcode.
|
* This function can override a callback's return value based on the opcode.
|
||||||
* This is used in one place to return a pointer to a `VstTime` object
|
* This is used in one place to return a pointer to a `VstTime` object
|
||||||
@@ -126,17 +144,21 @@ intptr_t send_event(boost::asio::local::stream_protocol::socket& socket,
|
|||||||
intptr_t value,
|
intptr_t value,
|
||||||
void* data,
|
void* data,
|
||||||
float option) {
|
float option) {
|
||||||
// Encode the right payload type for this event. Check the documentation for
|
// Encode the right payload types for this event. Check the documentation
|
||||||
// `EventPayload` for more information
|
// for `EventPayload` for more information. These types are converted to
|
||||||
|
// C-style data structures in `passthrough_event()` so they can be passed to
|
||||||
|
// a plugin or callback function.
|
||||||
const EventPayload payload =
|
const EventPayload payload =
|
||||||
data_converter.read(opcode, index, value, data);
|
data_converter.read(opcode, index, value, data);
|
||||||
|
const std::optional<EventPayload> value_payload =
|
||||||
|
data_converter.read_value(opcode, value);
|
||||||
|
|
||||||
if (logging.has_value()) {
|
if (logging.has_value()) {
|
||||||
auto [logger, is_dispatch] = logging.value();
|
auto [logger, is_dispatch] = logging.value();
|
||||||
logger.log_event(is_dispatch, opcode, index, value, payload, option);
|
logger.log_event(is_dispatch, opcode, index, value, payload, option);
|
||||||
}
|
}
|
||||||
|
|
||||||
const Event event{opcode, index, value, option, payload};
|
const Event event{opcode, index, value, option, payload, value_payload};
|
||||||
|
|
||||||
// Prevent two threads from writing over the socket at the same time and
|
// Prevent two threads from writing over the socket at the same time and
|
||||||
// messages getting out of order. This is needed because we can't prevent
|
// messages getting out of order. This is needed because we can't prevent
|
||||||
@@ -156,6 +178,7 @@ intptr_t send_event(boost::asio::local::stream_protocol::socket& socket,
|
|||||||
}
|
}
|
||||||
|
|
||||||
data_converter.write(opcode, data, response);
|
data_converter.write(opcode, data, response);
|
||||||
|
data_converter.write_value(opcode, value, response);
|
||||||
|
|
||||||
return data_converter.return_value(opcode, response.return_value);
|
return data_converter.return_value(opcode, response.return_value);
|
||||||
}
|
}
|
||||||
@@ -308,6 +331,8 @@ auto passthrough_event(AEffect* plugin, F callback) {
|
|||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
},
|
},
|
||||||
|
[&](DynamicSpeakerArrangement& speaker_arrangement)
|
||||||
|
-> EventResultPayload { return speaker_arrangement; },
|
||||||
[&](WantsChunkBuffer&) -> EventResultPayload {
|
[&](WantsChunkBuffer&) -> EventResultPayload {
|
||||||
// In this case the plugin will have written its data stored in
|
// In this case the plugin will have written its data stored in
|
||||||
// an array to which a pointer is stored in `data`, with the
|
// an array to which a pointer is stored in `data`, with the
|
||||||
|
|||||||
@@ -84,3 +84,10 @@ VstSpeakerArrangement& DynamicSpeakerArrangement::as_c_speaker_arrangement() {
|
|||||||
|
|
||||||
return *speaker_arrangement;
|
return *speaker_arrangement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t>& DynamicSpeakerArrangement::as_raw_data() {
|
||||||
|
// This will populate the buffer for us with the struct data
|
||||||
|
as_c_speaker_arrangement();
|
||||||
|
|
||||||
|
return speaker_arrangement_buffer;
|
||||||
|
}
|
||||||
|
|||||||
@@ -230,11 +230,18 @@ class alignas(16) DynamicSpeakerArrangement {
|
|||||||
const VstSpeakerArrangement& speaker_arrangement);
|
const VstSpeakerArrangement& speaker_arrangement);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a dynamically sized `VstSpeakerArrangement` struct based on
|
* Construct a dynamically sized `VstSpeakerArrangement` object based on
|
||||||
* this object.
|
* this object.
|
||||||
*/
|
*/
|
||||||
VstSpeakerArrangement& as_c_speaker_arrangement();
|
VstSpeakerArrangement& as_c_speaker_arrangement();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reconstruct the dynamically sized `VstSpeakerArrangement` object and
|
||||||
|
* return the raw data buffer. Needed to write the results back to the host
|
||||||
|
* since we can't just reassign the object.
|
||||||
|
*/
|
||||||
|
std::vector<uint8_t>& as_raw_data();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The flags field from `VstSpeakerArrangement`
|
* The flags field from `VstSpeakerArrangement`
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -359,6 +359,13 @@ class DispatchDataConverter : DefaultDataConverter {
|
|||||||
case effGetMidiKeyName:
|
case effGetMidiKeyName:
|
||||||
return *static_cast<const VstMidiKeyName*>(data);
|
return *static_cast<const VstMidiKeyName*>(data);
|
||||||
break;
|
break;
|
||||||
|
case effSetSpeakerArrangement:
|
||||||
|
case effGetSpeakerArrangement:
|
||||||
|
// This is the output speaker configuration, the `read_value()`
|
||||||
|
// method below reads the input speaker configuration
|
||||||
|
return DynamicSpeakerArrangement(
|
||||||
|
*static_cast<const VstSpeakerArrangement*>(data));
|
||||||
|
break;
|
||||||
// Any VST host I've encountered has properly zeroed out these their
|
// Any VST host I've encountered has properly zeroed out these their
|
||||||
// string buffers, but we'll add a list of opcodes that should
|
// string buffers, but we'll add a list of opcodes that should
|
||||||
// return a string just in case `DefaultDataConverter::read()` can't
|
// return a string just in case `DefaultDataConverter::read()` can't
|
||||||
@@ -380,6 +387,26 @@ class DispatchDataConverter : DefaultDataConverter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<EventPayload> read_value(const int opcode,
|
||||||
|
const intptr_t value) {
|
||||||
|
switch (opcode) {
|
||||||
|
case effSetSpeakerArrangement:
|
||||||
|
case effGetSpeakerArrangement:
|
||||||
|
// These two events are special in that they pass a pointer to
|
||||||
|
// the output speaker configuration through the `data`
|
||||||
|
// parameter, but then they also pass a pointer to the input
|
||||||
|
// speaker configuration through the `value` parameter. This is
|
||||||
|
// the only event that does this.
|
||||||
|
return DynamicSpeakerArrangement(
|
||||||
|
*static_cast<const VstSpeakerArrangement*>(
|
||||||
|
reinterpret_cast<void*>(value)));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return DefaultDataConverter::read_value(opcode, value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void write(const int opcode, void* data, const EventResult& response) {
|
void write(const int opcode, void* data, const EventResult& response) {
|
||||||
switch (opcode) {
|
switch (opcode) {
|
||||||
case effEditGetRect: {
|
case effEditGetRect: {
|
||||||
@@ -423,6 +450,25 @@ class DispatchDataConverter : DefaultDataConverter {
|
|||||||
|
|
||||||
*static_cast<VstMidiKeyName*>(data) = properties;
|
*static_cast<VstMidiKeyName*>(data) = properties;
|
||||||
} break;
|
} break;
|
||||||
|
case effGetSpeakerArrangement: {
|
||||||
|
// The plugin will have updated the objects passed by the host
|
||||||
|
// with its preferred output speaker configuration if it
|
||||||
|
// supports this. The same thing happens for the input speaker
|
||||||
|
// configuration in `write_value()`.
|
||||||
|
auto speaker_arrangement =
|
||||||
|
std::get<DynamicSpeakerArrangement>(response.payload);
|
||||||
|
|
||||||
|
// Reconstruct a dynamically sized `VstSpeakerArrangement`
|
||||||
|
// object to a buffer, and write back the results to the data
|
||||||
|
// parameter.
|
||||||
|
VstSpeakerArrangement* output =
|
||||||
|
static_cast<VstSpeakerArrangement*>(data);
|
||||||
|
std::vector<uint8_t> reconstructed_object =
|
||||||
|
speaker_arrangement.as_raw_data();
|
||||||
|
std::copy(reconstructed_object.begin(),
|
||||||
|
reconstructed_object.end(),
|
||||||
|
reinterpret_cast<uint8_t*>(output));
|
||||||
|
} break;
|
||||||
default:
|
default:
|
||||||
DefaultDataConverter::write(opcode, data, response);
|
DefaultDataConverter::write(opcode, data, response);
|
||||||
break;
|
break;
|
||||||
@@ -433,6 +479,32 @@ class DispatchDataConverter : DefaultDataConverter {
|
|||||||
return DefaultDataConverter::return_value(opcode, original);
|
return DefaultDataConverter::return_value(opcode, original);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void write_value(const int opcode,
|
||||||
|
intptr_t value,
|
||||||
|
const EventResult& response) {
|
||||||
|
switch (opcode) {
|
||||||
|
case effGetSpeakerArrangement: {
|
||||||
|
// Same as the above, but now for the input speaker
|
||||||
|
// configuration object under the `value` pointer
|
||||||
|
auto speaker_arrangement =
|
||||||
|
std::get<DynamicSpeakerArrangement>(response.payload);
|
||||||
|
|
||||||
|
VstSpeakerArrangement* output =
|
||||||
|
static_cast<VstSpeakerArrangement*>(
|
||||||
|
reinterpret_cast<void*>(value));
|
||||||
|
std::vector<uint8_t> reconstructed_object =
|
||||||
|
speaker_arrangement.as_raw_data();
|
||||||
|
std::copy(reconstructed_object.begin(),
|
||||||
|
reconstructed_object.end(),
|
||||||
|
reinterpret_cast<uint8_t*>(output));
|
||||||
|
} break;
|
||||||
|
default:
|
||||||
|
return DefaultDataConverter::write_value(opcode, value,
|
||||||
|
response);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<uint8_t>& chunk;
|
std::vector<uint8_t>& chunk;
|
||||||
VstRect& rect;
|
VstRect& rect;
|
||||||
|
|||||||
@@ -377,6 +377,11 @@ class HostCallbackDataConverter : DefaultDataConverter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<EventPayload> read_value(const int opcode,
|
||||||
|
const intptr_t value) {
|
||||||
|
return DefaultDataConverter::read_value(opcode, value);
|
||||||
|
}
|
||||||
|
|
||||||
void write(const int opcode, void* data, const EventResult& response) {
|
void write(const int opcode, void* data, const EventResult& response) {
|
||||||
switch (opcode) {
|
switch (opcode) {
|
||||||
case audioMasterGetTime:
|
case audioMasterGetTime:
|
||||||
@@ -415,6 +420,12 @@ class HostCallbackDataConverter : DefaultDataConverter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void write_value(const int opcode,
|
||||||
|
intptr_t value,
|
||||||
|
const EventResult& response) {
|
||||||
|
return DefaultDataConverter::write_value(opcode, value, response);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
AEffect* plugin;
|
AEffect* plugin;
|
||||||
std::optional<VstTimeInfo>& time_info;
|
std::optional<VstTimeInfo>& time_info;
|
||||||
|
|||||||
Reference in New Issue
Block a user