Add frequency resampler & use it in mp3 decoder
This commit is contained in:
parent
e517e14986
commit
e74f5957c0
3 changed files with 103 additions and 21 deletions
30
libs/audio/include/psemek/audio/resampler.hpp
Normal file
30
libs/audio/include/psemek/audio/resampler.hpp
Normal 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);
|
||||
};
|
||||
|
||||
}
|
||||
46
libs/audio/source/resampler.cpp
Normal file
46
libs/audio/source/resampler.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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};
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue