LibAudio: Make ResampleHelper templated for different sample types

Previously, ResampleHelper was fixed on handling double's, which makes
it unsuitable for the upcoming FLAC loader that needs to resample
integers. For this reason, ResampleHelper is templated to support
theoretically any type of sample, though only the necessary i32 and
double are templated right now.

The ResampleHelper implementations are moved from WavLoader.cpp to
Buffer.cpp.

This also improves some imports in the WavLoader files.
This commit is contained in:
kleines Filmröllchen 2021-06-25 13:37:38 +02:00 committed by Ali Mohammad Pur
parent 2e00155275
commit 184a9e7e67
4 changed files with 73 additions and 39 deletions

View file

@ -5,10 +5,10 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "Buffer.h"
#include <AK/Atomic.h>
#include <AK/Debug.h>
#include <AK/String.h>
#include <LibAudio/Buffer.h>
namespace Audio {
@ -44,7 +44,7 @@ i32 Buffer::allocate_id()
}
template<typename SampleReader>
static void read_samples_from_stream(InputMemoryStream& stream, SampleReader read_sample, Vector<Frame>& samples, ResampleHelper& resampler, int num_channels)
static void read_samples_from_stream(InputMemoryStream& stream, SampleReader read_sample, Vector<Frame>& samples, ResampleHelper<double>& resampler, int num_channels)
{
double norm_l = 0;
double norm_r = 0;
@ -127,13 +127,13 @@ static double read_norm_sample_8(InputMemoryStream& stream)
return double(sample) / NumericLimits<u8>::max();
}
RefPtr<Buffer> Buffer::from_pcm_data(ReadonlyBytes data, ResampleHelper& resampler, int num_channels, PcmSampleFormat sample_format)
RefPtr<Buffer> Buffer::from_pcm_data(ReadonlyBytes data, ResampleHelper<double>& resampler, int num_channels, PcmSampleFormat sample_format)
{
InputMemoryStream stream { data };
return from_pcm_stream(stream, resampler, num_channels, sample_format, data.size() / (pcm_bits_per_sample(sample_format) / 8));
}
RefPtr<Buffer> Buffer::from_pcm_stream(InputMemoryStream& stream, ResampleHelper& resampler, int num_channels, PcmSampleFormat sample_format, int num_samples)
RefPtr<Buffer> Buffer::from_pcm_stream(InputMemoryStream& stream, ResampleHelper<double>& resampler, int num_channels, PcmSampleFormat sample_format, int num_samples)
{
Vector<Frame> fdata;
fdata.ensure_capacity(num_samples);
@ -166,4 +166,54 @@ RefPtr<Buffer> Buffer::from_pcm_stream(InputMemoryStream& stream, ResampleHelper
return Buffer::create_with_samples(move(fdata));
}
template<typename SampleType>
ResampleHelper<SampleType>::ResampleHelper(double source, double target)
: m_ratio(source / target)
{
}
template ResampleHelper<i32>::ResampleHelper(double, double);
template ResampleHelper<double>::ResampleHelper(double, double);
template<typename SampleType>
Vector<SampleType> ResampleHelper<SampleType>::resample(Vector<SampleType> to_resample)
{
Vector<SampleType> resampled;
resampled.ensure_capacity(to_resample.size() * m_ratio);
for (auto sample : to_resample) {
process_sample(sample, sample);
while (read_sample(sample, sample))
resampled.unchecked_append(sample);
}
return resampled;
}
template Vector<i32> ResampleHelper<i32>::resample(Vector<i32>);
template Vector<double> ResampleHelper<double>::resample(Vector<double>);
template<typename SampleType>
void ResampleHelper<SampleType>::process_sample(SampleType sample_l, SampleType sample_r)
{
m_last_sample_l = sample_l;
m_last_sample_r = sample_r;
m_current_ratio += 1;
}
template void ResampleHelper<i32>::process_sample(i32, i32);
template void ResampleHelper<double>::process_sample(double, double);
template<typename SampleType>
bool ResampleHelper<SampleType>::read_sample(SampleType& next_l, SampleType& next_r)
{
if (m_current_ratio > 0) {
m_current_ratio -= m_ratio;
next_l = m_last_sample_l;
next_r = m_last_sample_r;
return true;
}
return false;
}
template bool ResampleHelper<i32>::read_sample(i32&, i32&);
template bool ResampleHelper<double>::read_sample(double&, double&);
}

