/src/serenity/Userland/Libraries/LibAudio/Resampler.h
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2022, kleines Filmröllchen <filmroellchen@serenityos.org>. |
3 | | * |
4 | | * SPDX-License-Identifier: BSD-2-Clause |
5 | | */ |
6 | | |
7 | | #pragma once |
8 | | |
9 | | #include <AK/Concepts.h> |
10 | | #include <AK/Types.h> |
11 | | #include <AK/Vector.h> |
12 | | |
13 | | namespace Audio { |
14 | | |
15 | | // Small helper to resample from one playback rate to another |
16 | | // This isn't really "smart", in that we just insert (or drop) samples. |
17 | | // Should do better... |
18 | | template<typename SampleType> |
19 | | class ResampleHelper { |
20 | | public: |
21 | | ResampleHelper(u32 source, u32 target) |
22 | 63.9k | : m_source(source) |
23 | 63.9k | , m_target(target) |
24 | 63.9k | { |
25 | 63.9k | VERIFY(source > 0); |
26 | 63.9k | VERIFY(target > 0); |
27 | 63.9k | } |
28 | | |
29 | | // To be used as follows: |
30 | | // while the resampler doesn't need a new sample, read_sample(current) and store the resulting samples. |
31 | | // as long as the resampler needs a new sample, process_sample(current) |
32 | | |
33 | | // Stores a new sample |
34 | | void process_sample(SampleType sample_l, SampleType sample_r) |
35 | 185M | { |
36 | 185M | m_last_sample_l = sample_l; |
37 | 185M | m_last_sample_r = sample_r; |
38 | 185M | m_current_ratio += m_target; |
39 | 185M | } |
40 | | |
41 | | // Assigns the given sample to its correct value and returns false if there is a new sample required |
42 | | bool read_sample(SampleType& next_l, SampleType& next_r) |
43 | 192M | { |
44 | 192M | if (m_current_ratio >= m_source) { |
45 | 7.71M | m_current_ratio -= m_source; |
46 | 7.71M | next_l = m_last_sample_l; |
47 | 7.71M | next_r = m_last_sample_r; |
48 | 7.71M | return true; |
49 | 7.71M | } |
50 | | |
51 | 185M | return false; |
52 | 192M | } |
53 | | |
54 | | template<ArrayLike<SampleType> Samples> |
55 | | ErrorOr<Vector<SampleType>> try_resample(Samples&& to_resample) |
56 | 63.9k | { |
57 | 63.9k | Vector<SampleType> resampled; |
58 | 63.9k | TRY(try_resample_into_end(resampled, forward<Samples>(to_resample))); |
59 | 0 | return resampled; |
60 | 63.9k | } |
61 | | |
62 | | template<ArrayLike<SampleType> Samples, size_t vector_inline_capacity = 0> |
63 | | ErrorOr<void> try_resample_into_end(Vector<SampleType, vector_inline_capacity>& destination, Samples&& to_resample) |
64 | 63.9k | { |
65 | 63.9k | float ratio = (m_source > m_target) ? static_cast<float>(m_source) / m_target : static_cast<float>(m_target) / m_source; |
66 | 63.9k | TRY(destination.try_ensure_capacity(destination.size() + to_resample.size() * ratio)); |
67 | 185M | for (auto sample : to_resample) { |
68 | 185M | process_sample(sample, sample); |
69 | | |
70 | 192M | while (read_sample(sample, sample)) |
71 | 7.71M | destination.unchecked_append(sample); |
72 | 185M | } |
73 | 63.9k | return {}; |
74 | 63.9k | } |
75 | | |
76 | | template<ArrayLike<SampleType> Samples> |
77 | | Vector<SampleType> resample(Samples&& to_resample) |
78 | 63.9k | { |
79 | 63.9k | return MUST(try_resample(forward<Samples>(to_resample))); |
80 | 63.9k | } |
81 | | |
82 | | void reset() |
83 | | { |
84 | | m_current_ratio = 0; |
85 | | m_last_sample_l = {}; |
86 | | m_last_sample_r = {}; |
87 | | } |
88 | | |
89 | | u32 source() const { return m_source; } |
90 | | u32 target() const { return m_target; } |
91 | | |
92 | | private: |
93 | | u32 const m_source; |
94 | | u32 const m_target; |
95 | | u32 m_current_ratio { 0 }; |
96 | | SampleType m_last_sample_l {}; |
97 | | SampleType m_last_sample_r {}; |
98 | | }; |
99 | | |
100 | | } |