mirror of
https://github.com/robbert-vdh/yabridge.git
synced 2026-05-08 12:30:12 +02:00
Add a serialization wrapper for streams
This commit is contained in:
@@ -9,6 +9,7 @@ Yabridge currently tracks CLAP 1.1.1. The implementation status for CLAP's core
|
||||
| core feature | status |
|
||||
| ----------------------------------------- | ------------------------------------------------------ |
|
||||
| Core plugin and host functionality | :warning: Everything but actual audio processing works |
|
||||
| Streams | :heavy_check_mark: |
|
||||
| `clap.plugin-factory` | :heavy_check_mark: |
|
||||
| `clap.plugin-invalidation-factory/draft0` | :x: Not supported yet |
|
||||
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
// yabridge: a Wine plugin bridge
|
||||
// Copyright (C) 2020-2022 Robbert van der Helm
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more destates.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "stream.h"
|
||||
|
||||
namespace clap {
|
||||
namespace stream {
|
||||
|
||||
/**
|
||||
* We'll try to read the host's `clap_istream_t` in 1 MB chunks.
|
||||
*/
|
||||
constexpr size_t read_chunk_size = 1 << 20;
|
||||
|
||||
Stream::Stream() {}
|
||||
|
||||
Stream::Stream(const clap_istream_t& original) {
|
||||
// CLAP streams have no length indication. A plugin could do something like
|
||||
// prepending the stream's length to the stream, but we can't do that. So
|
||||
// instead we'll try to read in 1 MB chunks until we reach end of file. Even
|
||||
// if the stream's size is over 1 MB, the host may still return less than 1
|
||||
// MB at a time at its discretion.
|
||||
size_t stream_length = 0;
|
||||
while (true) {
|
||||
// Start by reserving enough capacity to read 1 MB
|
||||
buffer_.resize(stream_length + read_chunk_size);
|
||||
const int64_t num_bytes_read = original.read(
|
||||
&original, buffer_.data() + stream_length, read_chunk_size);
|
||||
|
||||
// We're done when we reach the end of the file
|
||||
if (num_bytes_read <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
stream_length += num_bytes_read;
|
||||
}
|
||||
|
||||
// Trim the excess reserved space
|
||||
buffer_.resize(stream_length);
|
||||
}
|
||||
|
||||
const clap_ostream_t* Stream::ostream() {
|
||||
ostream_vtable.write = ostream_write;
|
||||
ostream_vtable.ctx = this;
|
||||
|
||||
return &ostream_vtable;
|
||||
}
|
||||
|
||||
const clap_istream_t* Stream::istream() {
|
||||
istream_vtable.read = istream_read;
|
||||
istream_vtable.ctx = this;
|
||||
|
||||
return &istream_vtable;
|
||||
}
|
||||
|
||||
void Stream::write_to_stream(const clap_ostream_t& original) const {
|
||||
// The host may not let us write the whole stream all at once, so we need to
|
||||
// keep track of how many bytes we've written and keep going until
|
||||
// everything has been written back to the host.
|
||||
size_t num_bytes_written = 0;
|
||||
while (num_bytes_written < buffer_.size()) {
|
||||
const int64_t actual_written_bytes =
|
||||
original.write(&original, buffer_.data() + num_bytes_written,
|
||||
buffer_.size() - num_bytes_written);
|
||||
assert(actual_written_bytes > 0);
|
||||
|
||||
num_bytes_written += actual_written_bytes;
|
||||
}
|
||||
}
|
||||
|
||||
int64_t CLAP_ABI Stream::ostream_write(const struct clap_ostream* stream,
|
||||
const void* buffer,
|
||||
uint64_t size) {
|
||||
assert(stream && stream->ctx && buffer);
|
||||
auto self = static_cast<Stream*>(stream->ctx);
|
||||
|
||||
// We can just read everything at the same time
|
||||
const size_t start_pos = self->buffer_.size();
|
||||
self->buffer_.resize(start_pos + size);
|
||||
std::copy_n(static_cast<const uint8_t*>(buffer), size,
|
||||
self->buffer_.data() + start_pos);
|
||||
|
||||
return static_cast<int64_t>(size);
|
||||
}
|
||||
|
||||
int64_t CLAP_ABI Stream::istream_read(const struct clap_istream* stream,
|
||||
void* buffer,
|
||||
uint64_t size) {
|
||||
assert(stream && stream->ctx && buffer);
|
||||
auto self = static_cast<Stream*>(stream->ctx);
|
||||
|
||||
// `self->read_pos` is a cursor in the buffer. CLAP streams always read from
|
||||
// begin to end with no way to rewind.
|
||||
const size_t num_bytes_read = std::min(
|
||||
static_cast<size_t>(size), self->buffer_.size() - self->read_pos);
|
||||
|
||||
std::copy_n(self->buffer_.data() + self->read_pos, num_bytes_read,
|
||||
static_cast<uint8_t*>(buffer));
|
||||
self->read_pos += num_bytes_read;
|
||||
|
||||
return static_cast<int64_t>(num_bytes_read);
|
||||
}
|
||||
|
||||
} // namespace stream
|
||||
} // namespace clap
|
||||
@@ -0,0 +1,92 @@
|
||||
// yabridge: a Wine plugin bridge
|
||||
// Copyright (C) 2020-2022 Robbert van der Helm
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more destates.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <bitsery/traits/vector.h>
|
||||
#include <clap/stream.h>
|
||||
|
||||
// Serialization messages for `clap/stream.h`
|
||||
|
||||
namespace clap {
|
||||
namespace stream {
|
||||
|
||||
/**
|
||||
* A serialization wrapper around streams that can be used as both a
|
||||
* `clap_istream_t` and a `clap_ostream_t`.
|
||||
*/
|
||||
class Stream {
|
||||
public:
|
||||
/**
|
||||
* Create an empty stream that can be written to by the plugin using
|
||||
* `ostream()`, and then written back to the host using
|
||||
* `write_to_ostream()`.
|
||||
*/
|
||||
Stream();
|
||||
|
||||
/**
|
||||
* Read a `clap_istream_t` from the host to a buffer. The results are
|
||||
* written to a buffer that can be serialized and send to the other side.
|
||||
*/
|
||||
Stream(const clap_istream_t& original);
|
||||
|
||||
/**
|
||||
* Get a `clap_ostream_t` for this buffer that the plugin can write to. This
|
||||
* is only valid as long as this object is not moved.
|
||||
*/
|
||||
const clap_ostream_t* ostream();
|
||||
/**
|
||||
* Get a `clap_istream_t` for this buffer that the plugin can read the
|
||||
* buffer from. This is only valid as long as this object is not moved.
|
||||
*/
|
||||
const clap_istream_t* istream();
|
||||
|
||||
/**
|
||||
* Write the entire buffer to a host provided `clap_ostream_t`.
|
||||
*/
|
||||
void write_to_stream(const clap_ostream_t& original) const;
|
||||
|
||||
template <typename S>
|
||||
void serialize(S& s) {
|
||||
s.container1b(buffer_, 50 << 20);
|
||||
}
|
||||
|
||||
protected:
|
||||
static int64_t CLAP_ABI ostream_write(const struct clap_ostream* stream,
|
||||
const void* buffer,
|
||||
uint64_t size);
|
||||
|
||||
static int64_t CLAP_ABI istream_read(const struct clap_istream* stream,
|
||||
void* buffer,
|
||||
uint64_t size);
|
||||
|
||||
private:
|
||||
std::vector<uint8_t> buffer_;
|
||||
|
||||
/**
|
||||
* The current position in the buffer used in `istream_read()`.
|
||||
*/
|
||||
size_t read_pos = 0;
|
||||
|
||||
// These are populated in the `ostream()` and `istream()` methods
|
||||
clap_ostream_t ostream_vtable{};
|
||||
clap_istream_t istream_vtable{};
|
||||
};
|
||||
|
||||
} // namespace stream
|
||||
} // namespace clap
|
||||
@@ -82,6 +82,7 @@ if with_clap
|
||||
'../common/serialization/clap/ext/params.cpp',
|
||||
'../common/serialization/clap/host.cpp',
|
||||
'../common/serialization/clap/plugin.cpp',
|
||||
'../common/serialization/clap/stream.cpp',
|
||||
'../common/utils.cpp',
|
||||
'../include/llvm/small-vector.cpp',
|
||||
'bridges/clap-impls/plugin-proxy.cpp',
|
||||
|
||||
@@ -85,6 +85,7 @@ if with_clap
|
||||
'../common/serialization/clap/ext/params.cpp',
|
||||
'../common/serialization/clap/host.cpp',
|
||||
'../common/serialization/clap/plugin.cpp',
|
||||
'../common/serialization/clap/stream.cpp',
|
||||
'bridges/clap-impls/host-proxy.cpp',
|
||||
'bridges/clap.cpp',
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user