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

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: Comments! 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_idx_|, etc.
Chris Rogers 2012/07/09 21:51:42 I'd add an extra blank line here
DaleCurtis 2012/07/10 01:00:25 Done.
33 // MSVC++ requires this to be set before any other includes to get M_PI.
34 #define _USE_MATH_DEFINES
35
36 #include "media/base/sinc_resampler.h"
37
38 #include <cmath>
39
40 #include "base/logging.h"
41
42 namespace media {
43
44 COMPILE_ASSERT(SincResampler::kKernelSize % 2 == 0, kKernelSize_must_be_even);
45 COMPILE_ASSERT(SincResampler::kBlockSize > SincResampler::kKernelSize,
46 kBlockSize_must_be_gt_kKernelSize);
47
48 SincResampler::SincResampler(const ReadCB& read_cb, double io_sample_rate_ratio)
49 : io_sample_rate_ratio_(io_sample_rate_ratio),
50 virtual_source_idx_(0),
51 buffer_primed_(false),
52 read_cb_(read_cb),
53 // TODO(dalecurtis): When we switch to AVX/SSE optimization, we'll need to
54 // allocate with 32-byte alignment and ensure they're sized % 32 bytes.
55 kernel_storage_(new float[kKernelStorageSize]),
56 input_buffer_(new float[kBufferSize]) {
57 memset(kernel_storage_.get(), 0,
58 sizeof(*kernel_storage_.get()) * kKernelStorageSize);
59 memset(input_buffer_.get(), 0, sizeof(*input_buffer_.get()) * kBufferSize);
60
61 InitializeKernel();
62 }
63
64 SincResampler::~SincResampler() {}
65
66 void SincResampler::InitializeKernel() {
Ami GONE FROM CHROMIUM 2012/07/03 20:54:42 Any reason this needs to be a function separate fr
DaleCurtis 2012/07/10 01:00:25 Just clarity. It's a bit large to stuff into the
67 // Blackman window parameters.
68 double alpha = 0.16;
69 double a0 = 0.5 * (1.0 - alpha);
70 double a1 = 0.5;
71 double a2 = 0.5 * alpha;
72
73 // |sinc_scale_factor| is basically the normalized cutoff frequency of the
74 // low-pass filter.
75 double sinc_scale_factor =
76 io_sample_rate_ratio_ > 1.0 ? 1.0 / io_sample_rate_ratio_ : 1.0;
77
78 // The sinc function is an idealized brick-wall filter, but since we're
79 // windowing it the transition from pass to stop does not happen right away.
80 // So we should adjust the lowpass filter cutoff slightly downward to avoid
81 // some aliasing at the very high-end.
82 // TODO(crogers): this value is empirical and to be more exact should vary
83 // depending on kKernelSize.
84 sinc_scale_factor *= 0.9;
85
86 // Generates a set of windowed sinc() kernels.
87 // We generate a range of sub-sample offsets from 0.0 to 1.0.
88 for (int offset_idx = 0; offset_idx <= kKernelOffsetCount; ++offset_idx) {
Ami GONE FROM CHROMIUM 2012/07/03 20:54:42 FWIW this could be a double as well and avoid the
DaleCurtis 2012/07/10 01:00:25 Then it needs a static_cast<int> when indexing int
89 double subsample_offset =
90 static_cast<double>(offset_idx) / kKernelOffsetCount;
91
92 for (int i = 0; i < kKernelSize; ++i) {
93 // Compute the sinc with offset.
94 double s =
95 sinc_scale_factor * M_PI * (i - kKernelSize / 2 - subsample_offset);
96 double sinc = (!s ? 1.0 : sin(s) / s) * sinc_scale_factor;
97
98 // Compute Blackman window, matching the offset of the sinc().
99 double x = (i - subsample_offset) / kKernelSize;
100 double window = a0 - a1 * cos(2.0 * M_PI * x) + a2 * cos(4.0 * M_PI * x);
101
102 // Window the sinc() function and store at the correct offset.
103 kernel_storage_[i + offset_idx * kKernelSize] = sinc * window;
104 }
105 }
106 }
107
108 void SincResampler::Resample(float* destination, int frames) {
109 int remaining_frames = frames;
110
111 // Setup various region pointers in the buffer (see diagram above).
112 float* r0 = input_buffer_.get() + kKernelSize / 2;
113 float* r1 = input_buffer_.get();
114 float* r2 = r0;
115 float* r3 = r0 + kBlockSize - kKernelSize / 2;
116 float* r4 = r0 + kBlockSize;
117 float* r5 = r0 + kKernelSize / 2;
118
119 // Step (1) -- Prime the input buffer at the start of the input stream.
120 if (!buffer_primed_) {
121 read_cb_.Run(r0, kBlockSize + kKernelSize / 2);
Ami GONE FROM CHROMIUM 2012/07/03 20:54:42 How does this not violate your expectations in the
DaleCurtis 2012/07/10 01:00:25 remaining_frames will be 0 before the next call oc
122 buffer_primed_ = true;
123 }
124
125 // Step (2) -- Resample!
126 while (remaining_frames) {
127 while (virtual_source_idx_ < kBlockSize) {
128 // |virtual_source_idx_| lies in between two kernel offsets so figure out
129 // what they are.
130 int source_idx = static_cast<int>(virtual_source_idx_);
131 double subsample_remainder = virtual_source_idx_ - source_idx;
132
133 double virtual_offset_idx = subsample_remainder * kKernelOffsetCount;
134 int offset_idx = static_cast<int>(virtual_offset_idx);
135
136 float* k1 = kernel_storage_.get() + offset_idx * kKernelSize;
137 float* k2 = k1 + kKernelSize;
138
139 // Initialize input pointer based on quantized |virtual_source_idx_|.
140 float* input_ptr = r1 + source_idx;
141
142 // We'll compute "convolutions" for the two kernels which straddle
143 // |virtual_source_idx_|.
144 float sum1 = 0;
145 float sum2 = 0;
146
147 // Figure out how much to weight each kernel's "convolution".
148 double kernel_interpolation_factor = virtual_offset_idx - offset_idx;
149
150 // Generate a single output sample.
151 int n = kKernelSize;
152 float input;
153 // TODO(dalecurtis): For initial commit, I've ripped out all the SSE
154 // optimizations, these definitely need to go back in before release.
155 while (n--) {
156 input = *input_ptr++;
157 sum1 += input * *k1++;
158 sum2 += input * *k2++;
159 }
160
161 // Linearly interpolate the two "convolutions".
162 double result = (1.0 - kernel_interpolation_factor) * sum1
163 + kernel_interpolation_factor * sum2;
164
165 *destination++ = result;
166
167 // Advance the virtual index.
168 virtual_source_idx_ += io_sample_rate_ratio_;
169
170 if (!--remaining_frames)
171 return;
172 }
173
174 // Wrap back around to the start.
175 virtual_source_idx_ -= kBlockSize;
176
177 // Step (3) Copy r3 to r1 and r4 to r2.
178 // This wraps the last input frames back to the start of the buffer.
179 memcpy(r1, r3, sizeof(*input_buffer_.get()) * (kKernelSize / 2));
180 memcpy(r2, r4, sizeof(*input_buffer_.get()) * (kKernelSize / 2));
181
182 // Step (4)
183 // Refresh the buffer with more input.
184 read_cb_.Run(r5, kBlockSize);
185 }
186 }
187
188 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698