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/sounds/audio_stream_handler.h"
6
7#include <string>
8
9#include "base/cancelable_callback.h"
10#include "base/logging.h"
11#include "base/single_thread_task_runner.h"
12#include "base/synchronization/lock.h"
13#include "base/time/time.h"
14#include "media/audio/audio_manager.h"
15#include "media/audio/audio_manager_base.h"
16#include "media/base/channel_layout.h"
17
18namespace media {
19
20namespace {
21
22// Volume percent.
23const double kOutputVolumePercent = 0.8;
24
25// The number of frames each OnMoreData() call will request.
26const int kDefaultFrameCount = 1024;
27
28// Keep alive timeout for audio stream.
29const int kKeepAliveMs = 1500;
30
31AudioStreamHandler::TestObserver* g_observer_for_testing = NULL;
32AudioOutputStream::AudioSourceCallback* g_audio_source_for_testing = NULL;
33
34}  // namespace
35
36class AudioStreamHandler::AudioStreamContainer
37    : public AudioOutputStream::AudioSourceCallback {
38 public:
39  AudioStreamContainer(const WavAudioHandler& wav_audio)
40      : started_(false),
41        stream_(NULL),
42        cursor_(0),
43        delayed_stop_posted_(false),
44        wav_audio_(wav_audio) {}
45
46  virtual ~AudioStreamContainer() {
47    DCHECK(AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
48  }
49
50  void Play() {
51    DCHECK(AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
52
53    if (!stream_) {
54      const AudioParameters& p = wav_audio_.params();
55      const AudioParameters params(AudioParameters::AUDIO_PCM_LOW_LATENCY,
56                                   p.channel_layout(),
57                                   p.sample_rate(),
58                                   p.bits_per_sample(),
59                                   kDefaultFrameCount);
60      stream_ = AudioManager::Get()->MakeAudioOutputStreamProxy(params,
61                                                                std::string());
62      if (!stream_ || !stream_->Open()) {
63        LOG(ERROR) << "Failed to open an output stream.";
64        return;
65      }
66      stream_->SetVolume(kOutputVolumePercent);
67    }
68
69    {
70      base::AutoLock al(state_lock_);
71
72      delayed_stop_posted_ = false;
73      stop_closure_.Reset(base::Bind(&AudioStreamContainer::StopStream,
74                                     base::Unretained(this)));
75
76      if (started_) {
77        if (wav_audio_.AtEnd(cursor_))
78          cursor_ = 0;
79        return;
80      }
81
82      cursor_ = 0;
83    }
84
85    started_ = true;
86    if (g_audio_source_for_testing)
87      stream_->Start(g_audio_source_for_testing);
88    else
89      stream_->Start(this);
90
91    if (g_observer_for_testing)
92      g_observer_for_testing->OnPlay();
93  }
94
95  void Stop() {
96    DCHECK(AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
97    StopStream();
98    if (stream_)
99      stream_->Close();
100    stream_ = NULL;
101    stop_closure_.Cancel();
102  }
103
104 private:
105  // AudioOutputStream::AudioSourceCallback overrides:
106  // Following methods could be called from *ANY* thread.
107  virtual int OnMoreData(AudioBus* dest,
108                         AudioBuffersState /* state */) OVERRIDE {
109    base::AutoLock al(state_lock_);
110    size_t bytes_written = 0;
111
112    if (wav_audio_.AtEnd(cursor_) ||
113        !wav_audio_.CopyTo(dest, cursor_, &bytes_written)) {
114      if (delayed_stop_posted_)
115        return 0;
116      delayed_stop_posted_ = true;
117      AudioManager::Get()->GetTaskRunner()->PostDelayedTask(
118          FROM_HERE,
119          stop_closure_.callback(),
120          base::TimeDelta::FromMilliseconds(kKeepAliveMs));
121      return 0;
122    }
123    cursor_ += bytes_written;
124    return dest->frames();
125  }
126
127  virtual void OnError(AudioOutputStream* /* stream */) OVERRIDE {
128    LOG(ERROR) << "Error during system sound reproduction.";
129  }
130
131  void StopStream() {
132    DCHECK(AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
133
134    if (stream_ && started_) {
135      // Do not hold the |state_lock_| while stopping the output stream.
136      stream_->Stop();
137      if (g_observer_for_testing)
138        g_observer_for_testing->OnStop(cursor_);
139    }
140
141    started_ = false;
142  }
143
144  // Must only be accessed on the AudioManager::GetTaskRunner() thread.
145  bool started_;
146  AudioOutputStream* stream_;
147
148  // All variables below must be accessed under |state_lock_| when |started_|.
149  base::Lock state_lock_;
150  size_t cursor_;
151  bool delayed_stop_posted_;
152  const WavAudioHandler wav_audio_;
153  base::CancelableClosure stop_closure_;
154
155  DISALLOW_COPY_AND_ASSIGN(AudioStreamContainer);
156};
157
158AudioStreamHandler::AudioStreamHandler(const base::StringPiece& wav_data)
159    : wav_audio_(wav_data),
160      initialized_(false) {
161  AudioManager* manager = AudioManager::Get();
162  if (!manager) {
163    LOG(ERROR) << "Can't get access to audio manager.";
164    return;
165  }
166  if (!wav_audio_.params().IsValid()) {
167    LOG(ERROR) << "Audio params are invalid.";
168    return;
169  }
170  stream_.reset(new AudioStreamContainer(wav_audio_));
171  initialized_ = true;
172}
173
174AudioStreamHandler::~AudioStreamHandler() {
175  DCHECK(CalledOnValidThread());
176  AudioManager::Get()->GetTaskRunner()->PostTask(
177      FROM_HERE,
178      base::Bind(&AudioStreamContainer::Stop, base::Unretained(stream_.get())));
179  AudioManager::Get()->GetTaskRunner()->DeleteSoon(FROM_HERE,
180                                                   stream_.release());
181}
182
183bool AudioStreamHandler::IsInitialized() const {
184  DCHECK(CalledOnValidThread());
185  return initialized_;
186}
187
188bool AudioStreamHandler::Play() {
189  DCHECK(CalledOnValidThread());
190
191  if (!IsInitialized())
192    return false;
193
194  AudioManager::Get()->GetTaskRunner()->PostTask(
195      FROM_HERE,
196      base::Bind(base::IgnoreResult(&AudioStreamContainer::Play),
197                 base::Unretained(stream_.get())));
198  return true;
199}
200
201void AudioStreamHandler::Stop() {
202  DCHECK(CalledOnValidThread());
203  AudioManager::Get()->GetTaskRunner()->PostTask(
204      FROM_HERE,
205      base::Bind(&AudioStreamContainer::Stop, base::Unretained(stream_.get())));
206}
207
208// static
209void AudioStreamHandler::SetObserverForTesting(TestObserver* observer) {
210  g_observer_for_testing = observer;
211}
212
213// static
214void AudioStreamHandler::SetAudioSourceForTesting(
215    AudioOutputStream::AudioSourceCallback* source) {
216  g_audio_source_for_testing = source;
217}
218
219}  // namespace media
220