#include #include #include #include #include namespace psemek::audio { namespace { struct mixer_impl final : mixer , std::enable_shared_from_this { channel_ptr add(stream_ptr stream) override; std::size_t read(util::span samples) override; std::optional length() const override { return std::nullopt; } std::size_t played() const override { return played_.load(); } private: std::vector channels_; std::vector alive_channels_; std::vector buffer_; std::mutex new_channels_mutex_; std::vector new_channels_; std::atomic played_{0}; }; channel_ptr mixer_impl::add(stream_ptr stream) { auto result = std::make_shared(std::move(stream)); { std::lock_guard lock{new_channels_mutex_}; new_channels_.push_back(result); } return result; } std::size_t mixer_impl::read(util::span samples) { { std::vector new_channels; { std::lock_guard lock{new_channels_mutex_}; new_channels = std::move(new_channels_); } for (auto & ch : new_channels) channels_.push_back(std::move(ch)); } std::fill(samples.begin(), samples.end(), 0.f); buffer_.resize(samples.size()); for (auto & ch : channels_) { auto stream = ch->stream(); if (!stream) continue; auto read = stream->read(buffer_); for (auto src = buffer_.data(), dst = samples.begin(); src != buffer_.data() + read; ++src, ++dst) *dst += *src; if (read < buffer_.size()) { ch->stop(); continue; } alive_channels_.push_back(std::move(ch)); } std::swap(channels_, alive_channels_); alive_channels_.clear(); played_.fetch_add(samples.size()); return samples.size(); } } mixer_ptr make_mixer() { return std::make_shared(); } mixer_ptr mix(std::vector const & streams) { auto mixer = make_mixer(); for (auto const & stream : streams) mixer->add(stream); return mixer; } }