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

Side by Side Diff: media/filters/audio_renderer_algorithm.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
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "media/filters/audio_renderer_algorithm.h" 5 #include "media/filters/audio_renderer_algorithm.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <cmath> 8 #include <cmath>
9 9
10 #include "base/logging.h" 10 #include "base/logging.h"
11 #include "base/memory/scoped_ptr.h" 11 #include "base/memory/scoped_ptr.h"
12 #include "media/audio/audio_util.h" 12 #include "media/audio/audio_util.h"
13 #include "media/base/audio_buffer.h" 13 #include "media/base/audio_buffer.h"
14 #include "media/base/audio_bus.h" 14 #include "media/base/audio_bus.h"
15 #include "media/filters/wsola_internals.h"
15 16
16 namespace media { 17 namespace media {
17 18
18 // The starting size in frames for |audio_buffer_|. Previous usage maintained a
19 // queue of 16 AudioBuffers, each of 512 frames. This worked well, so we
20 // maintain this number of frames.
21 static const int kStartingBufferSizeInFrames = 16 * 512;
22
23 // The maximum size in frames for the |audio_buffer_|. Arbitrarily determined. 19 // The maximum size in frames for the |audio_buffer_|. Arbitrarily determined.
24 // This number represents 3 seconds of 96kHz/16 bit 7.1 surround sound. 20 // This number represents 3 seconds of 96kHz/16 bit 7.1 surround sound.
25 static const int kMaxBufferSizeInFrames = 3 * 96000; 21 static const int kMaxBufferSizeInFrames = 3 * 96000;
26 22
27 // Duration of audio segments used for crossfading (in seconds).
28 static const double kWindowDuration = 0.08;
29
30 // Duration of crossfade between audio segments (in seconds).
31 static const double kCrossfadeDuration = 0.008;
32
33 // Max/min supported playback rates for fast/slow audio. Audio outside of these 23 // Max/min supported playback rates for fast/slow audio. Audio outside of these
34 // ranges are muted. 24 // ranges are muted.
35 // Audio at these speeds would sound better under a frequency domain algorithm. 25 // Audio at these speeds would sound better under a frequency domain algorithm.
36 static const float kMinPlaybackRate = 0.5f; 26 static const float kMinPlaybackRate = 0.5f;
37 static const float kMaxPlaybackRate = 4.0f; 27 static const float kMaxPlaybackRate = 4.0f;
38 28
29 // Overlap-and-add window size in milliseconds.
30 static const int kOlaWindowSizeMs = 20;
31
32 // Size of search interval in milliseconds. The search interval is
33 // [-delta delta] around |output_index_| * |playback_rate_|. So the search
34 // interval is 2 * delta.
35 static const int kWsolaSearchIntervalMs = 30;
36
37 // The starting size in frames for |audio_buffer_|. Previous usage maintained a
38 // queue of 16 AudioBuffers, each of 512 frames. This worked well, so we
39 // maintain this number of frames.
40 static const int kStartingBufferSizeInFrames = 16 * 512;
41
39 AudioRendererAlgorithm::AudioRendererAlgorithm() 42 AudioRendererAlgorithm::AudioRendererAlgorithm()
40 : channels_(0), 43 : channels_(0),
41 samples_per_second_(0), 44 samples_per_second_(0),
42 playback_rate_(0), 45 playback_rate_(0),
43 frames_in_crossfade_(0),
44 index_into_window_(0),
45 crossfade_frame_number_(0),
46 muted_(false), 46 muted_(false),
47 muted_partial_frame_(0), 47 muted_partial_frame_(0),
48 window_size_(0), 48 capacity_(kStartingBufferSizeInFrames),
49 capacity_(kStartingBufferSizeInFrames) { 49 output_index_(0),
50 search_block_center_offset_(0),
51 num_candidate_blocks_(0),
52 target_block_index_(0),
53 ola_window_size_(0),
54 ola_hop_size_(0),
55 num_complete_frames_(0) {
50 } 56 }
51 57
52 AudioRendererAlgorithm::~AudioRendererAlgorithm() {} 58 AudioRendererAlgorithm::~AudioRendererAlgorithm() {}
53 59
54 void AudioRendererAlgorithm::Initialize(float initial_playback_rate, 60 void AudioRendererAlgorithm::Initialize(float initial_playback_rate,
55 const AudioParameters& params) { 61 const AudioParameters& params) {
56 CHECK(params.IsValid()); 62 CHECK(params.IsValid());
57 63
58 channels_ = params.channels(); 64 channels_ = params.channels();
59 samples_per_second_ = params.sample_rate(); 65 samples_per_second_ = params.sample_rate();
60 SetPlaybackRate(initial_playback_rate); 66 SetPlaybackRate(initial_playback_rate);
67 num_candidate_blocks_ = (kWsolaSearchIntervalMs * samples_per_second_) / 1000;
68 ola_window_size_ = kOlaWindowSizeMs * samples_per_second_ / 1000;
61 69
62 window_size_ = samples_per_second_ * kWindowDuration; 70 // Make sure window size in an even number.
63 frames_in_crossfade_ = samples_per_second_ * kCrossfadeDuration; 71 ola_window_size_ += ola_window_size_ & 1;
64 crossfade_buffer_ = AudioBus::Create(channels_, frames_in_crossfade_); 72 ola_hop_size_ = ola_window_size_ / 2;
73
74 // |num_candidate_blocks_| / 2 is the offset of the center of the search
75 // block to the center of the first (left most) candidate block. The offset
76 // of the center of a candidate block to its left most point is
77 // |ola_window_size_| / 2 - 1. Note that |ola_window_size_| is even and in
78 // our convention the center belongs to the left half, so we need to subtract
79 // one frame to get the correct offset.
80 //
81 // Search Block
82 // <------------------------------------------->
83 //
84 // |ola_window_size_| / 2 - 1
85 // <----
86 //
87 // |num_candidate_blocks_| / 2
88 // <----------------
89 // center
90 // X----X----------------X---------------X-----X
91 // <----------> <---------->
92 // Candidate ... Candidate
93 // 1, ... |num_candidate_blocks_|
94 search_block_center_offset_ = num_candidate_blocks_ / 2 +
95 (ola_window_size_ / 2 - 1);
96
97 ola_window_.reset(new float[ola_window_size_]);
98 internal::GetSymmetricHanningWindow(ola_window_size_, ola_window_.get());
99
100 transition_window_.reset(new float[ola_window_size_ * 2]);
101 internal::GetSymmetricHanningWindow(2 * ola_window_size_,
102 transition_window_.get());
103
104 wsola_output_ = AudioBus::Create(channels_, ola_window_size_ + ola_hop_size_);
105
106 // Auxiliary containers.
107 optimal_block_ = AudioBus::Create(channels_, ola_window_size_);
108 search_block_ = AudioBus::Create(
109 channels_, num_candidate_blocks_ + (ola_window_size_ - 1));
110 target_block_ = AudioBus::Create(channels_, ola_window_size_);
65 } 111 }
66 112
67 int AudioRendererAlgorithm::FillBuffer(AudioBus* dest, int requested_frames) { 113 int AudioRendererAlgorithm::FillBuffer(AudioBus* dest, int requested_frames) {
68 if (playback_rate_ == 0) 114 if (playback_rate_ == 0)
69 return 0; 115 return 0;
70 116
117 DCHECK_EQ(channels_, dest->channels());
118
71 // Optimize the |muted_| case to issue a single clear instead of performing 119 // Optimize the |muted_| case to issue a single clear instead of performing
72 // the full crossfade and clearing each crossfaded frame. 120 // the full crossfade and clearing each crossfaded frame.
73 if (muted_) { 121 if (muted_) {
74 int frames_to_render = 122 int frames_to_render =
75 std::min(static_cast<int>(audio_buffer_.frames() / playback_rate_), 123 std::min(static_cast<int>(audio_buffer_.frames() / playback_rate_),
76 requested_frames); 124 requested_frames);
77 125
78 // Compute accurate number of frames to actually skip in the source data. 126 // Compute accurate number of frames to actually skip in the source data.
79 // Includes the leftover partial frame from last request. However, we can 127 // Includes the leftover partial frame from last request. However, we can
80 // only skip over complete frames, so a partial frame may remain for next 128 // only skip over complete frames, so a partial frame may remain for next
81 // time. 129 // time.
82 muted_partial_frame_ += frames_to_render * playback_rate_; 130 muted_partial_frame_ += frames_to_render * playback_rate_;
83 int seek_frames = static_cast<int>(muted_partial_frame_); 131 int seek_frames = static_cast<int>(muted_partial_frame_);
84 dest->ZeroFrames(frames_to_render); 132 dest->ZeroFrames(frames_to_render);
85 audio_buffer_.SeekFrames(seek_frames); 133 audio_buffer_.SeekFrames(seek_frames);
86 134
87 // Determine the partial frame that remains to be skipped for next call. If 135 // Determine the partial frame that remains to be skipped for next call. If
88 // the user switches back to playing, it may be off time by this partial 136 // the user switches back to playing, it may be off time by this partial
89 // frame, which would be undetectable. If they subsequently switch to 137 // frame, which would be undetectable. If they subsequently switch to
90 // another playback rate that mutes, the code will attempt to line up the 138 // another playback rate that mutes, the code will attempt to line up the
91 // frames again. 139 // frames again.
92 muted_partial_frame_ -= seek_frames; 140 muted_partial_frame_ -= seek_frames;
93 return frames_to_render; 141 return frames_to_render;
94 } 142 }
95 143
96 int slower_step = ceil(window_size_ * playback_rate_); 144 int slower_step = ceil(ola_window_size_ * playback_rate_);
97 int faster_step = ceil(window_size_ / playback_rate_); 145 int faster_step = ceil(ola_window_size_ / playback_rate_);
98 146
99 // Optimize the most common |playback_rate_| ~= 1 case to use a single copy 147 // Optimize the most common |playback_rate_| ~= 1 case to use a single copy
100 // instead of copying frame by frame. 148 // instead of copying frame by frame.
101 if (window_size_ <= faster_step && slower_step >= window_size_) { 149 if (ola_window_size_ <= faster_step && slower_step >= ola_window_size_) {
102 const int frames_to_copy = 150 const int frames_to_copy =
103 std::min(audio_buffer_.frames(), requested_frames); 151 std::min(audio_buffer_.frames(), requested_frames);
104 const int frames_read = audio_buffer_.ReadFrames(frames_to_copy, 0, dest); 152 const int frames_read = audio_buffer_.ReadFrames(frames_to_copy, 0, dest);
105 DCHECK_EQ(frames_read, frames_to_copy); 153 DCHECK_EQ(frames_read, frames_to_copy);
106 return frames_read; 154 return frames_read;
107 } 155 }
108 156
109 int total_frames_rendered = 0; 157 int rendered_frames = 0;
110 while (total_frames_rendered < requested_frames) { 158 do {
111 if (index_into_window_ >= window_size_) 159 rendered_frames += WriteCompletedFramesTo(
112 ResetWindow(); 160 requested_frames - rendered_frames, rendered_frames, dest);
113 161 } while (rendered_frames < requested_frames && WsolaIteration());
114 int rendered_frames = 0; 162 return rendered_frames;
115 if (window_size_ > faster_step) {
116 rendered_frames =
117 OutputFasterPlayback(dest,
118 total_frames_rendered,
119 requested_frames - total_frames_rendered,
120 window_size_,
121 faster_step);
122 } else if (slower_step < window_size_) {
123 rendered_frames =
124 OutputSlowerPlayback(dest,
125 total_frames_rendered,
126 requested_frames - total_frames_rendered,
127 slower_step,
128 window_size_);
129 } else {
130 NOTREACHED();
131 }
132
133 if (rendered_frames == 0)
134 break;
135
136 total_frames_rendered += rendered_frames;
137 }
138 return total_frames_rendered;
139 }
140
141 void AudioRendererAlgorithm::ResetWindow() {
142 DCHECK_LE(index_into_window_, window_size_);
143 index_into_window_ = 0;
144 crossfade_frame_number_ = 0;
145 }
146
147 int AudioRendererAlgorithm::OutputFasterPlayback(AudioBus* dest,
148 int dest_offset,
149 int requested_frames,
150 int input_step,
151 int output_step) {
152 // Ensure we don't run into OOB read/write situation.
153 CHECK_GT(input_step, output_step);
154 DCHECK_LT(index_into_window_, window_size_);
155 DCHECK_GT(playback_rate_, 1.0);
156 DCHECK(!muted_);
157
158 if (audio_buffer_.frames() < 1)
159 return 0;
160
161 // The audio data is output in a series of windows. For sped-up playback,
162 // the window is comprised of the following phases:
163 //
164 // a) Output raw data.
165 // b) Save bytes for crossfade in |crossfade_buffer_|.
166 // c) Drop data.
167 // d) Output crossfaded audio leading up to the next window.
168 //
169 // The duration of each phase is computed below based on the |window_size_|
170 // and |playback_rate_|.
171 DCHECK_LE(frames_in_crossfade_, output_step);
172
173 // This is the index of the end of phase a, beginning of phase b.
174 int outtro_crossfade_begin = output_step - frames_in_crossfade_;
175
176 // This is the index of the end of phase b, beginning of phase c.
177 int outtro_crossfade_end = output_step;
178
179 // This is the index of the end of phase c, beginning of phase d.
180 // This phase continues until |index_into_window_| reaches |window_size_|, at
181 // which point the window restarts.
182 int intro_crossfade_begin = input_step - frames_in_crossfade_;
183
184 // a) Output raw frames if we haven't reached the crossfade section.
185 if (index_into_window_ < outtro_crossfade_begin) {
186 // Read as many frames as we can and return the count. If it's not enough,
187 // we will get called again.
188 const int frames_to_copy =
189 std::min(requested_frames, outtro_crossfade_begin - index_into_window_);
190 int copied = audio_buffer_.ReadFrames(frames_to_copy, dest_offset, dest);
191 index_into_window_ += copied;
192 return copied;
193 }
194
195 // b) Save outtro crossfade frames into intermediate buffer, but do not output
196 // anything to |dest|.
197 if (index_into_window_ < outtro_crossfade_end) {
198 // This phase only applies if there are bytes to crossfade.
199 DCHECK_GT(frames_in_crossfade_, 0);
200 int crossfade_start = index_into_window_ - outtro_crossfade_begin;
201 int crossfade_count = outtro_crossfade_end - index_into_window_;
202 int copied = audio_buffer_.ReadFrames(
203 crossfade_count, crossfade_start, crossfade_buffer_.get());
204 index_into_window_ += copied;
205
206 // Did we get all the frames we need? If not, return and let subsequent
207 // calls try to get the rest.
208 if (copied != crossfade_count)
209 return 0;
210 }
211
212 // c) Drop frames until we reach the intro crossfade section.
213 if (index_into_window_ < intro_crossfade_begin) {
214 // Check if there is enough data to skip all the frames needed. If not,
215 // return 0 and let subsequent calls try to skip it all.
216 int seek_frames = intro_crossfade_begin - index_into_window_;
217 if (audio_buffer_.frames() < seek_frames)
218 return 0;
219 audio_buffer_.SeekFrames(seek_frames);
220
221 // We've dropped all the frames that need to be dropped.
222 index_into_window_ += seek_frames;
223 }
224
225 // d) Crossfade and output a frame, as long as we have data.
226 if (audio_buffer_.frames() < 1)
227 return 0;
228 DCHECK_GT(frames_in_crossfade_, 0);
229 DCHECK_LT(index_into_window_, window_size_);
230
231 int offset_into_buffer = index_into_window_ - intro_crossfade_begin;
232 int copied = audio_buffer_.ReadFrames(1, dest_offset, dest);
233 DCHECK_EQ(copied, 1);
234 CrossfadeFrame(crossfade_buffer_.get(),
235 offset_into_buffer,
236 dest,
237 dest_offset,
238 offset_into_buffer);
239 index_into_window_ += copied;
240 return copied;
241 }
242
243 int AudioRendererAlgorithm::OutputSlowerPlayback(AudioBus* dest,
244 int dest_offset,
245 int requested_frames,
246 int input_step,
247 int output_step) {
248 // Ensure we don't run into OOB read/write situation.
249 CHECK_LT(input_step, output_step);
250 DCHECK_LT(index_into_window_, window_size_);
251 DCHECK_LT(playback_rate_, 1.0);
252 DCHECK_NE(playback_rate_, 0);
253 DCHECK(!muted_);
254
255 if (audio_buffer_.frames() < 1)
256 return 0;
257
258 // The audio data is output in a series of windows. For slowed down playback,
259 // the window is comprised of the following phases:
260 //
261 // a) Output raw data.
262 // b) Output and save bytes for crossfade in |crossfade_buffer_|.
263 // c) Output* raw data.
264 // d) Output* crossfaded audio leading up to the next window.
265 //
266 // * Phases c) and d) do not progress |audio_buffer_|'s cursor so that the
267 // |audio_buffer_|'s cursor is in the correct place for the next window.
268 //
269 // The duration of each phase is computed below based on the |window_size_|
270 // and |playback_rate_|.
271 DCHECK_LE(frames_in_crossfade_, input_step);
272
273 // This is the index of the end of phase a, beginning of phase b.
274 int intro_crossfade_begin = input_step - frames_in_crossfade_;
275
276 // This is the index of the end of phase b, beginning of phase c.
277 int intro_crossfade_end = input_step;
278
279 // This is the index of the end of phase c, beginning of phase d.
280 // This phase continues until |index_into_window_| reaches |window_size_|, at
281 // which point the window restarts.
282 int outtro_crossfade_begin = output_step - frames_in_crossfade_;
283
284 // a) Output raw frames.
285 if (index_into_window_ < intro_crossfade_begin) {
286 // Read as many frames as we can and return the count. If it's not enough,
287 // we will get called again.
288 const int frames_to_copy =
289 std::min(requested_frames, intro_crossfade_begin - index_into_window_);
290 int copied = audio_buffer_.ReadFrames(frames_to_copy, dest_offset, dest);
291 index_into_window_ += copied;
292 return copied;
293 }
294
295 // b) Save the raw frames for the intro crossfade section, then copy the
296 // same frames to |dest|.
297 if (index_into_window_ < intro_crossfade_end) {
298 const int frames_to_copy =
299 std::min(requested_frames, intro_crossfade_end - index_into_window_);
300 int offset = index_into_window_ - intro_crossfade_begin;
301 int copied = audio_buffer_.ReadFrames(
302 frames_to_copy, offset, crossfade_buffer_.get());
303 crossfade_buffer_->CopyPartialFramesTo(offset, copied, dest_offset, dest);
304 index_into_window_ += copied;
305 return copied;
306 }
307
308 // c) Output a raw frame into |dest| without advancing the |audio_buffer_|
309 // cursor.
310 int audio_buffer_offset = index_into_window_ - intro_crossfade_end;
311 DCHECK_GE(audio_buffer_offset, 0);
312 if (audio_buffer_.frames() <= audio_buffer_offset)
313 return 0;
314 int copied =
315 audio_buffer_.PeekFrames(1, audio_buffer_offset, dest_offset, dest);
316 DCHECK_EQ(1, copied);
317
318 // d) Crossfade the next frame of |crossfade_buffer_| into |dest| if we've
319 // reached the outtro crossfade section of the window.
320 if (index_into_window_ >= outtro_crossfade_begin) {
321 int offset_into_crossfade_buffer =
322 index_into_window_ - outtro_crossfade_begin;
323 CrossfadeFrame(dest,
324 dest_offset,
325 crossfade_buffer_.get(),
326 offset_into_crossfade_buffer,
327 offset_into_crossfade_buffer);
328 }
329
330 index_into_window_ += copied;
331 return copied;
332 }
333
334 void AudioRendererAlgorithm::CrossfadeFrame(AudioBus* intro,
335 int intro_offset,
336 AudioBus* outtro,
337 int outtro_offset,
338 int fade_offset) {
339 float crossfade_ratio =
340 static_cast<float>(fade_offset) / frames_in_crossfade_;
341 for (int channel = 0; channel < channels_; ++channel) {
342 outtro->channel(channel)[outtro_offset] =
343 (1.0f - crossfade_ratio) * intro->channel(channel)[intro_offset] +
344 (crossfade_ratio) * outtro->channel(channel)[outtro_offset];
345 }
346 } 163 }
347 164
348 void AudioRendererAlgorithm::SetPlaybackRate(float new_rate) { 165 void AudioRendererAlgorithm::SetPlaybackRate(float new_rate) {
349 DCHECK_GE(new_rate, 0); 166 DCHECK_GE(new_rate, 0);
350 playback_rate_ = new_rate; 167 // Round it to two decimal digits.
DaleCurtis 2013/08/13 21:11:04 Is this really necessary? I don't really follow w
turaj 2013/08/16 22:13:56 I will rephrase the comment. Necessary in the sens
168 playback_rate_ = floor(new_rate * 100.f + 0.5f) / 100;
351 muted_ = 169 muted_ =
352 playback_rate_ < kMinPlaybackRate || playback_rate_ > kMaxPlaybackRate; 170 playback_rate_ < kMinPlaybackRate || playback_rate_ > kMaxPlaybackRate;
353
354 ResetWindow();
355 } 171 }
356 172
357 void AudioRendererAlgorithm::FlushBuffers() { 173 void AudioRendererAlgorithm::FlushBuffers() {
358 ResetWindow();
359
360 // Clear the queue of decoded packets (releasing the buffers). 174 // Clear the queue of decoded packets (releasing the buffers).
361 audio_buffer_.Clear(); 175 audio_buffer_.Clear();
176 output_index_ = 0;
177 target_block_index_ = 0;
178 wsola_output_->Zero();
179 num_complete_frames_ = 0;
362 } 180 }
363 181
364 base::TimeDelta AudioRendererAlgorithm::GetTime() { 182 base::TimeDelta AudioRendererAlgorithm::GetTime() {
365 return audio_buffer_.current_time(); 183 return audio_buffer_.current_time();
366 } 184 }
367 185
368 void AudioRendererAlgorithm::EnqueueBuffer( 186 void AudioRendererAlgorithm::EnqueueBuffer(
369 const scoped_refptr<AudioBuffer>& buffer_in) { 187 const scoped_refptr<AudioBuffer>& buffer_in) {
370 DCHECK(!buffer_in->end_of_stream()); 188 DCHECK(!buffer_in->end_of_stream());
371 audio_buffer_.Append(buffer_in); 189 audio_buffer_.Append(buffer_in);
372 } 190 }
373 191
374 bool AudioRendererAlgorithm::IsQueueFull() { 192 bool AudioRendererAlgorithm::IsQueueFull() {
375 return audio_buffer_.frames() >= capacity_; 193 return audio_buffer_.frames() >= capacity_;
376 } 194 }
377 195
378 void AudioRendererAlgorithm::IncreaseQueueCapacity() { 196 void AudioRendererAlgorithm::IncreaseQueueCapacity() {
379 capacity_ = std::min(2 * capacity_, kMaxBufferSizeInFrames); 197 capacity_ = std::min(2 * capacity_, kMaxBufferSizeInFrames);
380 } 198 }
381 199
200 bool AudioRendererAlgorithm::CanPerformWsola() const {
201 const int search_block_size = num_candidate_blocks_ + (ola_window_size_ - 1);
202 const int frames = audio_buffer_.frames();
203 if (target_block_index_ + ola_window_size_ <= frames &&
DaleCurtis 2013/08/13 21:11:04 Just return instead of if?
turaj 2013/08/16 22:13:56 Done.
204 GetSearchRegionIndex() + search_block_size <= frames) {
205 return true;
206 }
207 return false;
208 }
209
210 bool AudioRendererAlgorithm::WsolaIteration() {
211 if (!CanPerformWsola())
212 return false;
213
214 if (!GetOptimalBlock())
215 return false; // We cannot continue as |optimal_block| is not found.
216 // There was not enough data.
217
218 // Overlap-and-add.
219 for (int k = 0; k < channels_; ++k) {
220 float* ch_opt_frame = optimal_block_->channel(k);
DaleCurtis 2013/08/13 21:11:04 const float* const ?
turaj 2013/08/16 22:13:56 Done.
221 float* ch_output = wsola_output_->channel(k) + num_complete_frames_;
222 for (int n = 0; n < ola_hop_size_; ++n) {
223 ch_output[n] = ch_output[n] * ola_window_[ola_hop_size_ + n] +
224 ch_opt_frame[n] * ola_window_[n];
225 }
226
227 // Copy the second half to the output.
228 memcpy(&ch_output[ola_hop_size_], &ch_opt_frame[ola_hop_size_],
229 sizeof(*ch_opt_frame) * ola_hop_size_);
230 }
231
232 num_complete_frames_ += ola_hop_size_;
233 output_index_ += ola_hop_size_;
234
235 RemoveOldInputFrames();
236 return true;
237 }
238
239 int AudioRendererAlgorithm::GetSearchRegionIndex() const {
DaleCurtis 2013/08/13 21:11:04 You call this pretty frequently, it might be worth
turaj 2013/08/16 22:13:56 I did that, but it feels a bit unsafe as one has t
DaleCurtis 2013/08/19 22:15:23 Make this UpdateOutputIndex() and document on |out
turaj 2013/08/21 01:01:19 Done.
240 // Center of the search region, in frames.
241 const int search_block_center_index = static_cast<int>(floor(
DaleCurtis 2013/08/13 21:11:04 no need for floor + static_cast<int>, the cast is
turaj 2013/08/16 22:13:56 I thought without cast we get warning on Windows.
DaleCurtis 2013/08/19 22:15:23 Yes, you probably need the cast, but not cast and
242 output_index_ * playback_rate_ + 0.5));
243
244 // Index of the beginning of the search region, in frames.
245 return search_block_center_index - search_block_center_offset_;
246 }
247
248 void AudioRendererAlgorithm::RemoveOldInputFrames() {
DaleCurtis 2013/08/13 21:11:04 Instead of trying to calculate how many input fram
turaj 2013/08/16 22:13:56 what we need to know is the earliest frame we need
249 const int earliest_used_index = std::min(target_block_index_,
250 GetSearchRegionIndex());
251
252 if (earliest_used_index < 0)
253 return; // Nothing to remove
254
255 // Assuming |playback_rate_| * 100 == floor(|playback_rate_| * 100)
256 // that is |playback_rate_| is represented by 2 decimal digits, only.
257 // We eliminate blocks of size 100 * |playback_rate_| from input.
258 const int kOutputFramesPerBlock = 100;
259 const int input_frames_per_block =
260 static_cast<int>(floor(playback_rate_ * kOutputFramesPerBlock + 0.5f));
DaleCurtis 2013/08/13 21:11:04 Again, floor + cast is unnecessary.
turaj 2013/08/16 22:13:56 Done.
261 const int blocks_to_remove = earliest_used_index / input_frames_per_block;
DaleCurtis 2013/08/13 21:11:04 Is num >> den, such that integer division isn't lo
turaj 2013/08/16 22:13:56 we actually need the integer part of |earliest_use
262 const int input_frames_to_remove = input_frames_per_block * blocks_to_remove;
263
264 // Remove frames from input and adjust indices accordingly.
265 audio_buffer_.SeekFrames(input_frames_to_remove);
266 target_block_index_ -= input_frames_to_remove;
267
268 // Adjust output index.
269 output_index_ -= kOutputFramesPerBlock * blocks_to_remove;
270 DCHECK_GE(output_index_, 0);
271 }
272
273 int AudioRendererAlgorithm::WriteCompletedFramesTo(
274 int requested_frames, int dest_offset, AudioBus* dest) {
275 int rendered_frames = std::min(num_complete_frames_, requested_frames);
276
277 if (rendered_frames == 0)
278 return 0; // There is nothing to read from |wsola_output_|, return.
279
280 wsola_output_->CopyPartialFramesTo(0, rendered_frames, dest_offset, dest);
281
282 // Remove the frames which are read.
283 int frames_to_move = wsola_output_->frames() - rendered_frames;
DaleCurtis 2013/08/13 21:11:04 You should be able to use the Copy helpers in Audi
turaj 2013/08/16 22:13:56 CopyTo() uses memcpy() and I'm concerned about the
284 for (int k = 0; k < channels_; ++k) {
285 float* ch = wsola_output_->channel(k);
286 memmove(ch, &ch[rendered_frames], sizeof(*ch) * frames_to_move);
DaleCurtis 2013/08/13 21:11:04 Necessary vs memcpy? They don't seem to overlap?
turaj 2013/08/16 22:13:56 I'm not expecting to be zeroed, and there is no gu
287 }
288 num_complete_frames_ -= rendered_frames;
289 return rendered_frames;
290 }
291
292 bool AudioRendererAlgorithm::TargetIsWithinSearchRegion() const {
293 const int search_block_index = GetSearchRegionIndex();
294 const int search_block_size = num_candidate_blocks_ + (ola_window_size_ - 1);
295
296 if (target_block_index_ >= search_block_index &&
DaleCurtis 2013/08/13 21:11:04 Again, just return the value directly.
turaj 2013/08/16 22:13:56 Done.
297 target_block_index_ + ola_window_size_ <=
298 search_block_index + search_block_size) {
299 return true;
300 }
301 return false;
302 }
303
304 bool AudioRendererAlgorithm::GetOptimalBlock() {
305 int optimal_index = 0;
306 if (TargetIsWithinSearchRegion()) {
307 optimal_index = target_block_index_;
308 // Get the optimal window.
309 if (!PeekAudioWithZeroAppend(optimal_index, optimal_block_.get()))
310 return false;
311 } else {
312 if (!PeekAudioWithZeroAppend(target_block_index_, target_block_.get()))
313 return false;
314 const int search_block_index = GetSearchRegionIndex();
315
316 if (!PeekAudioWithZeroAppend(search_block_index, search_block_.get()))
317 return false;
318
319 int last_optimal = target_block_index_ - ola_hop_size_ -
320 search_block_index;
321 internal::Interval exclude_iterval = std::make_pair(last_optimal - 80,
DaleCurtis 2013/08/13 21:11:04 80? Extract to constant w/ documentation.
turaj 2013/08/16 22:13:56 Done.
322 last_optimal + 80);
323 // |optimal_index| is in frames and it is relative to the beginning
324 // of the |search_block_|.
325 optimal_index = internal::OptimalIndex(
326 search_block_.get(), target_block_.get(), exclude_iterval);
327
328 // Translate |index| w.r.t. the beginning of |audio_buffer_|.
329 optimal_index += search_block_index;
330
331 // Get the optimal window.
332 PeekAudioWithZeroAppend(optimal_index, optimal_block_.get());
333
334 // Make a transition from target block to the optimal block if different.
335 // Target block has the best continuation to the current output.
336 // Optimal block is the most similar block to the target, however, it might
337 // introduce some discontinuity when over-lap-added. Therefore, we combine
338 // them for a smoother transition. The length of transition window is twice
339 // as that of the optimal-block which makes it like a weighting function
340 // where target-block has higher weight close to zero (weight of 1 at index
341 // 0) and lower weight close the end.
342 for (int k = 0; k < channels_; ++k) {
343 float* ch_opt = optimal_block_->channel(k);
344 float* ch_target = target_block_->channel(k);
DaleCurtis 2013/08/13 21:11:04 const float* const ?
turaj 2013/08/16 22:13:56 Done.
345 for (int n = 0; n < ola_window_size_; ++n) {
346 ch_opt[n] = ch_opt[n] * transition_window_[n] + ch_target[n] *
347 transition_window_[ola_window_size_ + n];
348 }
349 }
350 }
351
352 // Next target is one hop ahead of the current optimal.
353 target_block_index_ = optimal_index + ola_hop_size_;
354 return true;
355 }
356
357 bool AudioRendererAlgorithm::PeekAudioWithZeroAppend(
DaleCurtis 2013/08/13 21:11:04 Technically this is prepending data, not appending
turaj 2013/08/16 22:13:56 Done.
358 int read_offset_frames, AudioBus* dest) {
359 int num_frames = dest->frames();
360 if (read_offset_frames + num_frames > audio_buffer_.frames())
DaleCurtis 2013/08/13 21:11:04 Should this be a CHECK() instead? Then this functi
turaj 2013/08/16 22:13:56 We can do that if you advise so. I was more along
DaleCurtis 2013/08/19 22:15:23 Chrome prefers to minimize potential paths through
turaj 2013/08/21 01:01:19 Sure. On 2013/08/19 22:15:23, DaleCurtis wrote:
361 return false;
362
363 int write_offset = 0;
364 int num_frames_to_read = dest->frames();
DaleCurtis 2013/08/13 21:11:04 You have two num_frames variables which do the sam
turaj 2013/08/16 22:13:56 Done.
365 if (read_offset_frames < 0) {
366 int num_zero_frames_appended = std::min(-read_offset_frames,
367 num_frames_to_read);
368 read_offset_frames = 0;
369 num_frames_to_read -= num_zero_frames_appended;
370 write_offset = num_zero_frames_appended;
371 dest->ZeroFrames(num_zero_frames_appended);
372 }
373 audio_buffer_.PeekFrames(num_frames_to_read, read_offset_frames,
DaleCurtis 2013/08/13 21:11:04 Should this return true if zero frames are peeked?
turaj 2013/08/16 22:13:56 According to the previous comments this function i
374 write_offset, dest);
375 return true;
376 }
377
382 } // namespace media 378 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698