Add frequency resampler & use it in mp3 decoder

This commit is contained in:
Nikita Lisitsa 2022-10-08 17:53:43 +03:00
parent e517e14986
commit e74f5957c0
3 changed files with 103 additions and 21 deletions

View file

@ -0,0 +1,30 @@
#pragma once
#include <psemek/util/span.hpp>
#include <vector>
namespace psemek::audio
{
struct resampler
{
// ratio is target frequency / source frequency
void feed(util::span<float const> samples, float ratio);
util::span<float const> result() const
{
return result_;
}
private:
std::vector<float> result_;
int position_{0};
float position_frac_{0.f};
float last_sample_[2] {0.f, 0.f};
void advance(float delta);
};
}

View file

@ -0,0 +1,46 @@
#include <psemek/audio/resampler.hpp>
#include <psemek/geom/math.hpp>
#include <vector>
namespace psemek::audio
{
void resampler::feed(util::span<float const> 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<int>(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<int>(std::floor(position_frac_));
position_ += floor;
position_frac_ -= floor;
}
}

View file

@ -1,4 +1,6 @@
#include <psemek/audio/track.hpp>
#include <psemek/audio/resampler.hpp>
#include <psemek/audio/constants.hpp>
#include <psemek/audio/minimp3/options.h>
#include <psemek/audio/minimp3/minimp3.h>
@ -31,7 +33,7 @@ namespace psemek::audio
{
mp3_stream_impl(std::shared_ptr<mp3_data_holder> 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<float>(frequency) / frame_info.hz);
}
}
@ -91,9 +95,11 @@ namespace psemek::audio
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::vector<float> source_buffer_;
resampler resampler_;
std::size_t resampler_pos_{0};
std::atomic<std::size_t> played_{0};
};