Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 |
| OLD | NEW |