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/track.hpp>
|
||||||
|
#include <psemek/audio/resampler.hpp>
|
||||||
|
#include <psemek/audio/constants.hpp>
|
||||||
|
|
||||||
#include <psemek/audio/minimp3/options.h>
|
#include <psemek/audio/minimp3/options.h>
|
||||||
#include <psemek/audio/minimp3/minimp3.h>
|
#include <psemek/audio/minimp3/minimp3.h>
|
||||||
|
|
@ -31,7 +33,7 @@ namespace psemek::audio
|
||||||
{
|
{
|
||||||
mp3_stream_impl(std::shared_ptr<mp3_data_holder> data)
|
mp3_stream_impl(std::shared_ptr<mp3_data_holder> data)
|
||||||
: data_(std::move(data))
|
: data_(std::move(data))
|
||||||
, buffer_(MINIMP3_MAX_SAMPLES_PER_FRAME)
|
, source_buffer_(MINIMP3_MAX_SAMPLES_PER_FRAME)
|
||||||
{
|
{
|
||||||
mp3dec_init(&decoder_);
|
mp3dec_init(&decoder_);
|
||||||
}
|
}
|
||||||
|
|
@ -48,33 +50,35 @@ namespace psemek::audio
|
||||||
std::size_t result = 0;
|
std::size_t result = 0;
|
||||||
while (result < sample_count)
|
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::size_t size = std::min(resampler_.result().size() - resampler_pos_, sample_count - result);
|
||||||
std::copy(buffer_.data() + buffer_pos_, buffer_.data() + buffer_pos_ + size, data + result);
|
std::copy(resampler_.result().data() + resampler_pos_, resampler_.result().data() + resampler_pos_ + size, data + result);
|
||||||
buffer_pos_ += size;
|
resampler_pos_ += size;
|
||||||
result += size;
|
result += size;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mp3dec_frame_info_t frame_info;
|
mp3dec_frame_info_t frame_info;
|
||||||
buffer_size_ = mp3dec_decode_frame(&decoder_, input + read_bytes_, data_->data.size() - read_bytes_, buffer_.data(), &frame_info);
|
auto source_buffer_size = mp3dec_decode_frame(&decoder_, input + read_bytes_, data_->data.size() - read_bytes_, source_buffer_.data(), &frame_info);
|
||||||
buffer_pos_ = 0;
|
resampler_pos_ = 0;
|
||||||
read_bytes_ += frame_info.frame_bytes;
|
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)
|
if (frame_info.frame_bytes == 0)
|
||||||
break;
|
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};
|
std::size_t read_bytes_{0};
|
||||||
|
|
||||||
mp3dec_t decoder_;
|
mp3dec_t decoder_;
|
||||||
std::vector<float> buffer_;
|
std::vector<float> source_buffer_;
|
||||||
std::size_t buffer_pos_{0};
|
|
||||||
std::size_t buffer_size_{0};
|
resampler resampler_;
|
||||||
|
std::size_t resampler_pos_{0};
|
||||||
|
|
||||||
std::atomic<std::size_t> played_{0};
|
std::atomic<std::size_t> played_{0};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue