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 "content/renderer/media/webaudiosourceprovider_impl.h"
6
7#include <vector>
8
9#include "base/bind.h"
10#include "base/callback_helpers.h"
11#include "base/logging.h"
12#include "media/base/bind_to_loop.h"
13#include "third_party/WebKit/public/platform/WebAudioSourceProviderClient.h"
14
15using blink::WebVector;
16
17namespace content {
18
19namespace {
20
21// Simple helper class for Try() locks.  Lock is Try()'d on construction and
22// must be checked via the locked() attribute.  If acquisition was successful
23// the lock will be released upon destruction.
24// TODO(dalecurtis): This should probably move to base/ if others start using
25// this pattern.
26class AutoTryLock {
27 public:
28  explicit AutoTryLock(base::Lock& lock)
29      : lock_(lock),
30        acquired_(lock_.Try()) {}
31
32  bool locked() const { return acquired_; }
33
34  ~AutoTryLock() {
35    if (acquired_) {
36      lock_.AssertAcquired();
37      lock_.Release();
38    }
39  }
40
41 private:
42  base::Lock& lock_;
43  const bool acquired_;
44  DISALLOW_COPY_AND_ASSIGN(AutoTryLock);
45};
46
47}  // namespace
48
49WebAudioSourceProviderImpl::WebAudioSourceProviderImpl(
50    const scoped_refptr<media::AudioRendererSink>& sink)
51    : weak_this_(this),
52      channels_(0),
53      sample_rate_(0),
54      volume_(1.0),
55      state_(kStopped),
56      renderer_(NULL),
57      client_(NULL),
58      sink_(sink) {
59}
60
61WebAudioSourceProviderImpl::~WebAudioSourceProviderImpl() {
62}
63
64void WebAudioSourceProviderImpl::setClient(
65    blink::WebAudioSourceProviderClient* client) {
66  base::AutoLock auto_lock(sink_lock_);
67  if (client && client != client_) {
68    // Detach the audio renderer from normal playback.
69    sink_->Stop();
70
71    // The client will now take control by calling provideInput() periodically.
72    client_ = client;
73
74    set_format_cb_ = media::BindToCurrentLoop(
75        base::Bind(&WebAudioSourceProviderImpl::OnSetFormat,
76                   weak_this_.GetWeakPtr()));
77
78    // If |renderer_| is set, then run |set_format_cb_| to send |client_|
79    // the current format info. If |renderer_| is not set, then |set_format_cb_|
80    // will get called when Initialize() is called.
81    // Note: Always using |set_format_cb_| ensures we have the same
82    // locking order when calling into |client_|.
83    if (renderer_)
84      base::ResetAndReturn(&set_format_cb_).Run();
85  } else if (!client && client_) {
86    // Restore normal playback.
87    client_ = NULL;
88    sink_->SetVolume(volume_);
89    if (state_ >= kStarted)
90      sink_->Start();
91    if (state_ >= kPlaying)
92      sink_->Play();
93  }
94}
95
96void WebAudioSourceProviderImpl::provideInput(
97    const WebVector<float*>& audio_data, size_t number_of_frames) {
98  if (!bus_wrapper_ ||
99      static_cast<size_t>(bus_wrapper_->channels()) != audio_data.size()) {
100    bus_wrapper_ = media::AudioBus::CreateWrapper(audio_data.size());
101  }
102
103  bus_wrapper_->set_frames(number_of_frames);
104  for (size_t i = 0; i < audio_data.size(); ++i)
105    bus_wrapper_->SetChannelData(i, audio_data[i]);
106
107  // Use a try lock to avoid contention in the real-time audio thread.
108  AutoTryLock auto_try_lock(sink_lock_);
109  if (!auto_try_lock.locked() || state_ != kPlaying) {
110    // Provide silence if we failed to acquire the lock or the source is not
111    // running.
112    bus_wrapper_->Zero();
113    return;
114  }
115
116  DCHECK(renderer_);
117  DCHECK(client_);
118  DCHECK_EQ(channels_, bus_wrapper_->channels());
119  renderer_->Render(bus_wrapper_.get(), 0);
120  bus_wrapper_->Scale(volume_);
121}
122
123void WebAudioSourceProviderImpl::Start() {
124  base::AutoLock auto_lock(sink_lock_);
125  DCHECK_EQ(state_, kStopped);
126  state_ = kStarted;
127  if (!client_)
128    sink_->Start();
129}
130
131void WebAudioSourceProviderImpl::Stop() {
132  base::AutoLock auto_lock(sink_lock_);
133  state_ = kStopped;
134  if (!client_)
135    sink_->Stop();
136}
137
138void WebAudioSourceProviderImpl::Play() {
139  base::AutoLock auto_lock(sink_lock_);
140  DCHECK_EQ(state_, kStarted);
141  state_ = kPlaying;
142  if (!client_)
143    sink_->Play();
144}
145
146void WebAudioSourceProviderImpl::Pause() {
147  base::AutoLock auto_lock(sink_lock_);
148  DCHECK(state_ == kPlaying || state_ == kStarted);
149  state_ = kStarted;
150  if (!client_)
151    sink_->Pause();
152}
153
154bool WebAudioSourceProviderImpl::SetVolume(double volume) {
155  base::AutoLock auto_lock(sink_lock_);
156  volume_ = volume;
157  if (!client_)
158    sink_->SetVolume(volume);
159  return true;
160}
161
162void WebAudioSourceProviderImpl::Initialize(
163    const media::AudioParameters& params,
164    RenderCallback* renderer) {
165  base::AutoLock auto_lock(sink_lock_);
166  CHECK(!renderer_);
167  renderer_ = renderer;
168
169  DCHECK_EQ(state_, kStopped);
170  sink_->Initialize(params, renderer);
171
172  // Keep track of the format in case the client hasn't yet been set.
173  channels_ = params.channels();
174  sample_rate_ = params.sample_rate();
175
176  if (!set_format_cb_.is_null())
177    base::ResetAndReturn(&set_format_cb_).Run();
178}
179
180void WebAudioSourceProviderImpl::OnSetFormat() {
181  base::AutoLock auto_lock(sink_lock_);
182  if (!client_)
183    return;
184
185  // Inform Blink about the audio stream format.
186  client_->setFormat(channels_, sample_rate_);
187}
188
189}  // namespace content
190