Home | History | Annotate | Download | only in audio
      1 // Copyright 2014 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 "components/copresence/mediums/audio/audio_player.h"
      6 
      7 #include <algorithm>
      8 #include <string>
      9 
     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/copresence/public/copresence_constants.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"
     19 
     20 namespace {
     21 
     22 const int kDefaultFrameCount = 1024;
     23 const double kOutputVolumePercent = 1.0f;
     24 
     25 }  // namespace
     26 
     27 namespace copresence {
     28 
     29 // Public methods.
     30 
     31 AudioPlayer::AudioPlayer()
     32     : is_playing_(false), stream_(NULL), frame_index_(0) {
     33 }
     34 
     35 AudioPlayer::~AudioPlayer() {
     36 }
     37 
     38 void AudioPlayer::Initialize() {
     39   media::AudioManager::Get()->GetTaskRunner()->PostTask(
     40       FROM_HERE,
     41       base::Bind(&AudioPlayer::InitializeOnAudioThread,
     42                  base::Unretained(this)));
     43 }
     44 
     45 void AudioPlayer::Play(
     46     const scoped_refptr<media::AudioBusRefCounted>& samples) {
     47   media::AudioManager::Get()->GetTaskRunner()->PostTask(
     48       FROM_HERE,
     49       base::Bind(
     50           &AudioPlayer::PlayOnAudioThread, base::Unretained(this), samples));
     51 }
     52 
     53 void AudioPlayer::Stop() {
     54   media::AudioManager::Get()->GetTaskRunner()->PostTask(
     55       FROM_HERE,
     56       base::Bind(&AudioPlayer::StopOnAudioThread, base::Unretained(this)));
     57 }
     58 
     59 bool AudioPlayer::IsPlaying() {
     60   return is_playing_;
     61 }
     62 
     63 void AudioPlayer::Finalize() {
     64   media::AudioManager::Get()->GetTaskRunner()->PostTask(
     65       FROM_HERE,
     66       base::Bind(&AudioPlayer::FinalizeOnAudioThread, base::Unretained(this)));
     67 }
     68 
     69 // Private methods.
     70 
     71 void AudioPlayer::InitializeOnAudioThread() {
     72   DCHECK(media::AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
     73   stream_ = output_stream_for_testing_
     74                 ? output_stream_for_testing_.get()
     75                 : media::AudioManager::Get()->MakeAudioOutputStreamProxy(
     76                       media::AudioParameters(
     77                           media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
     78                           media::CHANNEL_LAYOUT_MONO,
     79                           kDefaultSampleRate,
     80                           kDefaultBitsPerSample,
     81                           kDefaultFrameCount),
     82                       std::string());
     83 
     84   if (!stream_ || !stream_->Open()) {
     85     LOG(ERROR) << "Failed to open an output stream.";
     86     if (stream_) {
     87       stream_->Close();
     88       stream_ = NULL;
     89     }
     90     return;
     91   }
     92   stream_->SetVolume(kOutputVolumePercent);
     93 }
     94 
     95 void AudioPlayer::PlayOnAudioThread(
     96     const scoped_refptr<media::AudioBusRefCounted>& samples) {
     97   DCHECK(media::AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
     98   if (!stream_)
     99     return;
    100 
    101   {
    102     base::AutoLock al(state_lock_);
    103 
    104     samples_ = samples;
    105     frame_index_ = 0;
    106 
    107     if (is_playing_)
    108       return;
    109   }
    110 
    111   is_playing_ = true;
    112   stream_->Start(this);
    113 }
    114 
    115 void AudioPlayer::StopOnAudioThread() {
    116   DCHECK(media::AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
    117   if (!stream_)
    118     return;
    119 
    120   stream_->Stop();
    121   is_playing_ = false;
    122 }
    123 
    124 void AudioPlayer::StopAndCloseOnAudioThread() {
    125   DCHECK(media::AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
    126   if (!stream_)
    127     return;
    128 
    129   if (is_playing_)
    130     stream_->Stop();
    131   stream_->Close();
    132   stream_ = NULL;
    133 
    134   is_playing_ = false;
    135 }
    136 
    137 void AudioPlayer::FinalizeOnAudioThread() {
    138   DCHECK(media::AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
    139   StopAndCloseOnAudioThread();
    140   delete this;
    141 }
    142 
    143 int AudioPlayer::OnMoreData(media::AudioBus* dest,
    144                             media::AudioBuffersState /* state */) {
    145   base::AutoLock al(state_lock_);
    146   // Continuously play our samples till explicitly told to stop.
    147   const int leftover_frames = samples_->frames() - frame_index_;
    148   const int frames_to_copy = std::min(dest->frames(), leftover_frames);
    149 
    150   samples_->CopyPartialFramesTo(frame_index_, frames_to_copy, 0, dest);
    151   frame_index_ += frames_to_copy;
    152 
    153   // If we didn't fill the destination audio bus, wrap around and fill the rest.
    154   if (leftover_frames <= dest->frames()) {
    155     samples_->CopyPartialFramesTo(
    156         0, dest->frames() - frames_to_copy, frames_to_copy, dest);
    157     frame_index_ = dest->frames() - frames_to_copy;
    158   }
    159 
    160   return dest->frames();
    161 }
    162 
    163 void AudioPlayer::OnError(media::AudioOutputStream* /* stream */) {
    164   LOG(ERROR) << "Error during system sound reproduction.";
    165   media::AudioManager::Get()->GetTaskRunner()->PostTask(
    166       FROM_HERE,
    167       base::Bind(&AudioPlayer::StopAndCloseOnAudioThread,
    168                  base::Unretained(this)));
    169 }
    170 
    171 void AudioPlayer::FlushAudioLoopForTesting() {
    172   if (media::AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread())
    173     return;
    174 
    175   // Queue task on the audio thread, when it is executed, that means we've
    176   // successfully executed all the tasks before us.
    177   base::RunLoop rl;
    178   media::AudioManager::Get()->GetTaskRunner()->PostTaskAndReply(
    179       FROM_HERE,
    180       base::Bind(base::IgnoreResult(&AudioPlayer::FlushAudioLoopForTesting),
    181                  base::Unretained(this)),
    182       rl.QuitClosure());
    183   rl.Run();
    184 }
    185 
    186 }  // namespace copresence
    187