encoder/Interface: pass std::span to Write() and Read()

This commit is contained in:
Max Kellermann 2022-07-11 22:14:48 +02:00
parent 28e044a36a
commit 7e14f8f830
18 changed files with 118 additions and 115 deletions

View File

@ -21,6 +21,7 @@
#define MPD_ENCODER_INTERFACE_HXX
#include <cstddef>
#include <span>
struct AudioFormat;
struct Tag;
@ -91,18 +92,19 @@ public:
* @param data the buffer containing PCM samples
* @param length the length of the buffer in bytes
*/
virtual void Write(const void *data, std::size_t length) = 0;
virtual void Write(std::span<const std::byte> src) = 0;
/**
* Reads encoded data from the encoder.
*
* Call this repeatedly until no more data is returned.
*
* @param dest the destination buffer to copy to
* @param length the maximum length of the destination buffer
* @return the number of bytes written to #dest
* @param buffer a buffer that can be used to write data into
*
* @return the portion of the buffer that was filled (but may
* also point to a different buffer, e.g. one owned by this object)
*/
virtual std::size_t Read(void *dest, std::size_t length) noexcept = 0;
virtual std::span<const std::byte> Read(std::span<std::byte> buffer) noexcept = 0;
};
class PreparedEncoder {

View File

@ -28,12 +28,12 @@ EncoderToOutputStream(OutputStream &os, Encoder &encoder)
/* read from the encoder */
std::byte buffer[32768];
size_t nbytes = encoder.Read(buffer, sizeof(buffer));
if (nbytes == 0)
const auto r = encoder.Read(std::span{buffer});
if (r.empty())
return;
/* write everything to the stream */
os.Write(buffer, nbytes);
os.Write(r.data(), r.size());
}
}

View File

