From 184a9e7e67542faccd6a054b5c4d50f2916cd0d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?kleines=20Filmr=C3=B6llchen?= Date: Fri, 25 Jun 2021 13:37:38 +0200 Subject: [PATCH] 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. --- Userland/Libraries/LibAudio/Buffer.cpp | 58 +++++++++++++++++++++-- Userland/Libraries/LibAudio/Buffer.h | 20 +++++--- Userland/Libraries/LibAudio/WavLoader.cpp | 32 ++----------- Userland/Libraries/LibAudio/WavLoader.h | 2 +- 4 files changed, 73 insertions(+), 39 deletions(-) diff --git a/Userland/Libraries/LibAudio/Buffer.cpp b/Userland/Libraries/LibAudio/Buffer.cpp index 86a51aa7ac..efe6945a5f 100644 --- a/Userland/Libraries/LibAudio/Buffer.cpp +++ b/Userland/Libraries/LibAudio/Buffer.cpp @@ -5,10 +5,10 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include "Buffer.h" #include #include #include -#include namespace Audio { @@ -44,7 +44,7 @@ i32 Buffer::allocate_id() } template -static void read_samples_from_stream(InputMemoryStream& stream, SampleReader read_sample, Vector& samples, ResampleHelper& resampler, int num_channels) +static void read_samples_from_stream(InputMemoryStream& stream, SampleReader read_sample, Vector& samples, ResampleHelper& 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::max(); } -RefPtr Buffer::from_pcm_data(ReadonlyBytes data, ResampleHelper& resampler, int num_channels, PcmSampleFormat sample_format) +RefPtr Buffer::from_pcm_data(ReadonlyBytes data, ResampleHelper& 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::from_pcm_stream(InputMemoryStream& stream, ResampleHelper& resampler, int num_channels, PcmSampleFormat sample_format, int num_samples) +RefPtr Buffer::from_pcm_stream(InputMemoryStream& stream, ResampleHelper& resampler, int num_channels, PcmSampleFormat sample_format, int num_samples) { Vector fdata; fdata.ensure_capacity(num_samples); @@ -166,4 +166,54 @@ RefPtr Buffer::from_pcm_stream(InputMemoryStream& stream, ResampleHelper return Buffer::create_with_samples(move(fdata)); } +template +ResampleHelper::ResampleHelper(double source, double target) + : m_ratio(source / target) +{ +} +template ResampleHelper::ResampleHelper(double, double); +template ResampleHelper::ResampleHelper(double, double); + +template +Vector ResampleHelper::resample(Vector to_resample) +{ + Vector 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 ResampleHelper::resample(Vector); +template Vector ResampleHelper::resample(Vector); + +template +void ResampleHelper::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::process_sample(i32, i32); +template void ResampleHelper::process_sample(double, double); + +template +bool ResampleHelper::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::read_sample(i32&, i32&); +template bool ResampleHelper::read_sample(double&, double&); + } diff --git a/Userland/Libraries/LibAudio/Buffer.h b/Userland/Libraries/LibAudio/Buffer.h index 88d0fa0c56..3619111384 100644 --- a/Userland/Libraries/LibAudio/Buffer.h +++ b/Userland/Libraries/LibAudio/Buffer.h @@ -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 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 resample(Vector 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 { public: - static RefPtr from_pcm_data(ReadonlyBytes data, ResampleHelper& resampler, int num_channels, PcmSampleFormat sample_format); - static RefPtr from_pcm_stream(InputMemoryStream& stream, ResampleHelper& resampler, int num_channels, PcmSampleFormat sample_format, int num_samples); + static RefPtr from_pcm_data(ReadonlyBytes data, ResampleHelper& resampler, int num_channels, PcmSampleFormat sample_format); + static RefPtr from_pcm_stream(InputMemoryStream& stream, ResampleHelper& resampler, int num_channels, PcmSampleFormat sample_format, int num_samples); static NonnullRefPtr create_with_samples(Vector&& samples) { return adopt_ref(*new Buffer(move(samples))); diff --git a/Userland/Libraries/LibAudio/WavLoader.cpp b/Userland/Libraries/LibAudio/WavLoader.cpp index 3353549e20..2734ae4109 100644 --- a/Userland/Libraries/LibAudio/WavLoader.cpp +++ b/Userland/Libraries/LibAudio/WavLoader.cpp @@ -5,11 +5,11 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include "WavLoader.h" +#include "Buffer.h" #include #include #include -#include -#include #include #include @@ -30,7 +30,7 @@ WavLoaderPlugin::WavLoaderPlugin(const StringView& path) if (!valid) return; - m_resampler = make(m_sample_rate, m_device_sample_rate); + m_resampler = make>(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(m_sample_rate, m_device_sample_rate); + m_resampler = make>(m_sample_rate, m_device_sample_rate); } RefPtr 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; -} - } diff --git a/Userland/Libraries/LibAudio/WavLoader.h b/Userland/Libraries/LibAudio/WavLoader.h index 4eaa4b9a4b..edef9ed1d3 100644 --- a/Userland/Libraries/LibAudio/WavLoader.h +++ b/Userland/Libraries/LibAudio/WavLoader.h @@ -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 m_resampler; + OwnPtr> m_resampler; u32 m_sample_rate { 0 }; u16 m_num_channels { 0 };