audio_stream_handler.cc revision 0f1bc08d4cfcc34181b0b5cbf065c40f687bf740
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/logging.h"
10#include "base/message_loop/message_loop_proxy.h"
11#include "media/audio/audio_manager.h"
12#include "media/audio/audio_manager_base.h"
13#include "media/base/channel_layout.h"
14
15namespace media {
16
17namespace {
18
19// Volume percent.
20const double kOutputVolumePercent = 0.8;
21
22AudioStreamHandler::TestObserver* g_observer_for_testing = NULL;
23AudioOutputStream::AudioSourceCallback* g_audio_source_for_testing = NULL;
24
25}  // namespace
26
27class AudioStreamHandler::AudioStreamContainer
28    : public AudioOutputStream::AudioSourceCallback {
29 public:
30  AudioStreamContainer(const WavAudioHandler& wav_audio,
31                       const AudioParameters& params)
32      : stream_(NULL),
33        wav_audio_(wav_audio),
34        params_(params),
35        cursor_(0) {
36  }
37
38  virtual ~AudioStreamContainer() {
39    DCHECK(AudioManager::Get()->GetMessageLoop()->BelongsToCurrentThread());
40  }
41
42  void Play() {
43    DCHECK(AudioManager::Get()->GetMessageLoop()->BelongsToCurrentThread());
44
45    if (!stream_) {
46      stream_ = AudioManager::Get()->MakeAudioOutputStreamProxy(params_,
47                                                                std::string(),
48                                                                std::string());
49      if (!stream_ || !stream_->Open()) {
50        LOG(ERROR) << "Failed to open an output stream.";
51        return;
52      }
53      stream_->SetVolume(kOutputVolumePercent);
54    } else {
55      // TODO (ygorshenin@): implement smart stream rewind.
56      stream_->Stop();
57    }
58
59    cursor_ = 0;
60    if (g_audio_source_for_testing)
61      stream_->Start(g_audio_source_for_testing);
62    else
63      stream_->Start(this);
64
65    if (g_observer_for_testing)
66      g_observer_for_testing->OnPlay();
67  }
68
69  void Stop() {
70    DCHECK(AudioManager::Get()->GetMessageLoop()->BelongsToCurrentThread());
71    if (!stream_)
72      return;
73    stream_->Stop();
74    stream_->Close();
75    stream_ = NULL;
76
77    if (g_observer_for_testing)
78      g_observer_for_testing->OnStop(cursor_);
79  }
80
81 private:
82  // AudioOutputStream::AudioSourceCallback overrides:
83  // Following methods could be called from *ANY* thread.
84  virtual int OnMoreData(AudioBus* dest,
85                         AudioBuffersState /* state */) OVERRIDE {
86    size_t bytes_written = 0;
87    if (wav_audio_.AtEnd(cursor_) ||
88        !wav_audio_.CopyTo(dest, cursor_, &bytes_written)) {
89      AudioManager::Get()->GetMessageLoop()->PostTask(
90          FROM_HERE,
91          base::Bind(&AudioStreamContainer::Stop, base::Unretained(this)));
92      return 0;
93    }
94    cursor_ += bytes_written;
95
96    return dest->frames();
97  }
98
99  virtual int OnMoreIOData(AudioBus* /* source */,
100                           AudioBus* dest,
101                           AudioBuffersState state) OVERRIDE {
102    return OnMoreData(dest, state);
103  }
104
105  virtual void OnError(AudioOutputStream* /* stream */) OVERRIDE {
106    LOG(ERROR) << "Error during system sound reproduction.";
107  }
108
109  AudioOutputStream* stream_;
110
111  const WavAudioHandler wav_audio_;
112  const AudioParameters params_;
113
114  size_t cursor_;
115
116  DISALLOW_COPY_AND_ASSIGN(AudioStreamContainer);
117};
118
119AudioStreamHandler::AudioStreamHandler(const base::StringPiece& wav_data)
120    : wav_audio_(wav_data),
121      initialized_(false) {
122  AudioManager* manager = AudioManager::Get();
123  if (!manager) {
124    LOG(ERROR) << "Can't get access to audio manager.";
125    return;
126  }
127  AudioParameters params(
128      AudioParameters::AUDIO_PCM_LOW_LATENCY,
129      GuessChannelLayout(wav_audio_.num_channels()),
130      wav_audio_.sample_rate(),
131      wav_audio_.bits_per_sample(),
132      manager->GetDefaultOutputStreamParameters().frames_per_buffer());
133  if (!params.IsValid()) {
134    LOG(ERROR) << "Audio params are invalid.";
135    return;
136  }
137  stream_.reset(new AudioStreamContainer(wav_audio_, params));
138  initialized_ = true;
139}
140
141AudioStreamHandler::~AudioStreamHandler() {
142  DCHECK(CalledOnValidThread());
143  AudioManager::Get()->GetMessageLoop()->PostTask(
144      FROM_HERE,
145      base::Bind(&AudioStreamContainer::Stop, base::Unretained(stream_.get())));
146  AudioManager::Get()->GetMessageLoop()->DeleteSoon(FROM_HERE,
147                                                    stream_.release());
148}
149
150bool AudioStreamHandler::IsInitialized() const {
151  DCHECK(CalledOnValidThread());
152  return initialized_;
153}
154
155bool AudioStreamHandler::Play() {
156  DCHECK(CalledOnValidThread());
157
158  if (!IsInitialized())
159    return false;
160
161  AudioManager::Get()->GetMessageLoop()->PostTask(
162      FROM_HERE,
163      base::Bind(base::IgnoreResult(&AudioStreamContainer::Play),
164                 base::Unretained(stream_.get())));
165  return true;
166}
167
168void AudioStreamHandler::Stop() {
169  DCHECK(CalledOnValidThread());
170  AudioManager::Get()->GetMessageLoop()->PostTask(
171      FROM_HERE,
172      base::Bind(&AudioStreamContainer::Stop, base::Unretained(stream_.get())));
173}
174
175// static
176void AudioStreamHandler::SetObserverForTesting(TestObserver* observer) {
177  g_observer_for_testing = observer;
178}
179
180// static
181void AudioStreamHandler::SetAudioSourceForTesting(
182    AudioOutputStream::AudioSourceCallback* source) {
183  g_audio_source_for_testing = source;
184}
185
186}  // namespace media
187