Audio library refactor: use spans for stream->read

This commit is contained in:
Nikita Lisitsa 2023-01-10 03:35:34 +03:00
parent 7376109e96
commit 2209648999
22 changed files with 124 additions and 130 deletions

View file

@ -20,7 +20,7 @@ namespace psemek::audio
float smoothness() const; float smoothness() const;
float smoothness(float value); float smoothness(float value);
void apply(float * data, std::size_t sample_count); void apply(util::span<float> samples);
private: private:
std::atomic<float> gain_[2]; std::atomic<float> gain_[2];

View file

@ -1,5 +1,7 @@
#pragma once #pragma once
#include <psemek/util/span.hpp>
#include <cstddef> #include <cstddef>
#include <memory> #include <memory>
#include <optional> #include <optional>
@ -14,7 +16,7 @@ namespace psemek::audio
// Return value less than sample count means end of stream // Return value less than sample count means end of stream
// Must be called from mixing thread // Must be called from mixing thread
virtual std::size_t read(float * data, std::size_t sample_count) = 0; virtual std::size_t read(util::span<float> samples) = 0;
// The number of samples already played from this stream // The number of samples already played from this stream
virtual std::size_t played() const = 0; virtual std::size_t played() const = 0;

View file

@ -26,17 +26,16 @@ namespace psemek::audio
return played_.load(); return played_.load();
} }
std::size_t read(float * data, std::size_t sample_count) override std::size_t read(util::span<float> samples) override
{ {
auto end = data + sample_count; for (auto p = samples.begin(); p != samples.end();)
for (auto p = data; p != end;)
{ {
float v = func_(); float v = func_();
*p++ = v; *p++ = v;
*p++ = v; *p++ = v;
} }
played_.fetch_add(sample_count); played_.fetch_add(samples.size());
return sample_count; return samples.size();
} }
private: private:

View file

@ -26,18 +26,18 @@ namespace psemek::audio
return played_.load(); return played_.load();
} }
std::size_t read(float * data, std::size_t sample_count) override std::size_t read(util::span<float> samples) override
{ {
auto buffer = recorder_->buffer(); auto buffer = recorder_->buffer();
auto played = played_.load(); auto played = played_.load();
if (buffer.size() < played + sample_count) if (buffer.size() < played + samples.size())
{ {
recorder_->request(sample_count); recorder_->request(samples.size());
buffer = recorder_->buffer(); buffer = recorder_->buffer();
} }
auto count = std::min<std::size_t>(sample_count, buffer.size() - played); auto count = std::min<std::size_t>(samples.size(), buffer.size() - played);
std::copy(buffer.data() + played, buffer.data() + played + count, data); std::copy(buffer.data() + played, buffer.data() + played + count, samples.begin());
played_.fetch_add(count); played_.fetch_add(count);
return count; return count;
} }

View file

@ -27,13 +27,13 @@ namespace psemek::audio
return stream_->length(); return stream_->length();
} }
std::size_t read(float * data, std::size_t sample_count) override std::size_t read(util::span<float> samples) override
{ {
auto result = stream_->read(data, sample_count); auto count = stream_->read(samples);
for (std::size_t i = 0; i < result; i += 2) for (std::size_t i = 0; i < count; i += 2)
{ {
float v = std::max(std::abs(data[i]), std::abs(data[i + 1])); float v = std::max(std::abs(samples[i]), std::abs(samples[i + 1]));
float multiplier = (v > envelope_) ? envelope_attack_multiplier_ : envelope_release_multiplier_; float multiplier = (v > envelope_) ? envelope_attack_multiplier_ : envelope_release_multiplier_;
@ -50,11 +50,11 @@ namespace psemek::audio
float gain = std::exp(log_gain); float gain = std::exp(log_gain);
data[i + 0] *= gain; samples[i + 0] *= gain;
data[i + 1] *= gain; samples[i + 1] *= gain;
} }
return result; return count;
} }
std::size_t played() const override std::size_t played() const override

View file

