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