psemek/libs/audio/source/track_mp3.cpp
2022-10-08 17:42:03 +03:00

133 lines
2.8 KiB
C++

#include <psemek/audio/track.hpp>
#include <psemek/audio/minimp3/options.h>
#include <psemek/audio/minimp3/minimp3.h>
#include <atomic>
namespace psemek::audio
{
namespace
{
struct mp3_data_holder
{
std::vector<char> storage;
util::span<char const> data;
mp3_data_holder(std::vector<char> storage)
: storage(std::move(storage))
, data(this->storage)
{}
mp3_data_holder(util::span<char const> data)
: data(data)
{}
};
struct mp3_stream_impl
: stream
{
mp3_stream_impl(std::shared_ptr<mp3_data_holder> data)
: data_(std::move(data))
, buffer_(MINIMP3_MAX_SAMPLES_PER_FRAME)
{
mp3dec_init(&decoder_);
}
std::optional<std::size_t> length() const override
{
return std::nullopt;
}
std::size_t read(float * data, std::size_t sample_count) override
{
auto input = reinterpret_cast<std::uint8_t const *>(data_->data.data());
std::size_t result = 0;
while (result < sample_count)
{
if (buffer_pos_ < buffer_size_)
{
std::size_t size = std::min(buffer_size_ - buffer_pos_, sample_count - result);
std::copy(buffer_.data() + buffer_pos_, buffer_.data() + buffer_pos_ + size, data + result);
buffer_pos_ += size;
result += size;
}
else
{
mp3dec_frame_info_t frame_info;
buffer_size_ = mp3dec_decode_frame(&decoder_, input + read_bytes_, data_->data.size() - read_bytes_, buffer_.data(), &frame_info);
buffer_pos_ = 0;
read_bytes_ += frame_info.frame_bytes;
if (frame_info.channels == 1)
{
for (std::size_t i = buffer_size_ * 2; i > 0;)
{
i -= 2;
buffer_[i + 0] = buffer_[i / 2];
buffer_[i + 1] = buffer_[i / 2];
}
buffer_size_ *= 2;
}
if (frame_info.frame_bytes == 0)
break;
}
}
return result;
}
std::size_t played() const override
{
return played_.load();
}
private:
std::shared_ptr<mp3_data_holder> data_;
std::size_t read_bytes_{0};
mp3dec_t decoder_;
std::vector<float> buffer_;
std::size_t buffer_pos_{0};
std::size_t buffer_size_{0};
std::atomic<std::size_t> played_{0};
};
struct mp3_track_impl
: track
{
mp3_track_impl(std::shared_ptr<mp3_data_holder> data)
: data_(std::move(data))
{}
stream_ptr stream() const override
{
return std::make_shared<mp3_stream_impl>(data_);
}
std::optional<std::size_t> length() const override
{
return std::nullopt;
}
private:
std::shared_ptr<mp3_data_holder> data_;
};
}
track_ptr load_mp3(util::span<char const> data)
{
return std::make_shared<mp3_track_impl>(std::make_shared<mp3_data_holder>(data));
}
track_ptr load_mp3(std::vector<char> data)
{
return std::make_shared<mp3_track_impl>(std::make_shared<mp3_data_holder>(std::move(data)));
}
}