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

Side by Side Diff: webrtc/test/fake_audio_device.cc

Issue 2717623003: Add the ability to read/write to WAV files in FakeAudioDevice (Closed)
Patch Set: Address review feedback Created 3 years, 9 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
OLDNEW
1 /* 1 /*
2 * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. 2 * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
3 * 3 *
4 * Use of this source code is governed by a BSD-style license 4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source 5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found 6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may 7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree. 8 * be found in the AUTHORS file in the root of the source tree.
9 */ 9 */
10 10
11 #include "webrtc/test/fake_audio_device.h" 11 #include "webrtc/test/fake_audio_device.h"
12 12
13 #include <algorithm> 13 #include <algorithm>
14 #include <utility>
14 15
15 #include "webrtc/base/array_view.h"
16 #include "webrtc/base/checks.h" 16 #include "webrtc/base/checks.h"
17 #include "webrtc/base/random.h" 17 #include "webrtc/base/random.h"
18 #include "webrtc/common_audio/wav_file.h"
18 #include "webrtc/system_wrappers/include/event_wrapper.h" 19 #include "webrtc/system_wrappers/include/event_wrapper.h"
19 20
20 namespace webrtc { 21 namespace webrtc {
21 22
22 namespace { 23 namespace {
23 24
24 constexpr int kFrameLengthMs = 10; 25 constexpr int kFrameLengthMs = 10;
25 constexpr int kFramesPerSecond = 1000 / kFrameLengthMs; 26 constexpr int kFramesPerSecond = 1000 / kFrameLengthMs;
26 27
28 // Assuming 10ms audio packets..
29 class PulsedNoiseCapturer final : public test::FakeAudioDevice::Capturer {
30 public:
31 PulsedNoiseCapturer(int16_t max_amplitude, int sampling_frequency_in_hz)
32 : sampling_frequency_in_hz_(sampling_frequency_in_hz),
33 fill_with_zero_(false),
34 random_generator_(1),
35 max_amplitude_(max_amplitude) {
36 RTC_DCHECK_GT(max_amplitude, 0);
37 }
38
39 int SamplingFrequency() const override {
40 return sampling_frequency_in_hz_;
41 }
42
43 bool Capture(rtc::BufferT<int16_t>* buffer) override {
44 fill_with_zero_ = !fill_with_zero_;
45 buffer->SetData(buffer->size(), [&](rtc::ArrayView<int16_t> data) {
46 if (fill_with_zero_) {
47 std::fill(data.begin(), data.end(), 0);
48 } else {
49 std::generate(data.begin(), data.end(), [&]() {
50 return random_generator_.Rand(-max_amplitude_, max_amplitude_);
51 });
52 }
53 return data.size();
54 });
55 return true;
56 }
57
58 private:
59 int sampling_frequency_in_hz_;
60 bool fill_with_zero_;
61 Random random_generator_;
62 const int16_t max_amplitude_;
63 };
64
65 class WavFileReader final : public test::FakeAudioDevice::Capturer {
66 public:
67 WavFileReader(std::string filename, int sampling_frequency_in_hz)
68 : sampling_frequency_in_hz_(sampling_frequency_in_hz),
69 wav_reader_(filename) {
70 RTC_CHECK_EQ(wav_reader_.sample_rate(), sampling_frequency_in_hz);
71 RTC_CHECK_EQ(wav_reader_.num_channels(), 1);
72 }
73
74 int SamplingFrequency() const override {
75 return sampling_frequency_in_hz_;
76 }
77
78 bool Capture(rtc::BufferT<int16_t>* buffer) override {
79 buffer->SetData(buffer->size(), [&](rtc::ArrayView<int16_t> data) {
80 return wav_reader_.ReadSamples(data.size(), data.data());
81 });
82 return buffer->size() > 0;
83 }
84
85 private:
86 int sampling_frequency_in_hz_;
87 WavReader wav_reader_;
88 };
89
90 class WavFileWriter final : public test::FakeAudioDevice::Renderer {
91 public:
92 WavFileWriter(std::string filename, int sampling_frequency_in_hz)
93 : sampling_frequency_in_hz_(sampling_frequency_in_hz),
94 wav_writer_(filename, sampling_frequency_in_hz, 1) {}
95
96 int SamplingFrequency() const override {
97 return sampling_frequency_in_hz_;
98 }
99
100 bool Render(rtc::ArrayView<const int16_t> data) override {
101 wav_writer_.WriteSamples(data.data(), data.size());
102 return true;
103 }
104
105 private:
106 int sampling_frequency_in_hz_;
107 WavWriter wav_writer_;
108 };
109
110 class DiscardRenderer final : public test::FakeAudioDevice::Renderer {
111 public:
112 DiscardRenderer(int sampling_frequency_in_hz)
113 : sampling_frequency_in_hz_(sampling_frequency_in_hz) {}
114
115 int SamplingFrequency() const override {
116 return sampling_frequency_in_hz_;
117 }
118
119 bool Render(rtc::ArrayView<const int16_t> data) override {
120 return true;
121 }
122
123 private:
124 int sampling_frequency_in_hz_;
125 };
126
27 } // namespace 127 } // namespace
28 namespace test { 128 namespace test {
29 129
30 // Assuming 10ms audio packets.. 130 size_t FakeAudioDevice::SamplesPerFrame(int sampling_frequency_in_hz) {
31 class FakeAudioDevice::PulsedNoiseCapturer { 131 return rtc::CheckedDivExact(sampling_frequency_in_hz, kFramesPerSecond);
32 public: 132 }
33 PulsedNoiseCapturer(size_t num_samples_per_frame, int16_t max_amplitude)
34 : fill_with_zero_(false),
35 random_generator_(1),
36 max_amplitude_(max_amplitude),
37 random_audio_(num_samples_per_frame),
38 silent_audio_(num_samples_per_frame, 0) {
39 RTC_DCHECK_GT(max_amplitude, 0);
40 }
41 133
42 rtc::ArrayView<const int16_t> Capture() { 134 std::unique_ptr<FakeAudioDevice::Capturer>
43 fill_with_zero_ = !fill_with_zero_; 135 FakeAudioDevice::CreatePulsedNoiseCapturer(
44 if (!fill_with_zero_) { 136 int16_t max_amplitude, int sampling_frequency_in_hz) {
45 std::generate(random_audio_.begin(), random_audio_.end(), [&]() { 137 return std::unique_ptr<FakeAudioDevice::Capturer>(
46 return random_generator_.Rand(-max_amplitude_, max_amplitude_); 138 new PulsedNoiseCapturer(max_amplitude, sampling_frequency_in_hz));
47 }); 139 }
48 }
49 return fill_with_zero_ ? silent_audio_ : random_audio_;
50 }
51 140
52 private: 141 std::unique_ptr<FakeAudioDevice::Capturer> FakeAudioDevice::CreateWavFileReader(
53 bool fill_with_zero_; 142 std::string filename, int sampling_frequency_in_hz) {
54 Random random_generator_; 143 return std::unique_ptr<FakeAudioDevice::Capturer>(
55 const int16_t max_amplitude_; 144 new WavFileReader(filename, sampling_frequency_in_hz));
56 std::vector<int16_t> random_audio_; 145 }
57 std::vector<int16_t> silent_audio_;
58 };
59 146
60 FakeAudioDevice::FakeAudioDevice(float speed, 147 std::unique_ptr<FakeAudioDevice::Capturer> FakeAudioDevice::CreateWavFileReader(
61 int sampling_frequency_in_hz, 148 std::string filename) {
62 int16_t max_amplitude) 149 int sampling_frequency_in_hz = WavReader(filename).sample_rate();
63 : sampling_frequency_in_hz_(sampling_frequency_in_hz), 150 return std::unique_ptr<FakeAudioDevice::Capturer>(
64 num_samples_per_frame_( 151 new WavFileReader(filename, sampling_frequency_in_hz));
65 rtc::CheckedDivExact(sampling_frequency_in_hz_, kFramesPerSecond)), 152 }
153
154 std::unique_ptr<FakeAudioDevice::Renderer> FakeAudioDevice::CreateWavFileWriter(
155 std::string filename, int sampling_frequency_in_hz) {
156 return std::unique_ptr<FakeAudioDevice::Renderer>(
157 new WavFileWriter(filename, sampling_frequency_in_hz));
158 }
159
160 std::unique_ptr<FakeAudioDevice::Renderer>
161 FakeAudioDevice::CreateDiscardRenderer(int sampling_frequency_in_hz) {
162 return std::unique_ptr<FakeAudioDevice::Renderer>(
163 new DiscardRenderer(sampling_frequency_in_hz));
164 }
165
166
167 FakeAudioDevice::FakeAudioDevice(std::unique_ptr<Capturer> capturer,
168 std::unique_ptr<Renderer> renderer,
169 float speed)
170 : capturer_(std::move(capturer)),
171 renderer_(std::move(renderer)),
66 speed_(speed), 172 speed_(speed),
67 audio_callback_(nullptr), 173 audio_callback_(nullptr),
68 rendering_(false), 174 rendering_(false),
69 capturing_(false), 175 capturing_(false),
70 capturer_(new FakeAudioDevice::PulsedNoiseCapturer(num_samples_per_frame_, 176 done_rendering_(true, true),
71 max_amplitude)), 177 done_capturing_(true, true),
72 playout_buffer_(num_samples_per_frame_, 0),
73 tick_(EventTimerWrapper::Create()), 178 tick_(EventTimerWrapper::Create()),
74 thread_(FakeAudioDevice::Run, this, "FakeAudioDevice") { 179 thread_(FakeAudioDevice::Run, this, "FakeAudioDevice") {
75 RTC_DCHECK( 180 if (renderer_) {
76 sampling_frequency_in_hz == 8000 || sampling_frequency_in_hz == 16000 || 181 const int sample_rate = renderer_->SamplingFrequency();
77 sampling_frequency_in_hz == 32000 || sampling_frequency_in_hz == 44100 || 182 playout_buffer_.resize(SamplesPerFrame(sample_rate), 0);
78 sampling_frequency_in_hz == 48000); 183 RTC_CHECK(
184 sample_rate == 8000 || sample_rate == 16000 || sample_rate == 32000 ||
185 sample_rate == 44100 || sample_rate == 48000);
186 }
187 if (capturer_) {
188 const int sample_rate = capturer_->SamplingFrequency();
189 RTC_CHECK(
190 sample_rate == 8000 || sample_rate == 16000 || sample_rate == 32000 ||
191 sample_rate == 44100 || sample_rate == 48000);
kwiberg-webrtc 2017/03/13 14:22:28 Do the CHECK first in this function, so you don't
oprypin_webrtc 2017/03/13 15:11:36 I'm checking two different sample rates.
kwiberg-webrtc 2017/03/14 10:02:03 Right, I would have seen that if I hadn't been so
oprypin_webrtc 2017/03/14 11:58:13 Done.
192 }
79 } 193 }
80 194
81 FakeAudioDevice::~FakeAudioDevice() { 195 FakeAudioDevice::~FakeAudioDevice() {
82 StopPlayout(); 196 StopPlayout();
83 StopRecording(); 197 StopRecording();
84 thread_.Stop(); 198 thread_.Stop();
85 } 199 }
86 200
87 int32_t FakeAudioDevice::StartPlayout() { 201 int32_t FakeAudioDevice::StartPlayout() {
88 rtc::CritScope cs(&lock_); 202 rtc::CritScope cs(&lock_);
203 RTC_CHECK(renderer_);
89 rendering_ = true; 204 rendering_ = true;
205 done_rendering_.Reset();
90 return 0; 206 return 0;
91 } 207 }
92 208
93 int32_t FakeAudioDevice::StopPlayout() { 209 int32_t FakeAudioDevice::StopPlayout() {
94 rtc::CritScope cs(&lock_); 210 rtc::CritScope cs(&lock_);
95 rendering_ = false; 211 rendering_ = false;
212 done_rendering_.Set();
96 return 0; 213 return 0;
97 } 214 }
98 215
99 int32_t FakeAudioDevice::StartRecording() { 216 int32_t FakeAudioDevice::StartRecording() {
100 rtc::CritScope cs(&lock_); 217 rtc::CritScope cs(&lock_);
218 RTC_CHECK(capturer_);
101 capturing_ = true; 219 capturing_ = true;
220 done_capturing_.Reset();
102 return 0; 221 return 0;
103 } 222 }
104 223
105 int32_t FakeAudioDevice::StopRecording() { 224 int32_t FakeAudioDevice::StopRecording() {
106 rtc::CritScope cs(&lock_); 225 rtc::CritScope cs(&lock_);
107 capturing_ = false; 226 capturing_ = false;
227 done_capturing_.Set();
108 return 0; 228 return 0;
109 } 229 }
110 230
111 int32_t FakeAudioDevice::Init() { 231 int32_t FakeAudioDevice::Init() {
112 RTC_CHECK(tick_->StartTimer(true, kFrameLengthMs / speed_)); 232 RTC_CHECK(tick_->StartTimer(true, kFrameLengthMs / speed_));
113 thread_.Start(); 233 thread_.Start();
114 thread_.SetPriority(rtc::kHighPriority); 234 thread_.SetPriority(rtc::kHighPriority);
115 return 0; 235 return 0;
116 } 236 }
117 237
118 int32_t FakeAudioDevice::RegisterAudioCallback(AudioTransport* callback) { 238 int32_t FakeAudioDevice::RegisterAudioCallback(AudioTransport* callback) {
119 rtc::CritScope cs(&lock_); 239 rtc::CritScope cs(&lock_);
120 RTC_DCHECK(callback || audio_callback_ != nullptr); 240 RTC_DCHECK(callback || audio_callback_);
121 audio_callback_ = callback; 241 audio_callback_ = callback;
122 return 0; 242 return 0;
123 } 243 }
124 244
125 bool FakeAudioDevice::Playing() const { 245 bool FakeAudioDevice::Playing() const {
126 rtc::CritScope cs(&lock_); 246 rtc::CritScope cs(&lock_);
127 return rendering_; 247 return rendering_;
128 } 248 }
129 249
130 bool FakeAudioDevice::Recording() const { 250 bool FakeAudioDevice::Recording() const {
131 rtc::CritScope cs(&lock_); 251 rtc::CritScope cs(&lock_);
132 return capturing_; 252 return capturing_;
133 } 253 }
134 254
255 bool FakeAudioDevice::WaitForPlayoutEnd(int timeout_ms) {
256 return done_rendering_.Wait(timeout_ms);
257 }
258
259 bool FakeAudioDevice::WaitForRecordingEnd(int timeout_ms) {
260 return done_capturing_.Wait(timeout_ms);
261 }
262
135 bool FakeAudioDevice::Run(void* obj) { 263 bool FakeAudioDevice::Run(void* obj) {
136 static_cast<FakeAudioDevice*>(obj)->ProcessAudio(); 264 static_cast<FakeAudioDevice*>(obj)->ProcessAudio();
137 return true; 265 return true;
138 } 266 }
139 267
140 void FakeAudioDevice::ProcessAudio() { 268 void FakeAudioDevice::ProcessAudio() {
141 { 269 {
142 rtc::CritScope cs(&lock_); 270 rtc::CritScope cs(&lock_);
143 if (capturing_) { 271 if (capturing_) {
144 // Capture 10ms of audio. 2 bytes per sample. 272 // Capture 10ms of audio. 2 bytes per sample.
145 rtc::ArrayView<const int16_t> audio_data = capturer_->Capture(); 273 recording_buffer_.SetSize(
146 uint32_t new_mic_level = 0; 274 SamplesPerFrame(capturer_->SamplingFrequency()));
147 audio_callback_->RecordedDataIsAvailable( 275 const bool keep_capturing = capturer_->Capture(&recording_buffer_);
148 audio_data.data(), audio_data.size(), 2, 1, sampling_frequency_in_hz_, 276 uint32_t new_mic_level;
149 0, 0, 0, false, new_mic_level); 277 if (recording_buffer_.size() > 0) {
kwiberg-webrtc 2017/03/13 14:22:27 This condition is always true, right? (You could
oprypin_webrtc 2017/03/13 15:11:36 It is not true at the end of the recording. Don't
kwiberg-webrtc 2017/03/14 10:02:03 On line 273, you SetSize() the buffer to SamplesPe
oprypin_webrtc 2017/03/14 11:58:13 No. The Capture method is free to change the buffe
kwiberg-webrtc 2017/03/14 13:46:47 Ah, right! Sorry for being dense. However, since
oprypin_webrtc 2017/03/14 14:01:40 Yes, I removed that line in a later patchset. In
kwiberg-webrtc 2017/03/14 14:08:56 Acknowledged.
278 audio_callback_->RecordedDataIsAvailable(
279 recording_buffer_.data(), recording_buffer_.size(), 2, 1,
280 capturer_->SamplingFrequency(), 0, 0, 0, false, new_mic_level);
281 }
282 if (!keep_capturing) {
283 capturing_ = false;
284 done_capturing_.Set();
285 }
150 } 286 }
151 if (rendering_) { 287 if (rendering_) {
152 size_t samples_out = 0; 288 size_t samples_out;
153 int64_t elapsed_time_ms = -1; 289 int64_t elapsed_time_ms;
154 int64_t ntp_time_ms = -1; 290 int64_t ntp_time_ms;
291 const int sampling_frequency = renderer_->SamplingFrequency();
155 audio_callback_->NeedMorePlayData( 292 audio_callback_->NeedMorePlayData(
156 num_samples_per_frame_, 2, 1, sampling_frequency_in_hz_, 293 SamplesPerFrame(sampling_frequency), 2, 1, sampling_frequency,
157 playout_buffer_.data(), samples_out, &elapsed_time_ms, &ntp_time_ms); 294 playout_buffer_.data(), samples_out, &elapsed_time_ms, &ntp_time_ms);
295 const bool keep_rendering = renderer_->Render(
296 rtc::ArrayView<const int16_t>(playout_buffer_.data(), samples_out));
297 if (!keep_rendering) {
298 rendering_ = false;
299 done_rendering_.Set();
300 }
158 } 301 }
159 } 302 }
160 tick_->Wait(WEBRTC_EVENT_INFINITE); 303 tick_->Wait(WEBRTC_EVENT_INFINITE);
161 } 304 }
162 305
163 306
164 } // namespace test 307 } // namespace test
165 } // namespace webrtc 308 } // namespace webrtc
OLDNEW
« webrtc/test/fake_audio_device.h ('K') | « webrtc/test/fake_audio_device.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698