audio_power_monitor.cc revision ca12bfac764ba476d6cd062bf1dde12cc64c3f40
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/audio/audio_power_monitor.h"
6
7#include <algorithm>
8#include <cmath>
9
10#include "base/bind.h"
11#include "base/float_util.h"
12#include "base/logging.h"
13#include "base/message_loop/message_loop.h"
14#include "base/time/time.h"
15#include "media/base/audio_bus.h"
16
17namespace media {
18
19AudioPowerMonitor::AudioPowerMonitor(
20    int sample_rate,
21    const base::TimeDelta& time_constant,
22    const base::TimeDelta& measurement_period,
23    base::MessageLoop* message_loop,
24    const PowerMeasurementCallback& callback)
25    : sample_weight_(
26          1.0f - expf(-1.0f / (sample_rate * time_constant.InSecondsF()))),
27      num_frames_per_callback_(sample_rate * measurement_period.InSecondsF()),
28      message_loop_(message_loop),
29      power_level_callback_(callback),
30      average_power_(0.0f),
31      clipped_since_last_notification_(false),
32      frames_since_last_notification_(0),
33      last_reported_power_(-1.0f),
34      last_reported_clipped_(false) {
35  DCHECK(message_loop_);
36  DCHECK(!power_level_callback_.is_null());
37}
38
39AudioPowerMonitor::~AudioPowerMonitor() {
40}
41
42void AudioPowerMonitor::Scan(const AudioBus& buffer, int num_frames) {
43  DCHECK_LE(num_frames, buffer.frames());
44  const int num_channels = buffer.channels();
45  if (num_frames <= 0 || num_channels <= 0)
46    return;
47
48  // Calculate a new average power by applying a first-order low-pass filter
49  // over the audio samples in |buffer|.
50  //
51  // TODO(miu): Implement optimized SSE/NEON to more efficiently compute the
52  // results (in media/base/vector_math) in soon-upcoming change.
53  bool clipped = false;
54  float sum_power = 0.0f;
55  for (int i = 0; i < num_channels; ++i) {
56    float average_power_this_channel = average_power_;
57    const float* p = buffer.channel(i);
58    const float* const end_of_samples = p + num_frames;
59    for (; p < end_of_samples; ++p) {
60      const float sample = *p;
61      const float sample_squared = sample * sample;
62      clipped |= (sample_squared > 1.0f);
63      average_power_this_channel +=
64          (sample_squared - average_power_this_channel) * sample_weight_;
65    }
66    // If data in audio buffer is garbage, ignore its effect on the result.
67    if (base::IsNaN(average_power_this_channel))
68      average_power_this_channel = average_power_;
69    sum_power += average_power_this_channel;
70  }
71
72  // Update accumulated results.
73  average_power_ = std::max(0.0f, std::min(1.0f, sum_power / num_channels));
74  clipped_since_last_notification_ |= clipped;
75  frames_since_last_notification_ += num_frames;
76
77  // Once enough frames have been scanned, report the accumulated results.
78  if (frames_since_last_notification_ >= num_frames_per_callback_) {
79    // Note: Forgo making redundant callbacks when results remain unchanged.
80    // Part of this is to pin-down the power to zero if it is insignificantly
81    // small.
82    const float kInsignificantPower = 1.0e-10f;  // -100 dBFS
83    const float power =
84        (average_power_ < kInsignificantPower) ? 0.0f : average_power_;
85    if (power != last_reported_power_ ||
86        clipped_since_last_notification_ != last_reported_clipped_) {
87      const float power_dbfs = 10.0f * log10f(power);
88      // Try to post a task to run the callback with the dBFS result.  The
89      // posting of the task is guaranteed to be non-blocking, and therefore
90      // could fail.  However, in the common case, failures should be rare (and
91      // then the task-post will likely succeed the next time it's attempted).
92      if (!message_loop_->TryPostTask(
93              FROM_HERE,
94              base::Bind(power_level_callback_,
95                         power_dbfs, clipped_since_last_notification_))) {
96        DVLOG(2) << "TryPostTask() did not succeed.";
97        return;
98      }
99      last_reported_power_ = power;
100      last_reported_clipped_ = clipped_since_last_notification_;
101    }
102    clipped_since_last_notification_ = false;
103    frames_since_last_notification_ = 0;
104  }
105}
106
107}  // namespace media
108