diff --git a/libs/audio/include/psemek/audio/resampler.hpp b/libs/audio/include/psemek/audio/resampler.hpp new file mode 100644 index 00000000..4985f561 --- /dev/null +++ b/libs/audio/include/psemek/audio/resampler.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include + +#include + +namespace psemek::audio +{ + + struct resampler + { + // ratio is target frequency / source frequency + void feed(util::span samples, float ratio); + + util::span result() const + { + return result_; + } + + private: + std::vector result_; + int position_{0}; + float position_frac_{0.f}; + + float last_sample_[2] {0.f, 0.f}; + + void advance(float delta); + }; + +} diff --git a/libs/audio/source/resampler.cpp b/libs/audio/source/resampler.cpp new file mode 100644 index 00000000..9b959619 --- /dev/null +++ b/libs/audio/source/resampler.cpp @@ -0,0 +1,46 @@ +#include +#include + +#include + +namespace psemek::audio +{ + + void resampler::feed(util::span samples, float ratio) + { + result_.clear(); + + if (samples.empty()) + return; + + float const delta = 1.f / ratio; + + while (position_ < 0) + { + result_.push_back(geom::lerp(last_sample_[0], samples[0], position_frac_)); + result_.push_back(geom::lerp(last_sample_[1], samples[1], position_frac_)); + advance(delta); + } + + while (2 * position_ + 3 < samples.size()) + { + result_.push_back(geom::lerp(samples[2 * position_ + 0], samples[2 * position_ + 2], position_frac_)); + result_.push_back(geom::lerp(samples[2 * position_ + 1], samples[2 * position_ + 3], position_frac_)); + advance(delta); + } + + position_ -= static_cast(samples.size()) / 2; + + last_sample_[0] = samples[samples.size() - 2]; + last_sample_[1] = samples[samples.size() - 1]; + } + + void resampler::advance(float delta) + { + position_frac_ += delta; + auto floor = static_cast(std::floor(position_frac_)); + position_ += floor; + position_frac_ -= floor; + } + +} diff --git a/libs/audio/source/track_mp3.cpp b/libs/audio/source/track_mp3.cpp index 886fa1fc..cdd96ebf 100644 --- a/libs/audio/source/track_mp3.cpp +++ b/libs/audio/source/track_mp3.cpp @@ -1,4 +1,6 @@ #include +#include +#include #include #include @@ -31,7 +33,7 @@ namespace psemek::audio { mp3_stream_impl(std::shared_ptr data) : data_(std::move(data)) - , buffer_(MINIMP3_MAX_SAMPLES_PER_FRAME) + , source_buffer_(MINIMP3_MAX_SAMPLES_PER_FRAME) { mp3dec_init(&decoder_); } @@ -48,33 +50,35 @@ namespace psemek::audio std::size_t result = 0; while (result < sample_count) { - if (buffer_pos_ < buffer_size_) + if (resampler_pos_ < resampler_.result().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; + std::size_t size = std::min(resampler_.result().size() - resampler_pos_, sample_count - result); + std::copy(resampler_.result().data() + resampler_pos_, resampler_.result().data() + resampler_pos_ + size, data + result); + resampler_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; + auto source_buffer_size = mp3dec_decode_frame(&decoder_, input + read_bytes_, data_->data.size() - read_bytes_, source_buffer_.data(), &frame_info); + resampler_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; + + if (frame_info.channels == 1) + { + for (std::size_t i = source_buffer_size * 2; i > 0;) + { + i -= 2; + source_buffer_[i + 0] = source_buffer_[i / 2]; + source_buffer_[i + 1] = source_buffer_[i / 2]; + } + source_buffer_size *= 2; + } + + resampler_.feed({source_buffer_.data(), source_buffer_.data() + source_buffer_size}, static_cast(frequency) / frame_info.hz); } } @@ -91,9 +95,11 @@ namespace psemek::audio std::size_t read_bytes_{0}; mp3dec_t decoder_; - std::vector buffer_; - std::size_t buffer_pos_{0}; - std::size_t buffer_size_{0}; + std::vector source_buffer_; + + resampler resampler_; + std::size_t resampler_pos_{0}; + std::atomic played_{0}; };