Change blimp README to use Markdown.
[chromium-blink-merge.git] / media / base / audio_shifter.cc
blob11b7b6fab8f8764fe82796b29acded40e8782c9a
1 // Copyright (c) 2014 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.
5 #include <algorithm>
6 #include <cmath>
8 #include "base/bind.h"
9 #include "media/base/audio_bus.h"
10 #include "media/base/audio_shifter.h"
12 namespace media {
14 // return true if x is between a and b.
15 static bool between(double x, double a, double b) {
16 if (b < a)
17 return b <= x && x <= a;
18 return a <= x && x <= b;
21 class ClockSmoother {
22 public:
23 explicit ClockSmoother(base::TimeDelta clock_accuracy) :
24 clock_accuracy_(clock_accuracy),
25 inaccuracy_delta_(clock_accuracy * 10) {
26 inaccuracies_.push_back(std::make_pair(inaccuracy_sum_, inaccuracy_delta_));
29 base::TimeTicks Smooth(base::TimeTicks t,
30 base::TimeDelta delta) {
31 base::TimeTicks ret = t;
32 if (!previous_.is_null()) {
33 base::TimeDelta actual_delta = t - previous_;
34 base::TimeDelta new_fraction_off = actual_delta - delta;
35 inaccuracy_sum_ += new_fraction_off;
36 inaccuracy_delta_ += actual_delta;
37 inaccuracies_.push_back(std::make_pair(new_fraction_off, actual_delta));
38 if (inaccuracies_.size() > 1000) {
39 inaccuracy_sum_ -= inaccuracies_.front().first;
40 inaccuracy_delta_ -= inaccuracies_.front().second;
41 inaccuracies_.pop_front();
43 // 0.01 means 1% faster than regular clock.
44 // -0.02 means 2% slower than regular clock.
45 double fraction_off = inaccuracy_sum_.InSecondsF() /
46 inaccuracy_delta_.InSecondsF();
48 double delta_seconds = delta.InSecondsF();
49 delta_seconds += delta_seconds * fraction_off;
50 base::TimeTicks expected = previous_ +
51 base::TimeDelta::FromSecondsD(delta_seconds);
52 base::TimeDelta diff = t - expected;
53 if (diff < clock_accuracy_ && diff > -clock_accuracy_) {
54 ret = t + diff / 1000;
57 previous_ = ret;
58 return ret;
61 // 1.01 means 1% faster than regular clock.
62 // -0.98 means 2% slower than regular clock.
63 double Rate() const {
64 return 1.0 + inaccuracy_sum_.InSecondsF() /
65 inaccuracy_delta_.InSecondsF();
68 private:
69 base::TimeDelta clock_accuracy_;
70 std::deque<std::pair<base::TimeDelta, base::TimeDelta> > inaccuracies_;
71 base::TimeDelta inaccuracy_sum_;
72 base::TimeDelta inaccuracy_delta_;
73 base::TimeTicks previous_;
76 AudioShifter::AudioQueueEntry::AudioQueueEntry(
77 base::TimeTicks target_playout_time_,
78 scoped_ptr<AudioBus> audio_) :
79 target_playout_time(target_playout_time_),
80 audio(audio_.release()) {
83 AudioShifter::AudioQueueEntry::~AudioQueueEntry() {}
85 AudioShifter::AudioShifter(base::TimeDelta max_buffer_size,
86 base::TimeDelta clock_accuracy,
87 base::TimeDelta adjustment_time,
88 size_t rate,
89 int channels) :
90 max_buffer_size_(max_buffer_size),
91 clock_accuracy_(clock_accuracy),
92 adjustment_time_(adjustment_time),
93 rate_(rate),
94 input_clock_smoother_(new ClockSmoother(clock_accuracy)),
95 output_clock_smoother_(new ClockSmoother(clock_accuracy)),
96 running_(false),
97 position_(0),
98 previous_requested_samples_(0),
99 resampler_(channels, 1.0, 96,
100 base::Bind(&AudioShifter::ResamplerCallback,
101 base::Unretained(this))),
102 current_ratio_(1.0) {
105 AudioShifter::~AudioShifter() {}
107 void AudioShifter::Push(scoped_ptr<AudioBus> input,
108 base::TimeTicks playout_time) {
109 if (!queue_.empty()) {
110 playout_time = input_clock_smoother_->Smooth(
111 playout_time,
112 base::TimeDelta::FromSeconds(queue_.back().audio->frames()) / rate_);
114 queue_.push_back(AudioQueueEntry(playout_time, input.Pass()));
115 while (!queue_.empty() &&
116 queue_.back().target_playout_time -
117 queue_.front().target_playout_time > max_buffer_size_) {
118 DVLOG(1) << "AudioShifter: Audio overflow!";
119 queue_.pop_front();
120 position_ = 0;
124 void AudioShifter::Pull(AudioBus* output,
125 base::TimeTicks playout_time) {
126 // Add the kernel size since we incur some internal delay in
127 // resampling. All resamplers incur some delay, and for the
128 // SincResampler (used by MultiChannelResampler), this is
129 // (currently) kKernalSize / 2 frames.
130 playout_time += base::TimeDelta::FromSeconds(
131 SincResampler::kKernelSize) / rate_ / 2;
132 playout_time = output_clock_smoother_->Smooth(
133 playout_time,
134 base::TimeDelta::FromSeconds(previous_requested_samples_) / rate_);
135 previous_requested_samples_ = output->frames();
137 base::TimeTicks stream_time;
138 base::TimeTicks buffer_end_time;
139 if (queue_.empty()) {
140 DCHECK_EQ(position_, 0UL);
141 stream_time = end_of_last_consumed_audiobus_;
142 buffer_end_time = end_of_last_consumed_audiobus_;
143 } else {
144 stream_time = queue_.front().target_playout_time;
145 buffer_end_time = queue_.back().target_playout_time;
147 stream_time += base::TimeDelta::FromSecondsD(
148 (position_ - resampler_.BufferedFrames()) / rate_);
150 if (!running_ &&
151 base::TimeDelta::FromSeconds(output->frames() * 2) / rate_ +
152 clock_accuracy_ > buffer_end_time - stream_time) {
153 // We're not running right now, and we don't really have enough data
154 // to satisfy output reliably. Wait.
155 Zero(output);
156 return;
158 if (playout_time < stream_time -
159 base::TimeDelta::FromSeconds(output->frames()) / rate_ / 2 -
160 (running_ ? clock_accuracy_ : base::TimeDelta())) {
161 // |playout_time| is too far before the earliest known audio sample.
162 Zero(output);
163 return;
166 if (buffer_end_time < playout_time) {
167 // If the "playout_time" is actually capture time, then
168 // the entire queue will be in the past. Since we cannot
169 // play audio in the past. We add one buffer size to the
170 // bias to avoid buffer underruns in the future.
171 if (bias_ == base::TimeDelta()) {
172 bias_ = playout_time - stream_time +
173 clock_accuracy_ +
174 base::TimeDelta::FromSeconds(output->frames()) / rate_;
176 stream_time += bias_;
177 } else {
178 // Normal case, some part of the queue is
179 // ahead of the scheduled playout time.
181 // Skip any data that is simply too old, if we have
182 // better data somewhere in the qeueue.
184 // Reset bias
185 bias_ = base::TimeDelta();
187 while (!queue_.empty() &&
188 playout_time - stream_time > clock_accuracy_) {
189 queue_.pop_front();
190 position_ = 0;
191 resampler_.Flush();
192 if (queue_.empty()) {
193 Zero(output);
194 return;
196 stream_time = queue_.front().target_playout_time;
200 running_ = true;
201 double steady_ratio = output_clock_smoother_->Rate() /
202 input_clock_smoother_->Rate();
203 double time_difference = (playout_time - stream_time).InSecondsF();
204 double adjustment_time = adjustment_time_.InSecondsF();
205 // This is the ratio we would need to get perfect sync after
206 // |adjustment_time| has passed.
207 double slow_ratio = steady_ratio + time_difference / adjustment_time;
208 slow_ratio = std::max(0.9, std::min(1.1, slow_ratio));
209 adjustment_time = output->frames() / static_cast<double>(rate_);
210 // This is ratio we we'd need get perfect sync at the end of the
211 // current output audiobus.
212 double fast_ratio = steady_ratio + time_difference / adjustment_time;
213 fast_ratio = std::max(0.9, std::min(1.1, fast_ratio));
215 // If the current ratio is somewhere between the slow and the fast
216 // ratio, then keep it. This means we don't have to recalculate the
217 // tables very often and also allows us to converge on good sync faster.
218 if (!between(current_ratio_, slow_ratio, fast_ratio)) {
219 // Check if the direction has changed.
220 if ((current_ratio_ < steady_ratio) == (slow_ratio < steady_ratio)) {
221 // Two possible scenarios:
222 // Either we're really close to perfect sync, but the current ratio
223 // would overshoot, or the current ratio is insufficient to get to
224 // perfect sync in the alloted time. Clamp.
225 double max_ratio = std::max(fast_ratio, slow_ratio);
226 double min_ratio = std::min(fast_ratio, slow_ratio);
227 current_ratio_ = std::min(max_ratio,
228 std::max(min_ratio, current_ratio_));
229 } else {
230 // The "direction" has changed. (From speed up to slow down or
231 // vice versa, so we just take the slow ratio.
232 current_ratio_ = slow_ratio;
234 resampler_.SetRatio(current_ratio_);
236 resampler_.Resample(output->frames(), output);
239 void AudioShifter::ResamplerCallback(int frame_delay, AudioBus* destination) {
240 // TODO(hubbe): Use frame_delay
241 int pos = 0;
242 while (pos < destination->frames() && !queue_.empty()) {
243 size_t to_copy = std::min<size_t>(
244 queue_.front().audio->frames() - position_,
245 destination->frames() - pos);
246 CHECK_GT(to_copy, 0UL);
247 queue_.front().audio->CopyPartialFramesTo(position_,
248 to_copy,
249 pos,
250 destination);
251 pos += to_copy;
252 position_ += to_copy;
253 if (position_ >= static_cast<size_t>(queue_.front().audio->frames())) {
254 end_of_last_consumed_audiobus_ = queue_.front().target_playout_time +
255 base::TimeDelta::FromSeconds(queue_.front().audio->frames()) / rate_;
256 position_ -= queue_.front().audio->frames();
257 queue_.pop_front();
261 if (pos < destination->frames()) {
262 // Underflow
263 running_ = false;
264 position_ = 0;
265 previous_playout_time_ = base::TimeTicks();
266 bias_ = base::TimeDelta();
267 destination->ZeroFramesPartial(pos, destination->frames() - pos);
271 void AudioShifter::Flush() {
272 resampler_.Flush();
273 position_ = 0;
274 queue_.clear();
275 running_ = false;
276 previous_playout_time_ = base::TimeTicks();
277 bias_ = base::TimeDelta();
280 void AudioShifter::Zero(AudioBus* output) {
281 output->Zero();
282 running_ = false;
283 previous_playout_time_ = base::TimeTicks();
284 bias_ = base::TimeDelta();
287 } // namespace media