Home | History | Annotate | Download | only in sounds
      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:
     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 
     41   virtual ~AudioStreamContainer() {
     42     DCHECK(AudioManager::Get()->GetMessageLoop()->BelongsToCurrentThread());
     43   }
     44 
     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 
     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.
     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 
    102   virtual int OnMoreIOData(AudioBus* /* source */,
    103                            AudioBus* dest,
    104                            AudioBuffersState state) OVERRIDE {
    105     return OnMoreData(dest, state);
    106   }
    107 
    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 
    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 
    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 
    152 bool AudioStreamHandler::IsInitialized() const {
    153   DCHECK(CalledOnValidThread());
    154   return initialized_;
    155 }
    156 
    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 
    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
    178 void AudioStreamHandler::SetObserverForTesting(TestObserver* observer) {
    179   g_observer_for_testing = observer;
    180 }
    181 
    182 // static
    183 void AudioStreamHandler::SetAudioSourceForTesting(
    184     AudioOutputStream::AudioSourceCallback* source) {
    185   g_audio_source_for_testing = source;
    186 }
    187 
    188 }  // namespace media
    189