diff --git a/src/common/serialization/vst2.h b/src/common/serialization/vst2.h
index eae8f1d2..e44dfc16 100644
--- a/src/common/serialization/vst2.h
+++ b/src/common/serialization/vst2.h
@@ -53,9 +53,9 @@ constexpr size_t max_midi_events = max_buffer_size / sizeof(size_t);
[[maybe_unused]] constexpr size_t max_string_length = 64;
/**
- * The size for a buffer in which we're receiving chunks. Allow for up to 50 MB
- * chunks. Hopefully no plugin will come anywhere near this limit, but it will
- * add up when plugins start to audio samples in their presets.
+ * The maximum size for the buffer we're receiving chunks in. Allows for up to
+ * 50 MB chunks. Hopefully no plugin will come anywhere near this limit, but it
+ * will add up when plugins start to audio include samples in their presets.
*/
constexpr size_t binary_buffer_size = 50 << 20;
diff --git a/src/common/serialization/vst3/base.cpp b/src/common/serialization/vst3/base.cpp
index 24ef9618..1145dfb0 100644
--- a/src/common/serialization/vst3/base.cpp
+++ b/src/common/serialization/vst3/base.cpp
@@ -14,6 +14,9 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
+#include
+#include
+
#include "base.h"
UniversalTResult::UniversalTResult() : universal_result(Value::kResultFalse) {}
@@ -122,3 +125,137 @@ UniversalTResult::Value UniversalTResult::to_universal_result(
break;
}
}
+
+VectorStream::VectorStream(){FUNKNOWN_CTOR}
+
+VectorStream::VectorStream(Steinberg::IBStream* stream) {
+ FUNKNOWN_CTOR
+
+ if (!stream) {
+ throw std::runtime_error("Null pointer passed to VectorStream()");
+ }
+
+ if (stream->seek(0, Steinberg::IBStream::IStreamSeekMode::kIBSeekEnd) !=
+ Steinberg::kResultOk) {
+ throw std::runtime_error(
+ "IBStream passed to VectorStream() does not suport seeking to end");
+ }
+
+ // Now that we're at the end of the stream we know how large the buffer
+ // should be
+ int64 size;
+ assert(stream->tell(&size) == Steinberg::kResultOk);
+
+ int32 num_bytes_read;
+ buffer.resize(size);
+ assert(stream->seek(0, Steinberg::IBStream::IStreamSeekMode::kIBSeekSet) !=
+ Steinberg::kResultOk);
+ assert(stream->read(buffer.data(), size, &num_bytes_read) ==
+ Steinberg::kResultOk);
+ assert(num_bytes_read == 0 || num_bytes_read == size);
+}
+
+VectorStream::~VectorStream() {
+ FUNKNOWN_DTOR
+}
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor"
+IMPLEMENT_REFCOUNT(VectorStream)
+#pragma GCC diagnostic pop
+
+tresult PLUGIN_API VectorStream::queryInterface(Steinberg::FIDString _iid,
+ void** obj) {
+ QUERY_INTERFACE(_iid, obj, Steinberg::FUnknown::iid, Steinberg::IBStream)
+ QUERY_INTERFACE(_iid, obj, Steinberg::IBStream::iid, Steinberg::IBStream)
+ QUERY_INTERFACE(_iid, obj, Steinberg::ISizeableStream::iid,
+ Steinberg::ISizeableStream)
+
+ *obj = nullptr;
+ return Steinberg::kNoInterface;
+}
+
+tresult PLUGIN_API VectorStream::read(void* buffer,
+ int32 numBytes,
+ int32* numBytesRead) {
+ if (!buffer || numBytes < 0) {
+ return Steinberg::kInvalidArgument;
+ }
+
+ size_t bytes_to_read = std::min(static_cast(numBytes),
+ this->buffer.size() - seek_position);
+
+ std::copy_n(&this->buffer[seek_position], bytes_to_read,
+ reinterpret_cast(buffer));
+
+ seek_position += bytes_to_read;
+ if (numBytesRead) {
+ *numBytesRead = bytes_to_read;
+ }
+
+ return Steinberg::kResultOk;
+}
+
+tresult PLUGIN_API VectorStream::write(void* buffer,
+ int32 numBytes,
+ int32* numBytesWritten) {
+ if (!buffer || numBytes < 0) {
+ return Steinberg::kInvalidArgument;
+ }
+
+ if (seek_position + numBytes > this->buffer.size()) {
+ this->buffer.resize(seek_position + numBytes);
+ }
+
+ std::copy_n(reinterpret_cast(buffer), numBytes,
+ &this->buffer[seek_position]);
+
+ seek_position += numBytes;
+ if (numBytesWritten) {
+ *numBytesWritten = numBytes;
+ }
+
+ return Steinberg::kResultOk;
+}
+
+tresult PLUGIN_API VectorStream::seek(int64 pos, int32 mode, int64* result) {
+ switch (mode) {
+ case kIBSeekSet:
+ seek_position = pos;
+ break;
+ case kIBSeekCur:
+ seek_position += pos;
+ break;
+ case kIBSeekEnd:
+ seek_position = this->buffer.size() + pos;
+ break;
+ default:
+ return Steinberg::kInvalidArgument;
+ break;
+ }
+
+ if (result) {
+ *result = seek_position;
+ }
+
+ return Steinberg::kResultOk;
+}
+
+tresult PLUGIN_API VectorStream::tell(int64* pos) {
+ if (pos) {
+ *pos = seek_position;
+ return Steinberg::kResultOk;
+ } else {
+ return Steinberg::kInvalidArgument;
+ }
+}
+
+tresult PLUGIN_API VectorStream::getStreamSize(int64& size) {
+ size = seek_position;
+ return Steinberg::kResultOk;
+}
+
+tresult PLUGIN_API VectorStream::setStreamSize(int64 size) {
+ buffer.resize(size);
+ return Steinberg::kResultOk;
+}
diff --git a/src/common/serialization/vst3/base.h b/src/common/serialization/vst3/base.h
index f1e4a5fc..1cdeccae 100644
--- a/src/common/serialization/vst3/base.h
+++ b/src/common/serialization/vst3/base.h
@@ -18,14 +18,17 @@
#include
#include
+#include
#include
#include
+#include
// Yet Another layer of includes, but these are some VST3-specific typedefs that
// we'll need for all of our interfaces
-using Steinberg::TBool, Steinberg::int8, Steinberg::int32, Steinberg::tresult;
+using Steinberg::TBool, Steinberg::int8, Steinberg::int32, Steinberg::int64,
+ Steinberg::tresult;
/**
* Both `TUID` (`int8_t[16]`) and `FIDString` (`char*`) are hard to work with
@@ -36,6 +39,13 @@ using ArrayUID = std::array<
std::remove_reference_t()[0])>,
std::extent_v>;
+/**
+ * The maximum size for an `IBStream` we can serialize. Allows for up to 50 MB
+ * of preset data. Hopefully no plugin will come anywhere near this limit, but
+ * it will add up when plugins start to include audio samples in their presets.
+ */
+constexpr size_t max_vector_stream_size = 50 << 20;
+
/**
* Empty struct for when we have send a response to some operation without any
* result values.
@@ -101,3 +111,54 @@ class UniversalTResult {
Value universal_result;
};
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
+
+/**
+ * Serialize an `IBStream` into an `std::vector`, and allow the
+ * receiving side to use it as an `IBStream` again. `ISizeableStream` is defined
+ * but then for whatever reason never used, but we'll implement it anyways.
+ */
+class VectorStream : public Steinberg::IBStream,
+ public Steinberg::ISizeableStream {
+ public:
+ VectorStream();
+
+ /**
+ * Read an existing stream.
+ */
+ VectorStream(Steinberg::IBStream* stream);
+
+ virtual ~VectorStream();
+
+ DECLARE_FUNKNOWN_METHODS
+
+ // From `IBstream`
+ tresult PLUGIN_API read(void* buffer,
+ int32 numBytes,
+ int32* numBytesRead = nullptr) override;
+ tresult PLUGIN_API write(void* buffer,
+ int32 numBytes,
+ int32* numBytesWritten = nullptr) override;
+ tresult PLUGIN_API seek(int64 pos,
+ int32 mode,
+ int64* result = nullptr) override;
+ tresult PLUGIN_API tell(int64* pos) override;
+
+ // From `ISizeableStream`
+ tresult PLUGIN_API getStreamSize(int64& size) override;
+ tresult PLUGIN_API setStreamSize(int64 size) override;
+
+ template
+ void serialize(S& s) {
+ s.container1b(buffer, max_vector_stream_size);
+ // The seek position should always be initialized at 0
+ }
+
+ private:
+ std::vector buffer;
+ size_t seek_position;
+};
+
+#pragma GCC diagnostic pop