OLD | NEW |
---|---|
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 |
OLD | NEW |