@ -71,10 +71,10 @@ public:
void SendTag(const Tag &tag) override;
void Write(const void *data, size_t length) override;
void Write(std::span<const std::byte> src) override;
size_t Read(void *dest, size_t length) noexcept override {
return output_buffer.Read((std::byte *)dest, length);
std::span<const std::byte> Read(std::span<std::byte> buffer) noexcept override {
return buffer.first(output_buffer.Read(buffer.data(), buffer.size()));
}
private:
@ -267,27 +267,27 @@ pcm16_to_flac(int32_t *out, const int16_t *in, std::size_t num_samples) noexcept
}
void
FlacEncoder::Write(const void *data, size_t length)
FlacEncoder::Write(std::span<const std::byte> src)
{
void *exbuffer;
const void *buffer = nullptr;
/* format conversion */
const std::size_t num_frames = length / audio_format.GetFrameSize();
const std::size_t num_frames = src.size() / audio_format.GetFrameSize();
const std::size_t num_samples = num_frames * audio_format.channels;
switch (audio_format.format) {
case SampleFormat::S8:
exbuffer = expand_buffer.Get(length * 4);
pcm8_to_flac((int32_t *)exbuffer, (const int8_t *)data,
exbuffer = expand_buffer.Get(src.size() * 4);
pcm8_to_flac((int32_t *)exbuffer, (const int8_t *)src.data(),
num_samples);
buffer = exbuffer;
break;
case SampleFormat::S16:
exbuffer = expand_buffer.Get(length * 2);
pcm16_to_flac((int32_t *)exbuffer, (const int16_t *)data,
exbuffer = expand_buffer.Get(src.size() * 2);
pcm16_to_flac((int32_t *)exbuffer, (const int16_t *)src.data(),
num_samples);
buffer = exbuffer;
break;
@ -296,7 +296,7 @@ FlacEncoder::Write(const void *data, size_t length)
case SampleFormat::S32:
/* nothing need to be done; format is the same for
both mpd and libFLAC */
buffer = data;
buffer = src.data();
break;
default:

View File

@ -23,6 +23,7 @@
#include "util/NumberParser.hxx"
#include "util/ReusableArray.hxx"
#include "util/RuntimeError.hxx"
#include "util/SpanCast.hxx"
#include <lame/lame.h>
@ -167,14 +168,14 @@ LameEncoder::~LameEncoder() noexcept
}
void
LameEncoder::Write(std::span<const std::byte> src)
LameEncoder::Write(std::span<const std::byte> _src)
{
const auto *src = (const int16_t*)data;
const auto src = FromBytesStrict<const int16_t>(_src);
assert(output_begin == output_end);
const std::size_t num_frames = length / audio_format.GetFrameSize();
const std::size_t num_samples = length / audio_format.GetSampleSize();
const std::size_t num_samples = src.size();
const std::size_t num_frames = num_samples / audio_format.channels;
/* worst-case formula according to LAME documentation */
const std::size_t output_buffer_size = 5 * num_samples / 4 + 7200;
@ -183,7 +184,7 @@ LameEncoder::Write(std::span<const std::byte> src)
/* this is for only 16-bit audio */
int bytes_out = lame_encode_buffer_interleaved(gfp,
const_cast<short *>(src),
const_cast<short *>(src.data()),
num_frames,
dest, output_buffer_size);
@ -194,19 +195,18 @@ LameEncoder::Write(std::span<const std::byte> src)
output_end = dest + bytes_out;
}
size_t
LameEncoder::Read(void *dest, size_t length) noexcept
std::span<const std::byte>
LameEncoder::Read(std::span<std::byte> buffer) noexcept
{
const auto begin = output_begin;
assert(begin <= output_end);
const std::size_t remainning = output_end - begin;
if (length > remainning)
length = remainning;
const std::size_t nbytes = std::min(remainning, buffer.size());
memcpy(dest, begin, length);
memcpy(buffer.data(), begin, nbytes);
output_begin = begin + length;
return length;
output_begin = begin + nbytes;
return buffer.first(nbytes);
}
const EncoderPlugin lame_encoder_plugin = {

View File

@ -29,12 +29,12 @@ public:
:Encoder(false) {}
/* virtual methods from class Encoder */
void Write(const void *data, size_t length) override {
buffer.Append({(const std::byte *)data, length});
void Write(std::span<const std::byte> src) override {
buffer.Append(src);
}
size_t Read(void *dest, size_t length) noexcept override {
return buffer.Read((std::byte *)dest, length);
std::span<const std::byte> Read(std::span<std::byte> b) noexcept override {
return b.first(buffer.Read(b.data(), b.size()));
}
};

View File

@ -50,7 +50,7 @@ public:
flush = true;
}
size_t Read(void *dest, size_t length) noexcept override {
std::span<const std::byte> Read(std::span<std::byte> buffer) noexcept override {
ogg_page page;
bool success = stream.PageOut(page);
if (!success) {
@ -60,10 +60,11 @@ public:
}
if (!success)
return 0;
return {};
}
return ReadPage(page, dest, length);
return buffer.first(ReadPage(page, buffer.data(),
buffer.size()));
}
};

View File

@ -61,7 +61,7 @@ public:
/* virtual methods from class Encoder */
void End() override;
void Write(const void *data, size_t length) override;
void Write(std::span<const std::byte> src) override;
void PreTag() override;
void SendTag(const Tag &tag) override;
@ -272,10 +272,8 @@ OpusEncoder::WriteSilence(unsigned fill_frames)
}
void
OpusEncoder::Write(const void *_data, size_t length)
OpusEncoder::Write(std::span<const std::byte> src)
{
const auto *data = (const std::byte *)_data;
if (lookahead > 0) {
/* generate some silence at the beginning of the
stream */
@ -286,14 +284,12 @@ OpusEncoder::Write(const void *_data, size_t length)
lookahead = 0;
}
while (length > 0) {
size_t nbytes = buffer_size - buffer_position;
if (nbytes > length)
nbytes = length;
while (!src.empty()) {
const std::size_t nbytes = std::min(buffer_size - buffer_position,
src.size());
memcpy(buffer + buffer_position, data, nbytes);
data += nbytes;
length -= nbytes;
memcpy(buffer + buffer_position, src.data(), nbytes);
src = src.subspan(nbytes);
buffer_position += nbytes;
if (buffer_position == buffer_size)

View File

@ -22,6 +22,7 @@
#include "pcm/AudioFormat.hxx"
#include "util/DynamicFifoBuffer.hxx"
#include "util/RuntimeError.hxx"
#include "util/SpanCast.hxx"
extern "C"
{
@ -75,10 +76,10 @@ public:
void Flush() override;
void Write(const void *data, size_t length) override;
void Write(std::span<const std::byte> src) override;
size_t Read(void *dest, size_t length) noexcept override {
return output_buffer.Read((std::byte *)dest, length);
std::span<const std::byte> Read(std::span<std::byte> buffer) noexcept override {
return buffer.first(output_buffer.Read(buffer.data(), buffer.size()));
}
};
@ -165,24 +166,24 @@ ShineEncoder::WriteChunk(bool flush)
}
void
ShineEncoder::Write(const void *_data, size_t length)
ShineEncoder::Write(std::span<const std::byte> _src)
{
const auto *data = (const int16_t*)_data;
length /= sizeof(*data) * audio_format.channels;
const auto src = FromBytesStrict<const int16_t>(_src);
const std::size_t nframes = src.size() / audio_format.channels;
size_t written = 0;
if (input_pos > SHINE_MAX_SAMPLES)
input_pos = 0;
/* write all data to de-interleaved buffers */
while (written < length) {
while (written < nframes) {
for (;
written < length && input_pos < frame_size;
written < nframes && input_pos < frame_size;
written++, input_pos++) {
const size_t base =
written * audio_format.channels;
stereo[0][input_pos] = data[base];
stereo[1][input_pos] = data[base + 1];
stereo[0][input_pos] = src[base];
stereo[1][input_pos] = src[base + 1];
}
/* write if chunk is filled */
WriteChunk(false);

View File

@ -22,6 +22,7 @@
#include "pcm/AudioFormat.hxx"
#include "util/NumberParser.hxx"
#include "util/RuntimeError.hxx"
#include "util/SpanCast.hxx"
#include "util/Domain.hxx"
#include "Log.hxx"
@ -67,8 +68,8 @@ public:
flush = true;
}
void Write(const void *data, size_t length) override;
size_t Read(void *dest, size_t length) noexcept override;
void Write(std::span<const std::byte> src) override;
std::span<const std::byte> Read(std::span<std::byte> buffer) noexcept override;
};
class PreparedTwolameEncoder final : public PreparedEncoder {
@ -187,16 +188,16 @@ TwolameEncoder::~TwolameEncoder() noexcept
}
void
TwolameEncoder::Write(const void *data, size_t length)
TwolameEncoder::Write(std::span<const std::byte> _src)
{
const auto *src = (const int16_t*)data;
const auto src = FromBytesStrict<const int16_t>(_src);
assert(output_buffer_position == output_buffer_length);
const std::size_t num_frames = length / audio_format.GetFrameSize();
const std::size_t num_frames = src.size() / audio_format.channels;
int bytes_out = twolame_encode_buffer_interleaved(options,
src, num_frames,
src.data(), num_frames,
output_buffer,
sizeof(output_buffer));
if (bytes_out < 0)
@ -206,8 +207,8 @@ TwolameEncoder::Write(const void *data, size_t length)
output_buffer_position = 0;
}
size_t
TwolameEncoder::Read(void *dest, size_t length) noexcept
std::span<const std::byte>
TwolameEncoder::Read(std::span<std::byte> buffer) noexcept
{
assert(output_buffer_position <= output_buffer_length);
@ -224,14 +225,13 @@ TwolameEncoder::Read(void *dest, size_t length) noexcept
const std::size_t remainning = output_buffer_length - output_buffer_position;
if (length > remainning)
length = remainning;
const std::size_t nbytes = std::min(remainning, buffer.size());
memcpy(dest, output_buffer + output_buffer_position, length);
memcpy(buffer.data(), output_buffer + output_buffer_position, nbytes);
output_buffer_position += length;
output_buffer_position += nbytes;
return length;
return buffer.first(nbytes);
}
const EncoderPlugin twolame_encoder_plugin = {

View File

@ -55,7 +55,7 @@ public:
void PreTag() override;
void SendTag(const Tag &tag) override;
void Write(const void *data, size_t length) override;
void Write(std::span<const std::byte> src) override;
private:
void HeaderOut(vorbis_comment &vc);
@ -245,14 +245,14 @@ interleaved_to_vorbis_buffer(float **dest, const float *src,
}
void
VorbisEncoder::Write(const void *data, size_t length)
VorbisEncoder::Write(std::span<const std::byte> src)
{
std::size_t num_frames = length / audio_format.GetFrameSize();
std::size_t num_frames = src.size() / audio_format.GetFrameSize();
/* this is for only 16-bit audio */
interleaved_to_vorbis_buffer(vorbis_analysis_buffer(&vd, num_frames),
(const float *)data,
(const float *)(const void *)src.data(),
num_frames,
audio_format.channels);

View File

@ -36,10 +36,10 @@ public:
explicit WaveEncoder(AudioFormat &audio_format) noexcept;
/* virtual methods from class Encoder */
void Write(const void *data, size_t length) override;
void Write(std::span<const std::byte> src) override;
size_t Read(void *dest, size_t length) noexcept override {
return buffer.Read((std::byte *)dest, length);
std::span<const std::byte> Read(std::span<std::byte> b) noexcept override {
return b.first(buffer.Read(b.data(), b.size()));
}
};
@ -183,8 +183,9 @@ pcm24_to_wave(uint8_t *dst8, const uint32_t *src32, size_t length) noexcept
}
void
WaveEncoder::Write(const void *src, size_t length)
WaveEncoder::Write(std::span<const std::byte> src)
{
std::size_t length = src.size();
std::byte *dst = buffer.Write(length);
if (IsLittleEndian()) {
@ -192,29 +193,33 @@ WaveEncoder::Write(const void *src, size_t length)
case 8:
case 16:
case 32:// optimized cases
memcpy(dst, src, length);
memcpy(dst, src.data(), length);
break;
case 24:
length = pcm24_to_wave((uint8_t *)dst,
(const uint32_t *)src, length);
(const uint32_t *)(const void *)src.data(),
length);
break;
}
} else {
switch (bits) {
case 8:
memcpy(dst, src, length);
memcpy(dst, src.data(), length);
break;
case 16:
length = pcm16_to_wave((uint16_t *)dst,
(const uint16_t *)src, length);
(const uint16_t *)(const void *)src.data(),
length);
break;
case 24:
length = pcm24_to_wave((uint8_t *)dst,
(const uint32_t *)src, length);
(const uint32_t *)(const void *)src.data(),
length);
break;
case 32:
length = pcm32_to_wave((uint32_t *)dst,
(const uint32_t *)src, length);
(const uint32_t *)(const void *)src.data(),
length);
break;
}
}

View File

@ -333,7 +333,7 @@ RecorderOutput::Play(const void *chunk, size_t size)
return size;
}
encoder->Write(chunk, size);
encoder->Write({(const std::byte *)chunk, size});
EncoderToFile();

View File

@ -326,12 +326,14 @@ static void
EncoderToShout(shout_t *shout_conn, Encoder &encoder)
{
while (true) {
uint8_t buffer[32768];
size_t nbytes = encoder.Read(buffer, sizeof(buffer));
if (nbytes == 0)
std::byte buffer[32768];
const auto e = encoder.Read(std::span{buffer});
if (e.empty() == 0)
return;
int err = shout_send(shout_conn, buffer, nbytes);
int err = shout_send(shout_conn,
(const unsigned char *)e.data(),
e.size());
HandleShoutError(shout_conn, err);
}
}
@ -414,7 +416,7 @@ ShoutOutput::Delay() const noexcept
size_t
ShoutOutput::Play(const void *chunk, size_t size)
{
encoder->Write(chunk, size);
encoder->Write({(const std::byte *)chunk, size});
WritePage();
return size;
}
@ -422,9 +424,9 @@ ShoutOutput::Play(const void *chunk, size_t size)
bool
ShoutOutput::Pause()
{
static char silence[1020];
static std::byte silence[1020];
encoder->Write(silence, sizeof(silence));
encoder->Write(std::span{silence});
WritePage();
return true;

View File

@ -40,6 +40,7 @@
#include <queue>
#include <list>
#include <memory>
#include <span>
struct ConfigBlock;
class EventLoop;
@ -249,7 +250,7 @@ public:
*
* Throws on error.
*/
void EncodeAndPlay(const void *chunk, size_t size);
void EncodeAndPlay(std::span<const std::byte> src);
void SendTag(const Tag &tag) override;

View File

@ -161,14 +161,13 @@ HttpdOutput::ReadPage() noexcept
size_t size = 0;
do {
size_t nbytes = encoder->Read(buffer + size,
sizeof(buffer) - size);
if (nbytes == 0)
const auto r = encoder->Read(std::span{buffer}.subspan(size));
if (r.empty())
break;
unflushed_input = 0;
size += nbytes;
size += r.size();
} while (size < sizeof(buffer));
if (size == 0)
@ -301,11 +300,11 @@ HttpdOutput::BroadcastFromEncoder() noexcept
}
inline void
HttpdOutput::EncodeAndPlay(const void *chunk, size_t size)
HttpdOutput::EncodeAndPlay(std::span<const std::byte> src)
{
encoder->Write(chunk, size);
encoder->Write(src);
unflushed_input += size;
unflushed_input += src.size();
BroadcastFromEncoder();
}
@ -316,7 +315,7 @@ HttpdOutput::Play(const void *chunk, size_t size)
pause = false;
if (LockHasClients())
EncodeAndPlay(chunk, size);
EncodeAndPlay({(const std::byte *)chunk, size});
if (!timer->IsStarted())
timer->Start();

View File

@ -128,9 +128,7 @@ ReadEncoder(Encoder &encoder) noexcept
{
std::byte buffer[4096];
size_t nbytes = encoder.Read(buffer, sizeof(buffer));
const std::span<const std::byte> src{buffer, nbytes};
return AllocatedArray<std::byte>{src};
return AllocatedArray<std::byte>{encoder.Read(std::span{buffer})};
}
inline void
@ -313,7 +311,7 @@ SnapcastOutput::Play(const void *chunk, size_t size)
if (!LockHasClients())
return size;
encoder->Write(chunk, size);
encoder->Write({(const std::byte *)chunk, size});
unflushed_input += size;
if (unflushed_input >= 65536) {
@ -332,8 +330,8 @@ SnapcastOutput::Play(const void *chunk, size_t size)
while (true) {
std::byte buffer[32768];
size_t nbytes = encoder->Read(buffer, sizeof(buffer));
if (nbytes == 0)
const auto payload = encoder->Read(std::span{buffer});
if (payload.empty())
break;
unflushed_input = 0;
@ -342,7 +340,6 @@ SnapcastOutput::Play(const void *chunk, size_t size)
if (chunks.empty())
inject_event.Schedule();
const std::span<const std::byte> payload{buffer, nbytes};
chunks.push(std::make_shared<SnapcastChunk>(now, AllocatedArray{payload}));
}

View File

@ -37,7 +37,6 @@
int main(int argc, char **argv)
try {
const char *encoder_name;
static char buffer[32768];
/* parse command line */
@ -79,9 +78,10 @@ try {
/* do it */
static std::byte buffer[32768];
ssize_t nbytes;
while ((nbytes = read(0, buffer, sizeof(buffer))) > 0) {
encoder->Write(buffer, nbytes);
encoder->Write(std::span{buffer}.first(nbytes));
EncoderToOutputStream(os, *encoder);
}

View File

@ -33,8 +33,6 @@
#include <stddef.h>
static uint8_t zero[256];
int
main([[maybe_unused]] int argc, [[maybe_unused]] char **argv)
try {
@ -61,7 +59,8 @@ try {
/* write a block of data */
encoder->Write(zero, sizeof(zero));
static constexpr std::byte zero[256]{};
encoder->Write(std::span{zero});
EncoderToOutputStream(os, *encoder);
@ -86,7 +85,7 @@ try {
/* write another block of data */
encoder->Write(zero, sizeof(zero));
encoder->Write(std::span{zero});
/* finish */