1 // Copyright 2015 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.
5 #include "components/audio_modem/audio_player_impl.h"
10 #include "base/bind.h"
11 #include "base/bind_helpers.h"
12 #include "base/logging.h"
13 #include "base/run_loop.h"
14 #include "components/audio_modem/public/audio_modem_types.h"
15 #include "content/public/browser/browser_thread.h"
16 #include "media/audio/audio_manager.h"
17 #include "media/audio/audio_parameters.h"
18 #include "media/base/audio_bus.h"
22 const int kDefaultFrameCount
= 1024;
23 const double kOutputVolumePercent
= 1.0f
;
27 namespace audio_modem
{
31 AudioPlayerImpl::AudioPlayerImpl()
32 : is_playing_(false), stream_(nullptr), frame_index_(0) {
35 AudioPlayerImpl::~AudioPlayerImpl() {
38 void AudioPlayerImpl::Initialize() {
39 media::AudioManager::Get()->GetTaskRunner()->PostTask(
41 base::Bind(&AudioPlayerImpl::InitializeOnAudioThread
,
42 base::Unretained(this)));
45 void AudioPlayerImpl::Play(
46 const scoped_refptr
<media::AudioBusRefCounted
>& samples
) {
47 media::AudioManager::Get()->GetTaskRunner()->PostTask(
49 base::Bind(&AudioPlayerImpl::PlayOnAudioThread
,
50 base::Unretained(this),
54 void AudioPlayerImpl::Stop() {
55 media::AudioManager::Get()->GetTaskRunner()->PostTask(
57 base::Bind(&AudioPlayerImpl::StopOnAudioThread
, base::Unretained(this)));
60 void AudioPlayerImpl::Finalize() {
61 media::AudioManager::Get()->GetTaskRunner()->PostTask(
63 base::Bind(&AudioPlayerImpl::FinalizeOnAudioThread
,
64 base::Unretained(this)));
69 void AudioPlayerImpl::InitializeOnAudioThread() {
70 DCHECK(media::AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
71 stream_
= output_stream_for_testing_
72 ? output_stream_for_testing_
.get()
73 : media::AudioManager::Get()->MakeAudioOutputStreamProxy(
74 media::AudioParameters(
75 media::AudioParameters::AUDIO_PCM_LOW_LATENCY
,
76 media::CHANNEL_LAYOUT_MONO
,
78 kDefaultBitsPerSample
,
82 if (!stream_
|| !stream_
->Open()) {
83 LOG(ERROR
) << "Failed to open an output stream.";
90 stream_
->SetVolume(kOutputVolumePercent
);
93 void AudioPlayerImpl::PlayOnAudioThread(
94 const scoped_refptr
<media::AudioBusRefCounted
>& samples
) {
95 DCHECK(media::AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
96 if (!stream_
|| is_playing_
)
100 base::AutoLock
al(state_lock_
);
105 VLOG(3) << "Starting playback.";
107 stream_
->Start(this);
110 void AudioPlayerImpl::StopOnAudioThread() {
111 DCHECK(media::AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
112 if (!stream_
|| !is_playing_
)
115 VLOG(3) << "Stopping playback.";
120 void AudioPlayerImpl::StopAndCloseOnAudioThread() {
121 DCHECK(media::AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
130 void AudioPlayerImpl::FinalizeOnAudioThread() {
131 DCHECK(media::AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
132 StopAndCloseOnAudioThread();
136 int AudioPlayerImpl::OnMoreData(media::AudioBus
* dest
,
137 uint32
/* total_bytes_delay */) {
138 base::AutoLock
al(state_lock_
);
139 // Continuously play our samples till explicitly told to stop.
140 const int leftover_frames
= samples_
->frames() - frame_index_
;
141 const int frames_to_copy
= std::min(dest
->frames(), leftover_frames
);
143 samples_
->CopyPartialFramesTo(frame_index_
, frames_to_copy
, 0, dest
);
144 frame_index_
+= frames_to_copy
;
146 // If we didn't fill the destination audio bus, wrap around and fill the rest.
147 if (leftover_frames
<= dest
->frames()) {
148 samples_
->CopyPartialFramesTo(
149 0, dest
->frames() - frames_to_copy
, frames_to_copy
, dest
);
150 frame_index_
= dest
->frames() - frames_to_copy
;
153 return dest
->frames();
156 void AudioPlayerImpl::OnError(media::AudioOutputStream
* /* stream */) {
157 LOG(ERROR
) << "Error during system sound reproduction.";
158 media::AudioManager::Get()->GetTaskRunner()->PostTask(
160 base::Bind(&AudioPlayerImpl::StopAndCloseOnAudioThread
,
161 base::Unretained(this)));
164 void AudioPlayerImpl::FlushAudioLoopForTesting() {
165 if (media::AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread())
168 // Queue task on the audio thread, when it is executed, that means we've
169 // successfully executed all the tasks before us.
171 media::AudioManager::Get()->GetTaskRunner()->PostTaskAndReply(
173 base::Bind(base::IgnoreResult(&AudioPlayerImpl::FlushAudioLoopForTesting
),
174 base::Unretained(this)),
179 } // namespace audio_modem