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

Side by Side Diff: media/filters/wsola_internals.cc

Issue 19111004: Upgrade AudioRendererAlgorithm to use WSOLA, (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: "Dale's and Marco's comments are addressed." Created 7 years, 4 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 2013 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 #include "media/filters/wsola_internals.h"
6
7 #include <algorithm>
8 #include <cmath>
9 #include <limits>
10
11 #include "base/logging.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "media/base/audio_bus.h"
14
15 namespace media {
16
17 namespace internal {
18
19 bool InInterval(int n, Interval q) {
20 return n >= q.first && n <= q.second;
21 }
22
23 float MultiChannelSimilarityMeasure(const float* dot_prod_a_b,
24 const float* energy_a,
25 const float* energy_b,
26 int channels) {
27 const float kEpsilon = 1e-12;
28 float similarity_measure = 0;
29 for (int n = 0; n < channels; ++n) {
30 similarity_measure += dot_prod_a_b[n] / sqrt(energy_a[n] * energy_b[n] +
31 kEpsilon);
32 }
33 return similarity_measure;
34 }
35
36 void MultiChannelDotProduct(const AudioBus* a,
37 int frame_offset_a,
38 const AudioBus* b,
39 int frame_offset_b,
40 int num_frames,
41 float* dot_product) {
42 DCHECK_EQ(a->channels(), b->channels());
43 DCHECK_GE(frame_offset_a, 0);
44 DCHECK_GE(frame_offset_b, 0);
45 DCHECK_LE(frame_offset_a + num_frames, a->frames());
46 DCHECK_LE(frame_offset_b + num_frames, b->frames());
47
48 memset(dot_product, 0, sizeof(*dot_product) * a->channels());
49 for (int k = 0; k < a->channels(); ++k) {
50 const float* ch_a = a->channel(k) + frame_offset_a;
51 const float* ch_b = b->channel(k) + frame_offset_b;
52 for (int n = 0; n < num_frames; ++n) {
53 dot_product[k] += *ch_a++ * *ch_b++;
54 }
55 }
56 }
57
58 void MultiChannelMovingBlockEnergies(const AudioBus* input,
59 int frames_per_block,
60 float* energy) {
61 int num_blocks = input->frames() - (frames_per_block - 1);
62 int channels = input->channels();
63
64 for (int k = 0; k < input->channels(); ++k) {
65 const float* input_channel = input->channel(k);
66
67 energy[k] = 0;
68
69 // First block of channel |k|.
70 for (int m = 0; m < frames_per_block; ++m) {
71 energy[k] += input_channel[m] * input_channel[m];
72 }
73
74 const float* slide_out = input_channel;
75 const float* slide_in = &input_channel[frames_per_block];
DaleCurtis 2013/08/13 21:11:04 just input_channel + frames_per_block ?
turaj 2013/08/16 22:13:56 Done.
76 for (int n = 1; n < num_blocks; ++n, ++slide_in, ++slide_out) {
77 energy[k + n * channels] = energy[k + (n - 1) * channels] - *slide_out *
78 *slide_out + *slide_in * *slide_in;
79 }
80 }
81 }
82
83 // Fit the curve f(x) = a * x^2 + b * x + c such that
84 // f(-1) = |y[0]|
85 // f(0) = |y[1]|
86 // f(1) = |y[2]|.
87 void CubicInterpolation(const float* y_values,
88 float* extremum,
89 float* extremum_value) {
90 float a = 0.5f * (y_values[2] + y_values[0]) - y_values[1];
91 float b = 0.5f * (y_values[2] - y_values[0]);
92 float c = y_values[1];
93
94 DCHECK_NE(a, 0);
95 *extremum = -b / (2.f * a);
96 *extremum_value = a * (*extremum) * (*extremum) + b * (*extremum) + c;
97 }
98
99 int DecimatedSearch(int decimation,
100 Interval exclude_interval,
101 const AudioBus* target_block,
102 const AudioBus* search_segment,
103 const float* energy_target_block,
104 const float* energy_candidate_blocks) {
105 int channels = search_segment->channels();
106 int block_size = target_block->frames();
107 int num_candidate_blocks = search_segment->frames() - (block_size - 1);
108 scoped_ptr<float[]> dot_prod(new float[channels]);
109 float similarity[3]; // Three elements for cubic interpolation.
110
111 int n = 0;
112 MultiChannelDotProduct(target_block, 0, search_segment, n, block_size,
113 dot_prod.get());
114 similarity[0] = MultiChannelSimilarityMeasure(
115 dot_prod.get(), energy_target_block,
116 &energy_candidate_blocks[n * channels], channels);
117
118 // Set the starting point as optimal point.
119 float best_similarity = similarity[0];
120 int optimal_index = 0;
121
122 n += decimation;
123 if (n >= num_candidate_blocks) {
124 return 0;
125 }
126
127 MultiChannelDotProduct(target_block, 0, search_segment, n, block_size,
128 dot_prod.get());
129 similarity[1] = MultiChannelSimilarityMeasure(
130 dot_prod.get(), energy_target_block,
131 &energy_candidate_blocks[n * channels], channels);
132
133 n += decimation;
134 if (n >= num_candidate_blocks) {
135 // We cannot do any more sampling. Compare these two values and return the
136 // optimal index.
137 return similarity[1] > similarity[0] ? decimation : 0;
138 }
139
140 for (; n < num_candidate_blocks; n += decimation) {
141 MultiChannelDotProduct(target_block, 0, search_segment, n, block_size,
142 dot_prod.get());
143
144 similarity[2] = MultiChannelSimilarityMeasure(
145 dot_prod.get(), energy_target_block,
146 &energy_candidate_blocks[n * channels], channels);
147
148 if ((similarity[1] > similarity[0] && similarity[1] >= similarity[2]) ||
149 (similarity[1] >= similarity[0] && similarity[1] > similarity[2])) {
150 // A local maximum is found. Do a cubic interpolation for a better
151 // estimate of candidate maximum.
152 float normalized_candidate_index;
153 float candidate_similarity;
154 CubicInterpolation(similarity, &normalized_candidate_index,
155 &candidate_similarity);
156
157 int candidate_index = n - decimation + static_cast<int>(
DaleCurtis 2013/08/13 21:11:04 no need for cast+floor
turaj 2013/08/16 22:13:56 Done.
DaleCurtis 2013/08/19 22:15:23 Integer value, so just use static_cast instead of
turaj 2013/08/21 01:01:19 Done.
158 floor(normalized_candidate_index * decimation + 0.5f));
159 if (candidate_similarity > best_similarity &&
160 !InInterval(candidate_index, exclude_interval)) {
161 optimal_index = candidate_index;
162 best_similarity = candidate_similarity;
163 }
164 } else if (n + decimation >= num_candidate_blocks &&
165 similarity[2] > best_similarity && !InInterval(n, exclude_interval)) {
DaleCurtis 2013/08/13 21:11:04 should align with (
turaj 2013/08/16 22:13:56 Done.
166 // If this is the end-point and has a better similarity-measure than
167 // optimal, then we accept it as optimal point.
168 optimal_index = n;
169 best_similarity = similarity[2];
170 }
171 memmove(similarity, &similarity[1], 2 * sizeof(*similarity));
DaleCurtis 2013/08/13 21:11:04 memcpy?
turaj 2013/08/16 22:13:56 I thought overlapping in any direction is undefine
172 }
173 return optimal_index;
174 }
175
176 int FullSearch(int lim_low,
177 int lim_high,
178 Interval exclude_interval,
179 const AudioBus* target_block,
180 const AudioBus* search_block,
181 const float* energy_target_block,
182 const float* energy_candidate_blocks) {
183 int channels = search_block->channels();
184 int block_size = target_block->frames();
185 scoped_ptr<float[]> dot_prod(new float[channels]);
186
187 float best_similarity = std::numeric_limits<float>::min();
188 int optimal_index = 0;
189
190 for (int n = lim_low; n <= lim_high; ++n) {
191 if (InInterval(n, exclude_interval)) {
192 continue;
193 }
194 MultiChannelDotProduct(target_block, 0, search_block, n, block_size,
195 dot_prod.get());
196
197 float similarity = MultiChannelSimilarityMeasure(
198 dot_prod.get(), energy_target_block,
199 &energy_candidate_blocks[n * channels], channels);
200
201 if (similarity > best_similarity) {
202 best_similarity = similarity;
203 optimal_index = n;
204 }
205 }
206
207 return optimal_index;
208 }
209
210 int OptimalIndex(const AudioBus* search_block,
211 const AudioBus* target_block,
212 Interval exclude_interval) {
213 int channels = search_block->channels();
214 DCHECK(channels == target_block->channels());
DaleCurtis 2013/08/13 21:11:04 DCHECK_EQ
turaj 2013/08/16 22:13:56 Done.
215 int target_size = target_block->frames();
216 int num_candidate_blocks = search_block->frames() - (target_size - 1);
217 const int kSearchDecimation = 5;
DaleCurtis 2013/08/13 21:11:04 Comment.
turaj 2013/08/16 22:13:56 Done.
218
219 scoped_ptr<float[]> energy_target_block(new float[channels]);
DaleCurtis 2013/08/13 21:11:04 Bummer about these allocations, but don't worry ab
turaj 2013/08/16 22:13:56 We can define local variables of a maximum expecte
DaleCurtis 2013/08/19 22:15:23 We should save that for a follow up optimization p
220 scoped_ptr<float[]> energy_candidate_blocks(
221 new float[channels * num_candidate_blocks]);
222
223 // Energy of all candid frames.
224 MultiChannelMovingBlockEnergies(search_block, target_size,
225 energy_candidate_blocks.get());
226
227 // Energy of target frame.
228 MultiChannelDotProduct(target_block, 0, target_block, 0,
229 target_size, energy_target_block.get());
230
231 int optimal_index = DecimatedSearch(kSearchDecimation,
DaleCurtis 2013/08/13 21:11:04 Have you run the performance numbers w/ and w/o de
turaj 2013/08/16 22:13:56 No I haven't, but I can do that. Computationally,
232 exclude_interval, target_block,
233 search_block, energy_target_block.get(),
234 energy_candidate_blocks.get());
235
236 int lim_low = std::max(0, optimal_index - kSearchDecimation);
237 int lim_high = std::min(num_candidate_blocks - 1,
238 optimal_index + kSearchDecimation);
239 return FullSearch(lim_low, lim_high, exclude_interval, target_block,
240 search_block, energy_target_block.get(),
241 energy_candidate_blocks.get());
242 }
243
244 void GetSymmetricHanningWindow(int window_length, float* window) {
245 const float kPi = 3.14159265f;
DaleCurtis 2013/08/13 21:11:04 use M_PI, see top of sinc_resampler_unittest.cc fo
turaj 2013/08/16 22:13:56 Done.
246 const float scale = 2.f * kPi / static_cast<float>(window_length);
DaleCurtis 2013/08/13 21:11:04 double
turaj 2013/08/16 22:13:56 Done.
247 for (int n = 0; n < window_length; ++n)
248 window[n] = 0.5 * (1 - cos(n * scale));
DaleCurtis 2013/08/13 21:11:04 1.0 since cos takes double.
turaj 2013/08/16 22:13:56 Can I use cosf()? just because |window| is float.
DaleCurtis 2013/08/19 22:15:23 Yeah that's probably better than double everywhere
turaj 2013/08/21 01:01:19 Then does it make sense to have const float kPi =
249 }
250
251 } // namespace internal
252
253 } // namespace media
254
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698