View file

@ -88,25 +88,33 @@ String sample_format_name(PcmSampleFormat format);
// Small helper to resample from one playback rate to another
// This isn't really "smart", in that we just insert (or drop) samples.
// Should do better...
template<typename SampleType>
class ResampleHelper {
public:
ResampleHelper(double source, double target);
void process_sample(double sample_l, double sample_r);
bool read_sample(double& next_l, double& next_r);
// To be used as follows:
// while the resampler doesn't need a new sample, read_sample(current) and store the resulting samples.
// as long as the resampler needs a new sample, process_sample(current)
// Stores a new sample
void process_sample(SampleType sample_l, SampleType sample_r);
// Assigns the given sample to its correct value and returns false if there is a new sample required
bool read_sample(SampleType& next_l, SampleType& next_r);
Vector<SampleType> resample(Vector<SampleType> to_resample);
private:
const double m_ratio;
double m_current_ratio { 0 };
double m_last_sample_l { 0 };
double m_last_sample_r { 0 };
SampleType m_last_sample_l;
SampleType m_last_sample_r;
};
// A buffer of audio samples, normalized to 44100hz.
class Buffer : public RefCounted<Buffer> {
public:
static RefPtr<Buffer> from_pcm_data(ReadonlyBytes data, ResampleHelper& resampler, int num_channels, PcmSampleFormat sample_format);
static RefPtr<Buffer> from_pcm_stream(InputMemoryStream& stream, ResampleHelper& resampler, int num_channels, PcmSampleFormat sample_format, int num_samples);
static RefPtr<Buffer> from_pcm_data(ReadonlyBytes data, ResampleHelper<double>& resampler, int num_channels, PcmSampleFormat sample_format);
static RefPtr<Buffer> from_pcm_stream(InputMemoryStream& stream, ResampleHelper<double>& resampler, int num_channels, PcmSampleFormat sample_format, int num_samples);
static NonnullRefPtr<Buffer> create_with_samples(Vector<Frame>&& samples)
{
return adopt_ref(*new Buffer(move(samples)));

View file

@ -5,11 +5,11 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "WavLoader.h"
#include "Buffer.h"
#include <AK/Debug.h>
#include <AK/NumericLimits.h>
#include <AK/OwnPtr.h>
#include <LibAudio/Buffer.h>
#include <LibAudio/WavLoader.h>
#include <LibCore/File.h>
#include <LibCore/FileStream.h>
@ -30,7 +30,7 @@ WavLoaderPlugin::WavLoaderPlugin(const StringView& path)
if (!valid)
return;
m_resampler = make<ResampleHelper>(m_sample_rate, m_device_sample_rate);
m_resampler = make<ResampleHelper<double>>(m_sample_rate, m_device_sample_rate);
}
WavLoaderPlugin::WavLoaderPlugin(const ByteBuffer& buffer)
@ -46,7 +46,7 @@ WavLoaderPlugin::WavLoaderPlugin(const ByteBuffer& buffer)
if (!valid)
return;
m_resampler = make<ResampleHelper>(m_sample_rate, m_device_sample_rate);
m_resampler = make<ResampleHelper<double>>(m_sample_rate, m_device_sample_rate);
}
RefPtr<Buffer> WavLoaderPlugin::get_more_samples(size_t max_bytes_to_read_from_input)
@ -284,28 +284,4 @@ bool WavLoaderPlugin::parse_header()
return true;
}
ResampleHelper::ResampleHelper(double source, double target)
: m_ratio(source / target)
{
}
void ResampleHelper::process_sample(double sample_l, double sample_r)
{
m_last_sample_l = sample_l;
m_last_sample_r = sample_r;
m_current_ratio += 1;
}
bool ResampleHelper::read_sample(double& next_l, double& next_r)
{
if (m_current_ratio > 0) {
m_current_ratio -= m_ratio;
next_l = m_last_sample_l;
next_r = m_last_sample_r;
return true;
}
return false;
}
}

View file

@ -71,7 +71,7 @@ private:
//
// It would avoid duplicate resampling code and would allow clients
// to be agnostic of the destination audio device's sample rate.
OwnPtr<ResampleHelper> m_resampler;
OwnPtr<ResampleHelper<double>> m_resampler;
u32 m_sample_rate { 0 };
u16 m_num_channels { 0 };