1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
media / base / sinc_resampler.h [blame]
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef MEDIA_BASE_SINC_RESAMPLER_H_
#define MEDIA_BASE_SINC_RESAMPLER_H_
#include <memory>
#include "base/functional/callback.h"
#include "base/gtest_prod_util.h"
#include "base/memory/aligned_memory.h"
#include "base/memory/raw_ptr.h"
#include "build/build_config.h"
#include "media/base/media_export.h"
namespace media {
// SincResampler is a high-quality single-channel sample-rate converter.
class MEDIA_EXPORT SincResampler {
public:
// The kernel size can be adjusted for quality (higher is better) at the
// expense of performance. Must be a multiple of 32. We aim for 64 for
// perceptible audio quality (see crbug.com/1407622), but fallback to 32 in
// cases where `request_frames_` is too small (e.g. 10ms of 8kHz audio).
// Use SincResampler::KernelSize() to check which size is being used.
static constexpr int kMaxKernelSize = 64;
static constexpr int kMinKernelSize = 32;
// Default request size. Affects how often and for how much SincResampler
// calls back for input. Must be greater than 1.5 * `kernel_size_`.
static constexpr int kDefaultRequestSize = 512;
// A smaller request size, which still allows higher quality resampling, by
// guaranteeing we will use kMaxKernelSize.
static constexpr int kSmallRequestSize = kMaxKernelSize * 2;
// The kernel offset count is used for interpolation and is the number of
// sub-sample kernel shifts. Can be adjusted for quality (higher is better)
// at the expense of allocating more memory.
static constexpr int kKernelOffsetCount = 32;
// Callback type for providing more data into the resampler. Expects |frames|
// of data to be rendered into |destination|; zero padded if not enough frames
// are available to satisfy the request.
using ReadCB = base::RepeatingCallback<void(int frames, float* destination)>;
// Returns the kernel size which will be used for a given `request_frames`.
static int KernelSizeFromRequestFrames(int request_frames);
// Constructs a SincResampler with the specified |read_cb|, which is used to
// acquire audio data for resampling. |io_sample_rate_ratio| is the ratio
// of input / output sample rates. |request_frames| controls the size in
// frames of the buffer requested by each |read_cb| call. The value must be
// greater than 1.5*`kernel_size_`. Specify kDefaultRequestSize if there are
// no request size constraints.
SincResampler(double io_sample_rate_ratio,
int request_frames,
const ReadCB read_cb);
SincResampler(const SincResampler&) = delete;
SincResampler& operator=(const SincResampler&) = delete;
~SincResampler();
// Resample |frames| of data from |read_cb_| into |destination|.
void Resample(int frames, float* destination);
// The maximum size in output frames that guarantees Resample() will only make
// a single call to |read_cb_| for more data. Note: If PrimeWithSilence() is
// not called, chunk size will grow after the first two Resample() calls by
// `kernel_size_` / (2 * io_sample_rate_ratio). See the .cc file for details.
int ChunkSize() const { return chunk_size_; }
// Returns the max number of frames that could be requested (via multiple
// calls to |read_cb_|) during one Resample(|output_frames_requested|) call.
int GetMaxInputFramesRequested(int output_frames_requested) const;
// Guarantees that ChunkSize() will not change between calls by initializing
// the input buffer with silence. Note, this will cause the first few samples
// of output to be biased towards silence. Must be called again after Flush().
void PrimeWithSilence();
// Flush all buffered data and reset internal indices. Not thread safe, do
// not call while Resample() is in progress. Note, if PrimeWithSilence() was
// previously called it must be called again after the Flush().
void Flush();
// Update |io_sample_rate_ratio_|. SetRatio() will cause a reconstruction of
// the kernels used for resampling. Not thread safe, do not call while
// Resample() is in progress.
void SetRatio(double io_sample_rate_ratio);
// Return number of input frames consumed by a callback but not yet processed.
// Since input/output ratio can be fractional, so can this value.
// Zero before first call to Resample().
double BufferedFrames() const;
// Return the actual kernel size used by the resampler. Should be
// kMaxKernelSize most of the time, but varies based on `request_frames_`;
int KernelSize() const;
float* get_kernel_for_testing() { return kernel_storage_.get(); }
int kernel_storage_size_for_testing() { return kernel_storage_size_; }
private:
FRIEND_TEST_ALL_PREFIXES(SincResamplerTest, Convolve);
FRIEND_TEST_ALL_PREFIXES(SincResamplerPerfTest, Convolve_unoptimized_aligned);
FRIEND_TEST_ALL_PREFIXES(SincResamplerPerfTest, Convolve_optimized_aligned);
FRIEND_TEST_ALL_PREFIXES(SincResamplerPerfTest, Convolve_optimized_unaligned);
void InitializeKernel();
void UpdateRegions(bool second_load);
// Compute convolution of |k1| and |k2| over |input_ptr|, resultant sums are
// linearly interpolated using |kernel_interpolation_factor|. On x86, the
// underlying implementation is chosen at run time based on SSE support. On
// ARM, NEON support is chosen at compile time based on compilation flags.
static float Convolve_C(const int kernel_size,
const float* input_ptr,
const float* k1,
const float* k2,
double kernel_interpolation_factor);
#if defined(ARCH_CPU_X86_FAMILY)
static float Convolve_SSE(const int kernel_size,
const float* input_ptr,
const float* k1,
const float* k2,
double kernel_interpolation_factor);
static float Convolve_AVX2(const int kernel_size,
const float* input_ptr,
const float* k1,
const float* k2,
double kernel_interpolation_factor);
#elif defined(ARCH_CPU_ARM_FAMILY) && defined(USE_NEON)
static float Convolve_NEON(const int kernel_size,
const float* input_ptr,
const float* k1,
const float* k2,
double kernel_interpolation_factor);
#endif
// Selects runtime specific CPU features like SSE. Must be called before
// using SincResampler.
void InitializeCPUSpecificFeatures();
const int kernel_size_;
const int kernel_storage_size_;
// The ratio of input / output sample rates.
double io_sample_rate_ratio_;
// An index on the source input buffer with sub-sample precision. It must be
// double precision to avoid drift.
double virtual_source_idx_;
// The buffer is primed once at the very beginning of processing.
bool buffer_primed_;
// Source of data for resampling.
const ReadCB read_cb_;
// The size (in samples) to request from each |read_cb_| execution.
const int request_frames_;
// The number of source frames processed per pass.
int block_size_;
// Cached value used for ChunkSize(). The maximum size in frames that
// guarantees Resample() will only ask for input at most once.
int chunk_size_;
// The size (in samples) of the internal buffer used by the resampler.
const int input_buffer_size_;
// Contains kKernelOffsetCount kernels back-to-back, each of size
// `kernel_size_`. The kernel offsets are sub-sample shifts of a windowed sinc
// shifted from 0.0 to 1.0 sample.
std::unique_ptr<float[], base::AlignedFreeDeleter> kernel_storage_;
std::unique_ptr<float[], base::AlignedFreeDeleter> kernel_pre_sinc_storage_;
std::unique_ptr<float[], base::AlignedFreeDeleter> kernel_window_storage_;
// Data from the source is copied into this buffer for each processing pass.
std::unique_ptr<float[], base::AlignedFreeDeleter> input_buffer_;
// Stores the runtime selection of which Convolve function to use.
using ConvolveProc =
float (*)(const int, const float*, const float*, const float*, double);
ConvolveProc convolve_proc_;
// Pointers to the various regions inside |input_buffer_|. See the diagram at
// the top of the .cc file for more information.
raw_ptr<float, AllowPtrArithmetic> r0_;
const raw_ptr<float, AllowPtrArithmetic> r1_;
const raw_ptr<float, AllowPtrArithmetic> r2_;
raw_ptr<float, AllowPtrArithmetic> r3_;
raw_ptr<float, AllowPtrArithmetic> r4_;
};
} // namespace media
#endif // MEDIA_BASE_SINC_RESAMPLER_H_