virtual_audio_input_stream.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
1// Copyright (c) 2012 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/virtual_audio_input_stream.h"
6
7#include <algorithm>
8#include <utility>
9
10#include "base/bind.h"
11#include "base/message_loop.h"
12#include "base/message_loop_proxy.h"
13#include "media/audio/virtual_audio_output_stream.h"
14
15namespace media {
16
17// LoopbackAudioConverter works similar to AudioConverter and converts input
18// streams to different audio parameters. Then, the LoopbackAudioConverter can
19// be used as an input to another AudioConverter. This allows us to
20// use converted audio from AudioOutputStreams as input to an AudioConverter.
21// For example, this allows converting multiple streams into a common format and
22// using the converted audio as input to another AudioConverter (i.e. a mixer).
23class LoopbackAudioConverter : public AudioConverter::InputCallback {
24 public:
25  LoopbackAudioConverter(const AudioParameters& input_params,
26                         const AudioParameters& output_params)
27      : audio_converter_(input_params, output_params, false) {}
28
29  virtual ~LoopbackAudioConverter() {}
30
31  void AddInput(AudioConverter::InputCallback* input) {
32    audio_converter_.AddInput(input);
33  }
34
35  void RemoveInput(AudioConverter::InputCallback* input) {
36    audio_converter_.RemoveInput(input);
37  }
38
39 private:
40  virtual double ProvideInput(AudioBus* audio_bus,
41                              base::TimeDelta buffer_delay) OVERRIDE {
42    audio_converter_.Convert(audio_bus);
43    return 1.0;
44  }
45
46  AudioConverter audio_converter_;
47
48  DISALLOW_COPY_AND_ASSIGN(LoopbackAudioConverter);
49};
50
51VirtualAudioInputStream::VirtualAudioInputStream(
52    const AudioParameters& params, base::MessageLoopProxy* message_loop,
53    const AfterCloseCallback& after_close_cb)
54    : message_loop_(message_loop),
55      after_close_cb_(after_close_cb),
56      callback_(NULL),
57      buffer_duration_(base::TimeDelta::FromMicroseconds(
58          params.frames_per_buffer() * base::Time::kMicrosecondsPerSecond /
59          static_cast<float>(params.sample_rate()))),
60      buffer_(new uint8[params.GetBytesPerBuffer()]),
61      params_(params),
62      audio_bus_(AudioBus::Create(params_)),
63      mixer_(params_, params_, false),
64      num_attached_output_streams_(0) {
65  DCHECK(params_.IsValid());
66  DCHECK(message_loop_);
67}
68
69VirtualAudioInputStream::~VirtualAudioInputStream() {
70  for (AudioConvertersMap::iterator it = converters_.begin();
71       it != converters_.end(); ++it)
72    delete it->second;
73
74  DCHECK_EQ(0, num_attached_output_streams_);
75}
76
77bool VirtualAudioInputStream::Open() {
78  DCHECK(message_loop_->BelongsToCurrentThread());
79  memset(buffer_.get(), 0, params_.GetBytesPerBuffer());
80  return true;
81}
82
83void VirtualAudioInputStream::Start(AudioInputCallback* callback) {
84  DCHECK(message_loop_->BelongsToCurrentThread());
85  callback_ = callback;
86  next_read_time_ = base::Time::Now();
87  on_more_data_cb_.Reset(base::Bind(&VirtualAudioInputStream::ReadAudio,
88                                    base::Unretained(this)));
89  message_loop_->PostTask(FROM_HERE, on_more_data_cb_.callback());
90}
91
92void VirtualAudioInputStream::Stop() {
93  DCHECK(message_loop_->BelongsToCurrentThread());
94  on_more_data_cb_.Cancel();
95}
96
97void VirtualAudioInputStream::AddOutputStream(
98    VirtualAudioOutputStream* stream, const AudioParameters& output_params) {
99  DCHECK(message_loop_->BelongsToCurrentThread());
100
101  AudioConvertersMap::iterator converter = converters_.find(output_params);
102  if (converter == converters_.end()) {
103    std::pair<AudioConvertersMap::iterator, bool> result = converters_.insert(
104        std::make_pair(output_params,
105                       new LoopbackAudioConverter(output_params, params_)));
106    converter = result.first;
107
108    // Add to main mixer if we just added a new AudioTransform.
109    mixer_.AddInput(converter->second);
110  }
111  converter->second->AddInput(stream);
112  ++num_attached_output_streams_;
113}
114
115void VirtualAudioInputStream::RemoveOutputStream(
116    VirtualAudioOutputStream* stream, const AudioParameters& output_params) {
117  DCHECK(message_loop_->BelongsToCurrentThread());
118
119  DCHECK(converters_.find(output_params) != converters_.end());
120  converters_[output_params]->RemoveInput(stream);
121
122  --num_attached_output_streams_;
123  DCHECK_LE(0, num_attached_output_streams_);
124}
125
126void VirtualAudioInputStream::ReadAudio() {
127  DCHECK(message_loop_->BelongsToCurrentThread());
128  DCHECK(callback_);
129
130  mixer_.Convert(audio_bus_.get());
131  audio_bus_->ToInterleaved(params_.frames_per_buffer(),
132                            params_.bits_per_sample() / 8,
133                            buffer_.get());
134
135  callback_->OnData(this,
136                    buffer_.get(),
137                    params_.GetBytesPerBuffer(),
138                    params_.GetBytesPerBuffer(),
139                    1.0);
140
141  // Need to account for time spent here due to renderer side mixing as well as
142  // the imprecision of PostDelayedTask.
143  next_read_time_ += buffer_duration_;
144  base::Time now = base::Time::Now();
145  base::TimeDelta delay = next_read_time_ - now;
146  if (delay < base::TimeDelta()) {
147    // Reset the next read time if we end up getting too far behind. We'll just
148    // slow down playback to avoid using up all the CPU.
149    delay = buffer_duration_;
150    next_read_time_ = now + buffer_duration_;
151  }
152
153  message_loop_->PostDelayedTask(FROM_HERE,
154                                 on_more_data_cb_.callback(),
155                                 delay);
156}
157
158void VirtualAudioInputStream::Close() {
159  DCHECK(message_loop_->BelongsToCurrentThread());
160
161  if (callback_) {
162    DCHECK(on_more_data_cb_.IsCancelled());
163    callback_->OnClose(this);
164    callback_ = NULL;
165  }
166
167  // If a non-null AfterCloseCallback was provided to the constructor, invoke it
168  // here.  The callback is moved to a stack-local first since |this| could be
169  // destroyed during Run().
170  if (!after_close_cb_.is_null()) {
171    const AfterCloseCallback cb = after_close_cb_;
172    after_close_cb_.Reset();
173    cb.Run(this);
174  }
175}
176
177double VirtualAudioInputStream::GetMaxVolume() {
178  return 1.0;
179}
180
181void VirtualAudioInputStream::SetVolume(double volume) {}
182
183double VirtualAudioInputStream::GetVolume() {
184  return 1.0;
185}
186
187void VirtualAudioInputStream::SetAutomaticGainControl(bool enabled) {}
188
189bool VirtualAudioInputStream::GetAutomaticGainControl() {
190  return false;
191}
192
193}  // namespace media
194