@ -32,14 +32,15 @@ namespace psemek::audio
return length_; return length_;
} }
std::size_t read(float * data, std::size_t sample_count) override std::size_t read(util::span<float> samples) override
{ {
std::size_t count = 0; std::size_t count = 0;
for (; index_ != streams_.size(); ++index_) for (; index_ != streams_.size(); ++index_)
{ {
count += streams_[index_]->read(data + count, sample_count - count); count += streams_[index_]->read(samples);
if (count == sample_count) samples.consume(count);
if (samples.empty())
break; break;
} }

View file

@ -32,17 +32,17 @@ namespace psemek::audio
return stream_->length(); return stream_->length();
} }
std::size_t read(float * data, std::size_t sample_count) override std::size_t read(util::span<float> samples) override
{ {
auto result = stream_->read(data, sample_count); auto count = stream_->read(samples);
float strength = strength_.load(); float strength = strength_.load();
for (std::size_t i = 0; i < result; ++i) for (std::size_t i = 0; i < count; ++i)
{ {
data[i] = std::tanh(strength * data[i]); samples[i] = std::tanh(strength * samples[i]);
} }
return result; return count;
} }
std::size_t played() const override std::size_t played() const override

View file

@ -28,30 +28,30 @@ namespace psemek::audio
return stream_->played(); return stream_->played();
} }
std::size_t read(float * data, std::size_t sample_count) override std::size_t read(util::span<float> samples) override
{ {
auto const result = stream_->read(data, sample_count); auto const count = stream_->read(samples);
std::fill(data, data + std::min<std::size_t>(result, start_.samples()), 0.f); std::fill(samples.begin(), samples.begin() + std::min<std::size_t>(count, start_.samples()), 0.f);
if (result <= start_.samples()) if (count <= start_.samples())
{ {
start_ -= result; start_ -= count;
} }
else else
{ {
for (std::size_t i = start_.samples(); i < result; i += 2) for (std::size_t i = start_.samples(); i < count; i += 2)
{ {
float m = static_cast<float>(std::min<std::size_t>(current_, length_.samples())) / length_.samples(); float m = static_cast<float>(std::min<std::size_t>(current_, length_.samples())) / length_.samples();
data[i + 0] *= m; samples[i + 0] *= m;
data[i + 1] *= m; samples[i + 1] *= m;
current_ += 2; current_ += 2;
} }
start_ = duration{}; start_ = duration{};
} }
return result; return count;
} }
private: private:

View file

@ -29,12 +29,12 @@ namespace psemek::audio
return stream_->played(); return stream_->played();
} }
std::size_t read(float * data, std::size_t sample_count) override std::size_t read(util::span<float> samples) override
{ {
if (current_ >= length_.samples()) if (current_ >= length_.samples())
return 0; return 0;
auto result = stream_->read(data, sample_count); auto result = stream_->read(samples);
if (result <= start_.samples()) if (result <= start_.samples())
{ {
@ -46,8 +46,8 @@ namespace psemek::audio
{ {
float m = static_cast<float>(length_.samples() - std::min<std::size_t>(current_, length_.samples())) / length_.samples(); float m = static_cast<float>(length_.samples() - std::min<std::size_t>(current_, length_.samples())) / length_.samples();
data[i + 0] *= m; samples[i + 0] *= m;
data[i + 1] *= m; samples[i + 1] *= m;
current_ += 2; current_ += 2;
} }

View file

@ -20,17 +20,17 @@ namespace psemek::audio
return stream_->length(); return stream_->length();
} }
std::size_t read(float * data, std::size_t sample_count) override std::size_t read(util::span<float> samples) override
{ {
std::size_t count = stream_->read(data, sample_count); std::size_t count = stream_->read(samples);
for (std::size_t i = 0; i < count; i += 2) for (std::size_t i = 0; i < count; i += 2)
{ {
std::swap(prev_[0], data[i + 0]); std::swap(prev_[0], samples[i + 0]);
std::swap(prev_[1], data[i + 1]); std::swap(prev_[1], samples[i + 1]);
data[i + 0] = prev_[0] * a0_ + data[i + 0] * a1_; samples[i + 0] = prev_[0] * a0_ + samples[i + 0] * a1_;
data[i + 1] = prev_[1] * a0_ + data[i + 1] * a1_; samples[i + 1] = prev_[1] * a0_ + samples[i + 1] * a1_;
} }
return count; return count;

View file

@ -23,21 +23,21 @@ namespace psemek::audio
return std::nullopt; return std::nullopt;
} }
std::size_t read(float * data, std::size_t sample_count) override std::size_t read(util::span<float> samples) override
{ {
std::size_t result = 0; std::size_t count = 0;
while (result < sample_count && (!count_ || repeated_ < *count_)) while (count < samples.size() && (!count_ || repeated_ < *count_))
{ {
auto need = sample_count - result; auto need = samples.size() - count;
auto count = stream_->read(data + result, need); auto scount = stream_->read({samples.begin() + count, need});
result += count; count += scount;
if (count < need) if (scount < need)
{ {
++repeated_; ++repeated_;
stream_ = dup_->stream(); stream_ = dup_->stream();
} }
} }
return result; return count;
} }
std::size_t played() const override std::size_t played() const override

View file

@ -34,42 +34,42 @@ namespace psemek::audio
return stream_->length(); return stream_->length();
} }
std::size_t read(float * data, std::size_t sample_count) override std::size_t read(util::span<float> samples) override
{ {
bool const paused = paused_.load(); bool const paused = paused_.load();
if (paused) if (paused)
{ {
auto result = stream_->read(data, std::min(sample_count, level_)); auto count = stream_->read(samples.prefix(std::min(samples.size(), level_)));
for (std::size_t i = 0; i < result; i += 2) for (std::size_t i = 0; i < count; i += 2)
{ {
float gain = static_cast<float>(level_) / length_.samples(); float gain = static_cast<float>(level_) / length_.samples();
data[i + 0] *= gain; samples[i + 0] *= gain;
data[i + 1] *= gain; samples[i + 1] *= gain;
level_ -= 2; level_ -= 2;
} }
std::fill(data + result, data + sample_count, 0.f); std::fill(samples.begin() + count, samples.end(), 0.f);
return sample_count; return samples.size();
} }
else else
{ {
auto result = stream_->read(data, sample_count); auto count = stream_->read(samples);
auto const max_level = static_cast<std::size_t>(length_.samples()); auto const max_level = static_cast<std::size_t>(length_.samples());
for (std::size_t i = 0; i < result; i += 2) for (std::size_t i = 0; i < count; i += 2)
{ {
float gain = static_cast<float>(level_) / max_level; float gain = static_cast<float>(level_) / max_level;
data[i + 0] *= gain; samples[i + 0] *= gain;
data[i + 1] *= gain; samples[i + 1] *= gain;
if (level_ < max_level) if (level_ < max_level)
level_ += 2; level_ += 2;
} }
return result; return count;
} }
} }

