diff --git a/Userland/Libraries/LibAudio/Buffer.cpp b/Userland/Libraries/LibAudio/Buffer.cpp index d880ce22d7..1152a6ade3 100644 --- a/Userland/Libraries/LibAudio/Buffer.cpp +++ b/Userland/Libraries/LibAudio/Buffer.cpp @@ -13,31 +13,6 @@ namespace Audio { -u16 pcm_bits_per_sample(PcmSampleFormat format) -{ - switch (format) { - case Uint8: - return 8; - case Int16: - return 16; - case Int24: - return 24; - case Int32: - case Float32: - return 32; - case Float64: - return 64; - default: - VERIFY_NOT_REACHED(); - } -} - -String sample_format_name(PcmSampleFormat format) -{ - bool is_float = format == Float32 || format == Float64; - return String::formatted("PCM {}bit {}", pcm_bits_per_sample(format), is_float ? "Float" : "LE"); -} - i32 Buffer::allocate_id() { static Atomic next_id; @@ -161,83 +136,4 @@ ErrorOr> Buffer::from_pcm_stream(InputMemoryStream& stream return Buffer::create_with_samples(move(fdata)); } -template -ResampleHelper::ResampleHelper(u32 source, u32 target) - : m_source(source) - , m_target(target) -{ - VERIFY(source > 0); - VERIFY(target > 0); -} -template ResampleHelper::ResampleHelper(u32, u32); -template ResampleHelper::ResampleHelper(u32, u32); - -template -Vector ResampleHelper::resample(Vector to_resample) -{ - Vector resampled; - resampled.ensure_capacity(to_resample.size() * ceil_div(m_source, m_target)); - 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); - -ErrorOr> resample_buffer(ResampleHelper& resampler, Buffer const& to_resample) -{ - Vector resampled; - resampled.ensure_capacity(to_resample.sample_count() * ceil_div(resampler.source(), resampler.target())); - for (size_t i = 0; i < static_cast(to_resample.sample_count()); ++i) { - auto sample = to_resample.samples()[i]; - resampler.process_sample(sample.left, sample.right); - - while (resampler.read_sample(sample.left, sample.right)) - resampled.append(sample); - } - - return Buffer::create_with_samples(move(resampled)); -} - -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 += m_target; -} -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 >= m_source) { - m_current_ratio -= m_source; - 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&); - -template -void ResampleHelper::reset() -{ - m_current_ratio = 0; - m_last_sample_l = {}; - m_last_sample_r = {}; -} - -template void ResampleHelper::reset(); -template void ResampleHelper::reset(); - } diff --git a/Userland/Libraries/LibAudio/Buffer.h b/Userland/Libraries/LibAudio/Buffer.h index f1dcf64df8..d69090940f 100644 --- a/Userland/Libraries/LibAudio/Buffer.h +++ b/Userland/Libraries/LibAudio/Buffer.h @@ -17,58 +17,15 @@ #include #include #include +#include #include +#include #include #include namespace Audio { using namespace AK::Exponentials; -// Supported PCM sample formats. -enum PcmSampleFormat : u8 { - Uint8, - Int16, - Int24, - Int32, - Float32, - Float64, -}; - -// Most of the read code only cares about how many bits to read or write -u16 pcm_bits_per_sample(PcmSampleFormat format); -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(u32 source, u32 target); - - // 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); - - void reset(); - - u32 source() const { return m_source; } - u32 target() const { return m_target; } - -private: - const u32 m_source; - const u32 m_target; - u32 m_current_ratio { 0 }; - SampleType m_last_sample_l; - SampleType m_last_sample_r; -}; - // A buffer of audio samples. class Buffer : public RefCounted { public: diff --git a/Userland/Libraries/LibAudio/CMakeLists.txt b/Userland/Libraries/LibAudio/CMakeLists.txt index 89554a8091..1f6a863b1f 100644 --- a/Userland/Libraries/LibAudio/CMakeLists.txt +++ b/Userland/Libraries/LibAudio/CMakeLists.txt @@ -1,5 +1,7 @@ set(SOURCES Buffer.cpp + Resampler.cpp + SampleFormats.cpp ConnectionFromClient.cpp Loader.cpp WavLoader.cpp diff --git a/Userland/Libraries/LibAudio/Resampler.cpp b/Userland/Libraries/LibAudio/Resampler.cpp new file mode 100644 index 0000000000..35ed37c536 --- /dev/null +++ b/Userland/Libraries/LibAudio/Resampler.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2022, kleines Filmröllchen . + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "Resampler.h" +#include "Buffer.h" +#include "Sample.h" + +namespace Audio { + +ErrorOr> resample_buffer(ResampleHelper& resampler, Buffer const& to_resample) +{ + Vector resampled; + resampled.ensure_capacity(to_resample.sample_count() * ceil_div(resampler.source(), resampler.target())); + for (size_t i = 0; i < static_cast(to_resample.sample_count()); ++i) { + auto sample = to_resample.samples()[i]; + resampler.process_sample(sample.left, sample.right); + + while (resampler.read_sample(sample.left, sample.right)) + resampled.append(sample); + } + + return Buffer::create_with_samples(move(resampled)); +} + +} diff --git a/Userland/Libraries/LibAudio/Resampler.h b/Userland/Libraries/LibAudio/Resampler.h new file mode 100644 index 0000000000..718c9b3ac9 --- /dev/null +++ b/Userland/Libraries/LibAudio/Resampler.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2022, kleines Filmröllchen . + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +namespace Audio { + +// 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(u32 source, u32 target) + : m_source(source) + , m_target(target) + { + VERIFY(source > 0); + VERIFY(target > 0); + } + + // 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) + { + m_last_sample_l = sample_l; + m_last_sample_r = sample_r; + m_current_ratio += m_target; + } + + // 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) + { + if (m_current_ratio >= m_source) { + m_current_ratio -= m_source; + next_l = m_last_sample_l; + next_r = m_last_sample_r; + return true; + } + + return false; + } + + void reset() + { + m_current_ratio = 0; + m_last_sample_l = {}; + m_last_sample_r = {}; + } + + u32 source() const { return m_source; } + u32 target() const { return m_target; } + +private: + const u32 m_source; + const u32 m_target; + u32 m_current_ratio { 0 }; + SampleType m_last_sample_l; + SampleType m_last_sample_r; +}; + +class Buffer; +ErrorOr> resample_buffer(ResampleHelper& resampler, Buffer const& to_resample); + +} diff --git a/Userland/Libraries/LibAudio/SampleFormats.cpp b/Userland/Libraries/LibAudio/SampleFormats.cpp new file mode 100644 index 0000000000..740c24e58d --- /dev/null +++ b/Userland/Libraries/LibAudio/SampleFormats.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2022, kleines Filmröllchen . + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "SampleFormats.h" + +namespace Audio { + +u16 pcm_bits_per_sample(PcmSampleFormat format) +{ + switch (format) { + case Uint8: + return 8; + case Int16: + return 16; + case Int24: + return 24; + case Int32: + case Float32: + return 32; + case Float64: + return 64; + default: + VERIFY_NOT_REACHED(); + } +} + +String sample_format_name(PcmSampleFormat format) +{ + bool is_float = format == Float32 || format == Float64; + return String::formatted("PCM {}bit {}", pcm_bits_per_sample(format), is_float ? "Float" : "LE"); +} + +} diff --git a/Userland/Libraries/LibAudio/SampleFormats.h b/Userland/Libraries/LibAudio/SampleFormats.h new file mode 100644 index 0000000000..2990ee80b8 --- /dev/null +++ b/Userland/Libraries/LibAudio/SampleFormats.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2022, kleines Filmröllchen . + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +namespace Audio { + +// Supported PCM sample formats. +enum PcmSampleFormat : u8 { + Uint8, + Int16, + Int24, + Int32, + Float32, + Float64, +}; + +// Most of the read code only cares about how many bits to read or write +u16 pcm_bits_per_sample(PcmSampleFormat format); +String sample_format_name(PcmSampleFormat format); +}