mirror of
https://github.com/SerenityOS/serenity.git
synced 2025-01-24 02:12:09 -05:00
LibAudio: Resample with integer ratios instead of floats
Floating-point ratios are inherently imprecise, and can lead to unpredictable or nondeterministic behavior when resampling and expecting a certain number of resulting samples. Therefore, the resampler now uses integer ratios, with almost identical but fully predictable behavior. This also introduces the reset() function that the FLAC loader will use in the future.
This commit is contained in:
parent
6c9343e262
commit
d7ca60b998
2 changed files with 27 additions and 11 deletions
|
@ -8,6 +8,7 @@
|
||||||
#include "Buffer.h"
|
#include "Buffer.h"
|
||||||
#include <AK/Atomic.h>
|
#include <AK/Atomic.h>
|
||||||
#include <AK/Debug.h>
|
#include <AK/Debug.h>
|
||||||
|
#include <AK/StdLibExtras.h>
|
||||||
#include <AK/String.h>
|
#include <AK/String.h>
|
||||||
|
|
||||||
namespace Audio {
|
namespace Audio {
|
||||||
|
@ -167,18 +168,19 @@ RefPtr<Buffer> Buffer::from_pcm_stream(InputMemoryStream& stream, ResampleHelper
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename SampleType>
|
template<typename SampleType>
|
||||||
ResampleHelper<SampleType>::ResampleHelper(double source, double target)
|
ResampleHelper<SampleType>::ResampleHelper(u32 source, u32 target)
|
||||||
: m_ratio(source / target)
|
: m_source(source)
|
||||||
|
, m_target(target)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
template ResampleHelper<i32>::ResampleHelper(double, double);
|
template ResampleHelper<i32>::ResampleHelper(u32, u32);
|
||||||
template ResampleHelper<double>::ResampleHelper(double, double);
|
template ResampleHelper<double>::ResampleHelper(u32, u32);
|
||||||
|
|
||||||
template<typename SampleType>
|
template<typename SampleType>
|
||||||
Vector<SampleType> ResampleHelper<SampleType>::resample(Vector<SampleType> to_resample)
|
Vector<SampleType> ResampleHelper<SampleType>::resample(Vector<SampleType> to_resample)
|
||||||
{
|
{
|
||||||
Vector<SampleType> resampled;
|
Vector<SampleType> resampled;
|
||||||
resampled.ensure_capacity(to_resample.size() * m_ratio);
|
resampled.ensure_capacity(to_resample.size() * ceil_div(m_source, m_target));
|
||||||
for (auto sample : to_resample) {
|
for (auto sample : to_resample) {
|
||||||
process_sample(sample, sample);
|
process_sample(sample, sample);
|
||||||
|
|
||||||
|
@ -196,7 +198,7 @@ void ResampleHelper<SampleType>::process_sample(SampleType sample_l, SampleType
|
||||||
{
|
{
|
||||||
m_last_sample_l = sample_l;
|
m_last_sample_l = sample_l;
|
||||||
m_last_sample_r = sample_r;
|
m_last_sample_r = sample_r;
|
||||||
m_current_ratio += 1;
|
m_current_ratio += m_target;
|
||||||
}
|
}
|
||||||
template void ResampleHelper<i32>::process_sample(i32, i32);
|
template void ResampleHelper<i32>::process_sample(i32, i32);
|
||||||
template void ResampleHelper<double>::process_sample(double, double);
|
template void ResampleHelper<double>::process_sample(double, double);
|
||||||
|
@ -204,8 +206,8 @@ template void ResampleHelper<double>::process_sample(double, double);
|
||||||
template<typename SampleType>
|
template<typename SampleType>
|
||||||
bool ResampleHelper<SampleType>::read_sample(SampleType& next_l, SampleType& next_r)
|
bool ResampleHelper<SampleType>::read_sample(SampleType& next_l, SampleType& next_r)
|
||||||
{
|
{
|
||||||
if (m_current_ratio > 0) {
|
if (m_current_ratio >= m_source) {
|
||||||
m_current_ratio -= m_ratio;
|
m_current_ratio -= m_source;
|
||||||
next_l = m_last_sample_l;
|
next_l = m_last_sample_l;
|
||||||
next_r = m_last_sample_r;
|
next_r = m_last_sample_r;
|
||||||
return true;
|
return true;
|
||||||
|
@ -216,4 +218,15 @@ bool ResampleHelper<SampleType>::read_sample(SampleType& next_l, SampleType& nex
|
||||||
template bool ResampleHelper<i32>::read_sample(i32&, i32&);
|
template bool ResampleHelper<i32>::read_sample(i32&, i32&);
|
||||||
template bool ResampleHelper<double>::read_sample(double&, double&);
|
template bool ResampleHelper<double>::read_sample(double&, double&);
|
||||||
|
|
||||||
|
template<typename SampleType>
|
||||||
|
void ResampleHelper<SampleType>::reset()
|
||||||
|
{
|
||||||
|
m_current_ratio = 0;
|
||||||
|
m_last_sample_l = {};
|
||||||
|
m_last_sample_r = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
template void ResampleHelper<i32>::reset();
|
||||||
|
template void ResampleHelper<double>::reset();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,7 +91,7 @@ String sample_format_name(PcmSampleFormat format);
|
||||||
template<typename SampleType>
|
template<typename SampleType>
|
||||||
class ResampleHelper {
|
class ResampleHelper {
|
||||||
public:
|
public:
|
||||||
ResampleHelper(double source, double target);
|
ResampleHelper(u32 source, u32 target);
|
||||||
|
|
||||||
// To be used as follows:
|
// To be used as follows:
|
||||||
// while the resampler doesn't need a new sample, read_sample(current) and store the resulting samples.
|
// while the resampler doesn't need a new sample, read_sample(current) and store the resulting samples.
|
||||||
|
@ -103,9 +103,12 @@ public:
|
||||||
bool read_sample(SampleType& next_l, SampleType& next_r);
|
bool read_sample(SampleType& next_l, SampleType& next_r);
|
||||||
Vector<SampleType> resample(Vector<SampleType> to_resample);
|
Vector<SampleType> resample(Vector<SampleType> to_resample);
|
||||||
|
|
||||||
|
void reset();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const double m_ratio;
|
const u32 m_source;
|
||||||
double m_current_ratio { 0 };
|
const u32 m_target;
|
||||||
|
u32 m_current_ratio { 0 };
|
||||||
SampleType m_last_sample_l;
|
SampleType m_last_sample_l;
|
||||||
SampleType m_last_sample_r;
|
SampleType m_last_sample_r;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Reference in a new issue