Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(332)

Side by Side Diff: media/base/sinc_resampler.cc

Issue 10702050: Add SincResampler ported from WebKit. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Zero-Initialize Arrays + Check Max Error. Created 8 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 //
5 // Input buffer layout, dividing the total buffer into regions (r0 - r5):
6 //
7 // |----------------|-----------------------------------------|----------------|
8 //
9 // kBlockSize + kKernelSize / 2
10 // <--------------------------------------------------------->
11 // r0
12 //
13 // kKernelSize / 2 kKernelSize / 2 kKernelSize / 2 kKernelSize / 2
14 // <---------------> <---------------> <---------------> <--------------->
15 // r1 r2 r3 r4
16 //
17 // kBlockSize
18 // <--------------------------------------->
19 // r5
20 //
21 // The algorithm:
22 //
23 // 1) Consume input frames into r0 (r1 is zero-initialized).
24 // 2) Position kernel centered at start of r0 (r2) and generate output frames
25 // until kernel is centered at start of r4 or we've finished generating all
26 // the output frames.
27 // 3) Copy r3 to r1 and r4 to r2.
28 // 4) Consume input frames into r5 (zero-pad if we run out of input).
29 // 5) Goto (2) until all of input is consumed.
30 //
31 // Note: we're glossing over how the sub-sample handling works with
32 // |virtual_source_index_|, etc.
33 // MSVC++ requires this to be set before any other includes to get M_PI.
34 #define _USE_MATH_DEFINES
Chris Rogers 2012/07/02 20:38:14 Not sure if there's any cleaner way to do this? I
DaleCurtis 2012/07/03 00:36:34 Not that I've found, this is what I used in my oth
35
36 #include "media/base/sinc_resampler.h"
37
38 #include <cmath>
39
40 #include "base/logging.h"
41
42 namespace media {
43
44 // The kernel size can be adjusted for quality (higher is better).
45 static const int kKernelSize = 32;
Chris Rogers 2012/07/02 20:38:14 32 should be fine for now, but you could add a TOD
DaleCurtis 2012/07/03 00:36:34 Done.
46 // The kernel offset count is used for interpolation and is the number of
47 // sub-sample kernel shifts.
48 static const int kKernelOffsetCount = 32;
49
50 const int SincResampler::kBlockSize = 512;
51 const int SincResampler::kBufferSize = kBlockSize + kKernelSize;
52 COMPILE_ASSERT(kKernelSize % 2 == 0, kKernelSize_must_be_even);
53 COMPILE_ASSERT(
54 SincResampler::kBlockSize > kKernelSize, kBlockSize_must_be_gt_kKernelSize);
55
56 SincResampler::SincResampler(AudioSourceProvider* provider, double scale_factor)
57 : scale_factor_(scale_factor),
58 virtual_source_idx_(0),
59 buffer_primed_(false),
60 provider_(provider),
61 // TODO(dalecurtis): When we switch to AVX/SSE optimization, we'll need to
62 // allocate with 32-byte alignment and ensure they're sized % 32 bytes.
63 kernel_storage_(new float[kKernelSize * (kKernelOffsetCount + 1)]),
64 input_buffer_(new float[kBufferSize]) {
65 memset(kernel_storage_, 0,
66 sizeof(*kernel_storage_) * kKernelSize * (kKernelOffsetCount + 1));
67 memset(input_buffer_, 0, sizeof(*kernel_storage_) * kBufferSize);
68
69 InitializeKernel();
70 }
71
72 SincResampler::~SincResampler() {
73 delete[] kernel_storage_;
74 delete[] input_buffer_;
75 }
76
77 void SincResampler::InitializeKernel() {
78 // Blackman window parameters.
79 double alpha = 0.16;
80 double a0 = 0.5 * (1.0 - alpha);
81 double a1 = 0.5;
82 double a2 = 0.5 * alpha;
83
84 // |sinc_scale_factor| is basically the normalized cutoff frequency of the
85 // low-pass filter.
86 double sinc_scale_factor = scale_factor_ > 1.0 ? 1.0 / scale_factor_ : 1.0;
87
88 // The sinc function is an idealized brick-wall filter, but since we're
89 // windowing it the transition from pass to stop does not happen right away.
90 // So we should adjust the lowpass filter cutoff slightly downward to avoid
91 // some aliasing at the very high-end.
92 // TODO(crogers): this value is empirical and to be more exact should vary
93 // depending on kKernelSize.
94 sinc_scale_factor *= 0.9;
95
96 // Generates a set of windowed sinc() kernels.
97 // We generate a range of sub-sample offsets from 0.0 to 1.0.
98 for (int offset_idx = 0; offset_idx <= kKernelOffsetCount; ++offset_idx) {
99 double subsample_offset =
100 static_cast<double>(offset_idx) / kKernelOffsetCount;
101
102 for (int i = 0; i < kKernelSize; ++i) {
103 // Compute the sinc with offset.
104 // TODO(dalecurtis): Is M_PI precise enough here? It was using piDouble.
Chris Rogers 2012/07/02 20:38:14 I think it should be fine -- in any case the unit
DaleCurtis 2012/07/03 00:36:34 Done.
105 double s =
106 sinc_scale_factor * M_PI * (i - kKernelSize / 2 - subsample_offset);
107 double sinc = (!s ? 1.0 : sin(s) / s) * sinc_scale_factor;
108
109 // Compute Blackman window, matching the offset of the sinc().
110 double x = (i - subsample_offset) / kKernelSize;
111 double window = a0 - a1 * cos(2.0 * M_PI * x) + a2 * cos(4.0 * M_PI * x);
112
113 // Window the sinc() function and store at the correct offset.
114 kernel_storage_[i + offset_idx * kKernelSize] = sinc * window;
115 }
116 }
117 }
118
119 void SincResampler::Resample(float* destination, int number_of_frames) {
120 int remaining_frames = number_of_frames;
121
122 // Setup various region pointers in the buffer (see diagram above).
123 float* r0 = input_buffer_ + kKernelSize / 2;
124 float* r1 = input_buffer_;
125 float* r2 = r0;
126 float* r3 = r0 + kBlockSize - kKernelSize / 2;
127 float* r4 = r0 + kBlockSize;
128 float* r5 = r0 + kKernelSize / 2;
129
130 // Step (1) -- Prime the input buffer at the start of the input stream.
131 if (!buffer_primed_) {
132 provider_->ProvideInput(r0, kBlockSize + kKernelSize / 2);
133 buffer_primed_ = true;
134 }
135
136 // Step (2) -- Resample!
137 while (remaining_frames) {
138 while (virtual_source_idx_ < kBlockSize) {
139 // |virtual_source_idx_| lies in between two kernel offsets so figure out
140 // what they are.
141 int source_idx = static_cast<int>(virtual_source_idx_);
142 double subsample_remainder = virtual_source_idx_ - source_idx;
143
144 double virtual_offset_idx = subsample_remainder * kKernelOffsetCount;
145 int offset_idx = static_cast<int>(virtual_offset_idx);
146
147 float* k1 = kernel_storage_ + offset_idx * kKernelSize;
148 float* k2 = k1 + kKernelSize;
149
150 // Initialize input pointer based on quantized |virtual_source_idx_|.
151 float* input_ptr = r1 + source_idx;
152
153 // We'll compute "convolutions" for the two kernels which straddle
154 // |virtual_source_idx_|.
155 float sum1 = 0;
156 float sum2 = 0;
157
158 // Figure out how much to weight each kernel's "convolution".
159 double kernel_interpolation_factor = virtual_offset_idx - offset_idx;
160
161 // Generate a single output sample.
162 int n = kKernelSize;
163 float input;
164 // TODO(dalecurtis): For initial commit, I've ripped out all the SSE
165 // optimizations, these definitely need to go back in before release.
166 while (n--) {
167 input = *input_ptr++;
168 sum1 += input * *k1++;
169 sum2 += input * *k2++;
170 }
171
172 // Linearly interpolate the two "convolutions".
173 double result = (1.0 - kernel_interpolation_factor) * sum1
174 + kernel_interpolation_factor * sum2;
175
176 *destination++ = result;
177
178 // Advance the virtual index.
179 virtual_source_idx_ += scale_factor_;
180
181 if (!--remaining_frames)
182 return;
183 }
184
185 // Wrap back around to the start.
186 virtual_source_idx_ -= kBlockSize;
187
188 // Step (3) Copy r3 to r1 and r4 to r2.
189 // This wraps the last input frames back to the start of the buffer.
190 memcpy(r1, r3, sizeof(*input_buffer_) * (kKernelSize / 2));
191 memcpy(r2, r4, sizeof(*input_buffer_) * (kKernelSize / 2));
192
193 // Step (4)
194 // Refresh the buffer with more input.
195 provider_->ProvideInput(r5, kBlockSize);
196 }
197 }
198
199 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698