View file

@ -46,17 +46,17 @@ namespace psemek::audio
return std::nullopt; return std::nullopt;
} }
std::size_t read(float * data, std::size_t sample_count) override std::size_t read(util::span<float> samples) override
{ {
std::size_t result = 0; std::size_t count = 0;
while (result < sample_count) while (count < samples.size())
{ {
if (resampler_pos_ < resampler_.result().size()) if (resampler_pos_ < resampler_.result().size())
{ {
std::size_t size = std::min(sample_count - result, resampler_.result().size() - resampler_pos_); std::size_t size = std::min(samples.size() - count, resampler_.result().size() - resampler_pos_);
std::copy(resampler_.result().data() + resampler_pos_, resampler_.result().data() + resampler_pos_ + size, data + result); std::copy(resampler_.result().data() + resampler_pos_, resampler_.result().data() + resampler_pos_ + size, samples.begin() + count);
result += size; count += size;
resampler_pos_ += size; resampler_pos_ += size;
played_ += size; played_ += size;
} }
@ -64,18 +64,18 @@ namespace psemek::audio
{ {
resampler_pos_ = 0; resampler_pos_ = 0;
std::size_t request_size = std::max<std::size_t>(sample_count, std::ceil(resampler_.ratio() * sample_count / 2.f) * 2); std::size_t request_size = std::max<std::size_t>(samples.size(), std::ceil(resampler_.ratio() * samples.size() / 2.f) * 2);
source_buffer_.resize(request_size); source_buffer_.resize(request_size);
auto count = stream_->read(source_buffer_.data(), request_size); auto source_count = stream_->read({source_buffer_.data(), request_size});
if (count == 0) if (source_count == 0)
break; break;
resampler_.feed({source_buffer_.data(), source_buffer_.data() + count}); resampler_.feed({source_buffer_.data(), source_buffer_.data() + source_count});
} }
} }
return result; return count;
} }
std::size_t played() const override std::size_t played() const override

View file

@ -19,12 +19,11 @@ namespace psemek::audio
return length_.samples(); return length_.samples();
} }
std::size_t read(float * data, std::size_t sample_count) override std::size_t read(util::span<float> samples) override
{ {
auto played = stream_->played(); auto played = stream_->played();
auto result = std::min<std::size_t>(length_.samples() - played, sample_count); auto max_count = std::min<std::size_t>(length_.samples() - played, samples.size());
result = stream_->read(data, result); return stream_->read(samples.prefix(max_count));
return result;
} }
std::size_t played() const override std::size_t played() const override

