Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(57)

Side by Side Diff: components/copresence/mediums/audio/audio_recorder.cc

Issue 419073002: Add the copresence DirectiveHandler. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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_recorder.h"
6
7 #include <algorithm>
8 #include <vector>
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 "base/synchronization/waitable_event.h"
15 #include "components/copresence/common/copresence_constants.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "media/audio/audio_manager.h"
18 #include "media/audio/audio_manager_base.h"
19 #include "media/base/audio_bus.h"
20
21 namespace {
22
23 const float kProcessIntervalMs = 500.0; // milliseconds.
xiyuan 2014/07/25 21:02:10 nit: 500.0f
rkc 2014/07/28 21:02:03 Done.
24
25 void AudioBusToString(scoped_ptr<media::AudioBus> source, std::string* buffer) {
Daniel Erat 2014/07/28 21:18:18 std::string seems strange to use for passing audio
rkc 2014/07/29 00:33:36 We'd have to end up converting this to a string an
26 buffer->resize(source->frames() * source->channels() * sizeof(float));
27 float* buffer_view = reinterpret_cast<float*>(string_as_array(buffer));
28
29 const int channels = source->channels();
30 for (int ch = 0; ch < channels; ++ch) {
31 for (int si = 0, di = ch; si < source->frames(); ++si, di += channels)
32 buffer_view[di] = source->channel(ch)[si];
33 }
34 }
35
36 // Called every kProcessIntervalMs to process the recorded audio. This
37 // converts our samples to the required sample rate, interleaves the samples
38 // and sends them to the whispernet decoder to process.
39 void ProcessSamples(
40 scoped_ptr<media::AudioBus> bus,
41 const copresence::AudioRecorder::DecodeSamplesCallback& callback) {
42 std::string samples;
43 AudioBusToString(bus.Pass(), &samples);
44 content::BrowserThread::PostTask(
45 content::BrowserThread::UI, FROM_HERE, base::Bind(callback, samples));
46 }
47
48 } // namespace
49
50 namespace copresence {
51
52 // Public methods.
53
54 AudioRecorder::AudioRecorder(const DecodeSamplesCallback& decode_callback)
55 : stream_(NULL),
56 is_recording_(false),
57 decode_callback_(decode_callback),
58 total_buffer_frames_(0),
59 buffer_frame_index_(0),
60 input_stream_for_testing_(NULL) {
61 media::AudioManager::Get()->GetTaskRunner()->PostTask(
62 FROM_HERE,
63 base::Bind(&AudioRecorder::InitializeOnAudioThread,
64 base::Unretained(this)));
65 }
66
67 AudioRecorder::AudioRecorder(
68 const DecodeSamplesCallback& decode_callback,
69 media::AudioInputStream* input_stream_for_testing,
70 scoped_ptr<media::AudioParameters> params_for_testing)
71 : stream_(NULL),
72 is_recording_(false),
73 decode_callback_(decode_callback),
74 total_buffer_frames_(0),
75 buffer_frame_index_(0),
76 input_stream_for_testing_(input_stream_for_testing),
77 params_for_testing_(params_for_testing.Pass()) {
78 media::AudioManager::Get()->GetTaskRunner()->PostTask(
79 FROM_HERE,
80 base::Bind(&AudioRecorder::InitializeOnAudioThread,
81 base::Unretained(this)));
82 }
83
84 AudioRecorder::~AudioRecorder() {
85 }
86
87 void AudioRecorder::Record() {
88 media::AudioManager::Get()->GetTaskRunner()->PostTask(
89 FROM_HERE,
90 base::Bind(&AudioRecorder::RecordOnAudioThread, base::Unretained(this)));
91 }
92
93 void AudioRecorder::Stop() {
94 media::AudioManager::Get()->GetTaskRunner()->PostTask(
95 FROM_HERE,
96 base::Bind(&AudioRecorder::StopOnAudioThread, base::Unretained(this)));
97 }
98
99 void AudioRecorder::Finalize() {
100 media::AudioManager::Get()->GetTaskRunner()->PostTask(
101 FROM_HERE,
102 base::Bind(&AudioRecorder::FinalizeOnAudioThread,
103 base::Unretained(this)));
104 }
105
106 // Private methods.
107
108 void AudioRecorder::InitializeOnAudioThread() {
109 DCHECK(media::AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
110 media::AudioParameters params =
Daniel Erat 2014/07/28 21:18:18 just do: media::AudioParameters params = params
rkc 2014/07/29 00:33:36 Doing that for the stream already, missed this one
111 media::AudioManager::Get()->GetInputStreamParameters(
112 media::AudioManagerBase::kDefaultDeviceId);
113
114 if (params_for_testing_.get())
115 params = *params_for_testing_;
116
117 const media::AudioParameters dest_params(params.format(),
118 kDefaultChannelLayout,
119 kDefaultChannels,
120 params.input_channels(),
121 kDefaultSampleRate,
122 kDefaultBitsPerSample,
123 params.frames_per_buffer(),
124 media::AudioParameters::NO_EFFECTS);
125
126 converter_.reset(new media::AudioConverter(
127 params, dest_params, params.sample_rate() == dest_params.sample_rate()));
128 converter_->AddInput(this);
129
130 total_buffer_frames_ = kProcessIntervalMs * dest_params.sample_rate() / 1000;
131 buffer_ =
132 media::AudioBus::Create(dest_params.channels(), total_buffer_frames_);
133 buffer_frame_index_ = 0;
134
135 stream_ = input_stream_for_testing_ ? input_stream_for_testing_ : stream_ =
136 media::AudioManager::Get()->MakeAudioInputStream(
137 params, media::AudioManagerBase::kDefaultDeviceId);
138
139 if (!stream_ || !stream_->Open()) {
140 LOG(ERROR) << "Failed to open an input stream.";
141 if (stream_) {
142 stream_->Close();
143 stream_ = NULL;
144 }
145 return;
146 }
147 stream_->SetVolume(stream_->GetMaxVolume());
148 }
149
150 void AudioRecorder::RecordOnAudioThread() {
151 DCHECK(media::AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
152 if (!stream_ || is_recording_)
153 return;
154
155 DVLOG(2) << "Recording Audio.";
156 converter_->Reset();
157 stream_->Start(this);
158 is_recording_ = true;
159 }
160
161 void AudioRecorder::StopOnAudioThread() {
162 DCHECK(media::AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
163 if (!stream_)
164 return;
165
166 stream_->Stop();
167 is_recording_ = false;
168 }
169
170 void AudioRecorder::StopAndCloseOnAudioThread() {
171 DCHECK(media::AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
172 if (!stream_)
173 return;
174
175 if (is_recording_)
176 stream_->Stop();
177 stream_->Close();
178 stream_ = NULL;
179
180 is_recording_ = false;
181 }
182
183 void AudioRecorder::FinalizeOnAudioThread() {
184 DCHECK(media::AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
185 StopAndCloseOnAudioThread();
186 delete this;
187 }
188
189 void AudioRecorder::OnData(media::AudioInputStream* stream,
190 const media::AudioBus* source,
191 uint32 /* hardware_delay_bytes */,
192 double /* volume */) {
Daniel Erat 2014/07/28 21:18:18 DCHECK which thread this is running on
rkc 2014/07/29 00:33:35 This isn't necessarily going to be called on the d
193 temp_conversion_buffer_ = source;
194 while (temp_conversion_buffer_) {
195 // source->frames() == source_params.frames_per_buffer(), so we only have
196 // one chunk of data in the source; correspondingly set the destination
197 // size to one chunk.
198 // TODO(rkc): Optimize this to directly write into buffer_ so we can avoid
199 // the copy into this buffer and then the copy back into buffer_.
200 scoped_ptr<media::AudioBus> converted_source =
201 media::AudioBus::Create(kDefaultChannels, converter_->ChunkSize());
202 // Convert accumulated samples into converted_source. Note: One call may not
203 // be enough to consume the samples from |source|. The converter may have
204 // accumulated samples over time due to a fractional input:output sample
205 // rate ratio. Since |source| is ephemeral, Convert() must be called until
206 // |source| is at least buffered into the converter. Once |source| is
207 // consumed during ProvideInput(), |temp_conversion_buffer_| will be set to
208 // NULL, which will break the conversion loop.
209 converter_->Convert(converted_source.get());
210
211 int remaining_buffer_frames = buffer_->frames() - buffer_frame_index_;
212 int frames_to_copy =
213 std::min(remaining_buffer_frames, converted_source->frames());
214 converted_source->CopyPartialFramesTo(
215 0, frames_to_copy, buffer_frame_index_, buffer_.get());
216 buffer_frame_index_ += frames_to_copy;
217
218 // Buffer full, send it for processing.
219 if (buffer_->frames() == buffer_frame_index_) {
220 ProcessSamples(buffer_.Pass(), decode_callback_);
221 buffer_ = media::AudioBus::Create(kDefaultChannels, total_buffer_frames_);
222 buffer_frame_index_ = 0;
223
224 // Copy any remaining frames in the source to our buffer.
225 int remaining_source_frames = converted_source->frames() - frames_to_copy;
226 converted_source->CopyPartialFramesTo(frames_to_copy,
227 remaining_source_frames,
228 buffer_frame_index_,
229 buffer_.get());
230 buffer_frame_index_ += remaining_source_frames;
231 }
232 }
233 }
234
235 void AudioRecorder::OnError(media::AudioInputStream* /* stream */) {
236 LOG(ERROR) << "Error during sound recording.";
237 media::AudioManager::Get()->GetTaskRunner()->PostTask(
238 FROM_HERE,
239 base::Bind(&AudioRecorder::StopAndCloseOnAudioThread,
240 base::Unretained(this)));
241 }
242
243 double AudioRecorder::ProvideInput(media::AudioBus* dest,
244 base::TimeDelta /* buffer_delay */) {
245 DCHECK(temp_conversion_buffer_);
246 DCHECK_LE(temp_conversion_buffer_->frames(), dest->frames());
247 temp_conversion_buffer_->CopyTo(dest);
248 temp_conversion_buffer_ = NULL;
249 return 1.0;
250 }
251
252 bool AudioRecorder::IsRecording() {
253 if (media::AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread())
DaleCurtis 2014/07/25 20:41:29 Ditto.
rkc 2014/07/28 21:02:03 Done.
254 return false;
255
256 // All this does is flush the tasks on the audio thread.
257 base::RunLoop rl;
258 media::AudioManager::Get()->GetTaskRunner()->PostTaskAndReply(
259 FROM_HERE,
260 base::Bind(base::IgnoreResult(&AudioRecorder::IsRecording),
261 base::Unretained(this)),
262 rl.QuitClosure());
263 rl.Run();
264
265 return is_recording_;
266 }
267
268 } // namespace copresence
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698