OLD | NEW |
---|---|
(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 | |
OLD | NEW |