View file

@ -31,11 +31,11 @@ namespace psemek::audio
return stream_->played(); return stream_->played();
} }
std::size_t read(float * data, std::size_t sample_count) override std::size_t read(util::span<float> samples) override
{ {
auto result = stream_->read(data, sample_count); auto count = stream_->read(samples);
base_.apply(data, result); base_.apply(samples.prefix(count));
return result; return count;
} }
private: private:
@ -69,11 +69,11 @@ namespace psemek::audio
return stream_->played(); return stream_->played();
} }
std::size_t read(float * data, std::size_t sample_count) override std::size_t read(util::span<float> samples) override
{ {
auto result = stream_->read(data, sample_count); auto count = stream_->read(samples);
base_.apply(data, result); base_.apply(samples.prefix(count));
return result; return count;
} }
private: private:

View file

@ -26,13 +26,12 @@ namespace psemek::audio
return multiplier_to_smoothness(old); return multiplier_to_smoothness(old);
} }
void volume_base::apply(float * data, std::size_t sample_count) void volume_base::apply(util::span<float> samples)
{ {
float gain[2] = {gain_[0].load(), gain_[1].load()}; float gain[2] = {gain_[0].load(), gain_[1].load()};
float smoothness_multiplier = smoothness_multiplier_.load(); float smoothness_multiplier = smoothness_multiplier_.load();
auto end = data + sample_count; for (auto p = samples.begin(); p < samples.end();)
for (auto p = data; p < end;)
{ {
*p++ *= real_gain_[0]; *p++ *= real_gain_[0];
*p++ *= real_gain_[1]; *p++ *= real_gain_[1];

View file

@ -77,7 +77,7 @@ namespace psemek::audio
std::size_t const size = len / 2; std::size_t const size = len / 2;
std::size_t read = 0; std::size_t read = 0;
if (output) if (output)
read = output->read(self->buffer.data(), size); read = output->read({self->buffer.data(), size});
std::fill(self->buffer.data() + read, self->buffer.data() + size, 0.f); std::fill(self->buffer.data() + read, self->buffer.data() + size, 0.f);
for (auto s : self->buffer) for (auto s : self->buffer)

View file

@ -17,7 +17,7 @@ namespace psemek::audio
{ {
channel_ptr add(stream_ptr stream) override; channel_ptr add(stream_ptr stream) override;
std::size_t read(float * data, std::size_t sample_count) override; std::size_t read(util::span<float> samples) override;
std::optional<std::size_t> length() const override std::optional<std::size_t> length() const override
{ {
@ -53,7 +53,7 @@ namespace psemek::audio
return result; return result;
} }
std::size_t mixer_impl::read(float * data, std::size_t sample_count) std::size_t mixer_impl::read(util::span<float> samples)
{ {
{ {
std::vector<channel_ptr> new_channels; std::vector<channel_ptr> new_channels;
@ -65,9 +65,9 @@ namespace psemek::audio
channels_.push_back(std::move(ch)); channels_.push_back(std::move(ch));
} }
std::fill(data, data + sample_count, 0.f); std::fill(samples.begin(), samples.end(), 0.f);
buffer_.resize(sample_count); buffer_.resize(samples.size());
for (auto & ch : channels_) for (auto & ch : channels_)
{ {
@ -75,17 +75,11 @@ namespace psemek::audio
if (!stream) if (!stream)
continue; continue;
auto read = stream->read(buffer_.data(), sample_count); auto read = stream->read(buffer_);
{ std::copy(buffer_.data(), buffer_.data() + read, samples.begin());
auto begin = buffer_.data();
auto end = begin + read;
auto dst = data;
for (; begin < end; )
*dst++ += *begin++;
}
if (read < sample_count) if (read < buffer_.size())
{ {
ch->stop(); ch->stop();
continue; continue;
@ -97,9 +91,9 @@ namespace psemek::audio
std::swap(channels_, alive_channels_); std::swap(channels_, alive_channels_);
alive_channels_.clear(); alive_channels_.clear();
played_.fetch_add(sample_count); played_.fetch_add(samples.size());
return sample_count; return samples.size();
} }
} }

View file

@ -40,9 +40,9 @@ namespace psemek::audio
samples_ = {storage_.data(), storage_.data() + samples_.size()}; samples_ = {storage_.data(), storage_.data() + samples_.size()};
} }
auto result = stream_->read(storage_.data() + samples_.size(), samples); auto count = stream_->read({storage_.data() + samples_.size(), samples});
samples_ = {storage_.data(), storage_.data() + samples_.size() + result}; samples_ = {storage_.data(), storage_.data() + samples_.size() + count};
return result; return count;
} }
util::span<float const> buffer() const override util::span<float const> buffer() const override

View file

@ -34,20 +34,20 @@ namespace psemek::audio
return std::min(left_->played(), right_->played()); return std::min(left_->played(), right_->played());
} }
std::size_t read(float * data, std::size_t sample_count) override std::size_t read(util::span<float> samples) override
{ {
if (buffer_.size() < sample_count) if (buffer_.size() < samples.size())
buffer_.resize(sample_count); buffer_.resize(samples.size());
auto right_result = right_->read(data, sample_count); auto right_result = right_->read(samples);
auto left_result = left_->read(buffer_.data(), sample_count); auto left_result = left_->read({buffer_.data(), samples.size()});
auto result = std::min(right_result, left_result); auto result = std::min(right_result, left_result);
{ {
auto begin = buffer_.data(); auto begin = buffer_.data();
auto end = buffer_.data() + result; auto end = buffer_.data() + result;
auto dst = data; auto dst = samples.data();
for (; begin < end; begin += 2, dst += 2) for (; begin < end; begin += 2, dst += 2)
*dst = *begin; *dst = *begin;
} }

View file

@ -43,19 +43,19 @@ namespace psemek::audio
return std::nullopt; return std::nullopt;
} }
std::size_t read(float * data, std::size_t sample_count) override std::size_t read(util::span<float> samples) override
{ {
auto input = reinterpret_cast<std::uint8_t const *>(data_->data.data()); auto input = reinterpret_cast<std::uint8_t const *>(data_->data.data());
std::size_t result = 0; std::size_t count = 0;
while (result < sample_count) while (count < samples.size())
{ {
if (resampler_pos_ < resampler_.result().size()) if (resampler_pos_ < resampler_.result().size())
{ {
std::size_t size = std::min(resampler_.result().size() - resampler_pos_, sample_count - result); std::size_t size = std::min(resampler_.result().size() - resampler_pos_, samples.size() - count);
std::copy(resampler_.result().data() + resampler_pos_, resampler_.result().data() + resampler_pos_ + size, data + result); std::copy(resampler_.result().data() + resampler_pos_, resampler_.result().data() + resampler_pos_ + size, samples.begin() + count);
resampler_pos_ += size; resampler_pos_ += size;
result += size; count += size;
} }
else else
{ {
@ -84,9 +84,9 @@ namespace psemek::audio
} }
} }
played_.fetch_add(result); played_.fetch_add(count);
return result; return count;
} }
std::size_t played() const override std::size_t played() const override

View file

@ -25,23 +25,23 @@ namespace psemek::audio
return std::nullopt; return std::nullopt;
} }
std::size_t read(float * data, std::size_t sample_count) override std::size_t read(util::span<float> samples) override
{ {
std::size_t const size = buffer_.size(); std::size_t const size = buffer_.size();
for (std::size_t i = 0; i < sample_count; i += 2) for (std::size_t i = 0; i < samples.size(); i += 2)
{ {
buffer_[buffer_pos_ + 0] = (buffer_[buffer_pos_ + 0] + buffer_[(buffer_pos_ + 2) % size + 0]) / 2.f; buffer_[buffer_pos_ + 0] = (buffer_[buffer_pos_ + 0] + buffer_[(buffer_pos_ + 2) % size + 0]) / 2.f;
buffer_[buffer_pos_ + 1] = (buffer_[buffer_pos_ + 1] + buffer_[(buffer_pos_ + 2) % size + 1]) / 2.f; buffer_[buffer_pos_ + 1] = (buffer_[buffer_pos_ + 1] + buffer_[(buffer_pos_ + 2) % size + 1]) / 2.f;
data[i + 0] = buffer_[buffer_pos_ + 0]; samples[i + 0] = buffer_[buffer_pos_ + 0];
data[i + 1] = buffer_[buffer_pos_ + 1]; samples[i + 1] = buffer_[buffer_pos_ + 1];
buffer_pos_ += 2; buffer_pos_ += 2;
buffer_pos_ %= size; buffer_pos_ %= size;
} }
return sample_count; return samples.size();
} }
std::size_t played() const override std::size_t played() const override