118 lines
2.2 KiB
C++
118 lines
2.2 KiB
C++
#include <psemek/audio/mixer.hpp>
|
|
|
|
#include <memory>
|
|
#include <vector>
|
|
#include <atomic>
|
|
|
|
namespace psemek::audio
|
|
{
|
|
|
|
namespace
|
|
{
|
|
|
|
struct channel_impl final
|
|
: mixer::channel
|
|
{
|
|
stream_ptr stream;
|
|
|
|
void stop() override;
|
|
|
|
bool is_stopped() const override;
|
|
};
|
|
|
|
void channel_impl::stop()
|
|
{
|
|
std::atomic_store(&stream, stream_ptr());
|
|
}
|
|
|
|
bool channel_impl::is_stopped() const
|
|
{
|
|
return std::atomic_load(&stream) != nullptr;
|
|
}
|
|
|
|
struct mixer_impl final
|
|
: mixer
|
|
, std::enable_shared_from_this<mixer_impl>
|
|
{
|
|
channel_ptr add(stream_ptr stream) override;
|
|
|
|
std::size_t read(float * data, std::size_t sample_count) override;
|
|
|
|
private:
|
|
std::vector<std::shared_ptr<channel_impl>> channels_;
|
|
std::vector<std::shared_ptr<channel_impl>> alive_channels_;
|
|
|
|
std::vector<float> buffer_;
|
|
|
|
std::mutex new_channels_mutex_;
|
|
std::vector<std::shared_ptr<channel_impl>> new_channels_;
|
|
};
|
|
|
|
mixer::channel_ptr mixer_impl::add(stream_ptr stream)
|
|
{
|
|
auto result = std::make_shared<channel_impl>();
|
|
result->stream = std::move(stream);
|
|
|
|
{
|
|
std::lock_guard lock{new_channels_mutex_};
|
|
new_channels_.push_back(result);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
std::size_t mixer_impl::read(float * data, std::size_t sample_count)
|
|
{
|
|
{
|
|
std::vector<std::shared_ptr<channel_impl>> 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(data, data + sample_count, 0.f);
|
|
|
|
buffer_.resize(sample_count);
|
|
|
|
for (auto & ch : channels_)
|
|
{
|
|
auto stream = std::atomic_load(&(ch->stream));
|
|
if (!stream)
|
|
continue;
|
|
|
|
auto read = stream->read(buffer_.data(), sample_count);
|
|
|
|
{
|
|
auto begin = buffer_.data();
|
|
auto end = begin + read;
|
|
auto dst = data;
|
|
for (; begin < end; )
|
|
*dst++ += *begin++;
|
|
}
|
|
|
|
if (read < sample_count)
|
|
{
|
|
ch->stop();
|
|
continue;
|
|
}
|
|
|
|
alive_channels_.push_back(std::move(ch));
|
|
}
|
|
|
|
std::swap(channels_, alive_channels_);
|
|
alive_channels_.clear();
|
|
|
|
return sample_count;
|
|
}
|
|
|
|
}
|
|
|
|
mixer_ptr make_mixer()
|
|
{
|
|
return std::make_shared<mixer_impl>();
|
|
}
|
|
|
|
}
|