diff --git a/src/encoder/EncoderInterface.hxx b/src/encoder/EncoderInterface.hxx index fca0a660d..79facda95 100644 --- a/src/encoder/EncoderInterface.hxx +++ b/src/encoder/EncoderInterface.hxx @@ -21,6 +21,7 @@ #define MPD_ENCODER_INTERFACE_HXX #include +#include 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 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 Read(std::span buffer) noexcept = 0; }; class PreparedEncoder { diff --git a/src/encoder/ToOutputStream.cxx b/src/encoder/ToOutputStream.cxx index 0926f45cc..873c61903 100644 --- a/src/encoder/ToOutputStream.cxx +++ b/src/encoder/ToOutputStream.cxx @@ -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()); } } diff --git a/src/encoder/plugins/FlacEncoderPlugin.cxx b/src/encoder/plugins/FlacEncoderPlugin.cxx index 49615df4b..4490d1c48 100644 --- a/src/encoder/plugins/FlacEncoderPlugin.cxx +++ b/src/encoder/plugins/FlacEncoderPlugin.cxx @@ -71,10 +71,10 @@ public: void SendTag(const Tag &tag) override; - void Write(const void *data, size_t length) override; + void Write(std::span src) override; - size_t Read(void *dest, size_t length) noexcept override { - return output_buffer.Read((std::byte *)dest, length); + std::span Read(std::span 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 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: diff --git a/src/encoder/plugins/LameEncoderPlugin.cxx b/src/encoder/plugins/LameEncoderPlugin.cxx index e4b38cad6..1e90ce89e 100644 --- a/src/encoder/plugins/LameEncoderPlugin.cxx +++ b/src/encoder/plugins/LameEncoderPlugin.cxx @@ -23,6 +23,7 @@ #include "util/NumberParser.hxx" #include "util/ReusableArray.hxx" #include "util/RuntimeError.hxx" +#include "util/SpanCast.hxx" #include @@ -167,14 +168,14 @@ LameEncoder::~LameEncoder() noexcept } void -LameEncoder::Write(std::span src) +LameEncoder::Write(std::span _src) { - const auto *src = (const int16_t*)data; + const auto src = FromBytesStrict(_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 src) /* this is for only 16-bit audio */ int bytes_out = lame_encode_buffer_interleaved(gfp, - const_cast(src), + const_cast(src.data()), num_frames, dest, output_buffer_size); @@ -194,19 +195,18 @@ LameEncoder::Write(std::span src) output_end = dest + bytes_out; } -size_t -LameEncoder::Read(void *dest, size_t length) noexcept +std::span +LameEncoder::Read(std::span 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 = { diff --git a/src/encoder/plugins/NullEncoderPlugin.cxx b/src/encoder/plugins/NullEncoderPlugin.cxx index 3212143c0..10f48969a 100644 --- a/src/encoder/plugins/NullEncoderPlugin.cxx +++ b/src/encoder/plugins/NullEncoderPlugin.cxx @@ -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 src) override { + buffer.Append(src); } - size_t Read(void *dest, size_t length) noexcept override { - return buffer.Read((std::byte *)dest, length); + std::span Read(std::span b) noexcept override { + return b.first(buffer.Read(b.data(), b.size())); } }; diff --git a/src/encoder/plugins/OggEncoder.hxx b/src/encoder/plugins/OggEncoder.hxx index 39b62d822..ea2579648 100644 --- a/src/encoder/plugins/OggEncoder.hxx +++ b/src/encoder/plugins/OggEncoder.hxx @@ -50,7 +50,7 @@ public: flush = true; } - size_t Read(void *dest, size_t length) noexcept override { + std::span Read(std::span 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())); } }; diff --git a/src/encoder/plugins/OpusEncoderPlugin.cxx b/src/encoder/plugins/OpusEncoderPlugin.cxx index 24eacda0f..9fc7cc9f2 100644 --- a/src/encoder/plugins/OpusEncoderPlugin.cxx +++ b/src/encoder/plugins/OpusEncoderPlugin.cxx @@ -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 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 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) diff --git a/src/encoder/plugins/ShineEncoderPlugin.cxx b/src/encoder/plugins/ShineEncoderPlugin.cxx index 19a4cd776..92330a0c2 100644 --- a/src/encoder/plugins/ShineEncoderPlugin.cxx +++ b/src/encoder/plugins/ShineEncoderPlugin.cxx @@ -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 src) override; - size_t Read(void *dest, size_t length) noexcept override { - return output_buffer.Read((std::byte *)dest, length); + std::span Read(std::span 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 _src) { - const auto *data = (const int16_t*)_data; - length /= sizeof(*data) * audio_format.channels; + const auto src = FromBytesStrict(_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); diff --git a/src/encoder/plugins/TwolameEncoderPlugin.cxx b/src/encoder/plugins/TwolameEncoderPlugin.cxx index 5ca2dd756..65c1de3d6 100644 --- a/src/encoder/plugins/TwolameEncoderPlugin.cxx +++ b/src/encoder/plugins/TwolameEncoderPlugin.cxx @@ -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 src) override; + std::span Read(std::span 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 _src) { - const auto *src = (const int16_t*)data; + const auto src = FromBytesStrict(_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 +TwolameEncoder::Read(std::span 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 = { diff --git a/src/encoder/plugins/VorbisEncoderPlugin.cxx b/src/encoder/plugins/VorbisEncoderPlugin.cxx index 649ffa54e..fdb5d102c 100644 --- a/src/encoder/plugins/VorbisEncoderPlugin.cxx +++ b/src/encoder/plugins/VorbisEncoderPlugin.cxx @@ -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 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 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); diff --git a/src/encoder/plugins/WaveEncoderPlugin.cxx b/src/encoder/plugins/WaveEncoderPlugin.cxx index 322e89417..e623ad01b 100644 --- a/src/encoder/plugins/WaveEncoderPlugin.cxx +++ b/src/encoder/plugins/WaveEncoderPlugin.cxx @@ -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 src) override; - size_t Read(void *dest, size_t length) noexcept override { - return buffer.Read((std::byte *)dest, length); + std::span Read(std::span 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 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; } } diff --git a/src/output/plugins/RecorderOutputPlugin.cxx b/src/output/plugins/RecorderOutputPlugin.cxx index dd6af3d04..9b2112339 100644 --- a/src/output/plugins/RecorderOutputPlugin.cxx +++ b/src/output/plugins/RecorderOutputPlugin.cxx @@ -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(); diff --git a/src/output/plugins/ShoutOutputPlugin.cxx b/src/output/plugins/ShoutOutputPlugin.cxx index 0ddf3cfcf..a338d66d2 100644 --- a/src/output/plugins/ShoutOutputPlugin.cxx +++ b/src/output/plugins/ShoutOutputPlugin.cxx @@ -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; diff --git a/src/output/plugins/httpd/HttpdInternal.hxx b/src/output/plugins/httpd/HttpdInternal.hxx index e892ff2c7..b56f2702a 100644 --- a/src/output/plugins/httpd/HttpdInternal.hxx +++ b/src/output/plugins/httpd/HttpdInternal.hxx @@ -40,6 +40,7 @@ #include #include #include +#include struct ConfigBlock; class EventLoop; @@ -249,7 +250,7 @@ public: * * Throws on error. */ - void EncodeAndPlay(const void *chunk, size_t size); + void EncodeAndPlay(std::span src); void SendTag(const Tag &tag) override; diff --git a/src/output/plugins/httpd/HttpdOutputPlugin.cxx b/src/output/plugins/httpd/HttpdOutputPlugin.cxx index 0e81a1097..acb5889e9 100644 --- a/src/output/plugins/httpd/HttpdOutputPlugin.cxx +++ b/src/output/plugins/httpd/HttpdOutputPlugin.cxx @@ -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 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(); diff --git a/src/output/plugins/snapcast/SnapcastOutputPlugin.cxx b/src/output/plugins/snapcast/SnapcastOutputPlugin.cxx index fc612735b..f7d128b2f 100644 --- a/src/output/plugins/snapcast/SnapcastOutputPlugin.cxx +++ b/src/output/plugins/snapcast/SnapcastOutputPlugin.cxx @@ -128,9 +128,7 @@ ReadEncoder(Encoder &encoder) noexcept { std::byte buffer[4096]; - size_t nbytes = encoder.Read(buffer, sizeof(buffer)); - const std::span src{buffer, nbytes}; - return AllocatedArray{src}; + return AllocatedArray{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 payload{buffer, nbytes}; chunks.push(std::make_shared(now, AllocatedArray{payload})); } diff --git a/test/run_encoder.cxx b/test/run_encoder.cxx index 7782bee9f..4285afb34 100644 --- a/test/run_encoder.cxx +++ b/test/run_encoder.cxx @@ -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); } diff --git a/test/test_vorbis_encoder.cxx b/test/test_vorbis_encoder.cxx index ffcdc1fa5..3bfa45276 100644 --- a/test/test_vorbis_encoder.cxx +++ b/test/test_vorbis_encoder.cxx @@ -33,8 +33,6 @@ #include -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 */