1 /*
2 * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
3 *
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
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11 #include "modules/audio_processing/test/fake_recording_device.h"
12
13 #include <algorithm>
14 #include <memory>
15
16 #include "absl/types/optional.h"
17 #include "modules/audio_processing/agc/gain_map_internal.h"
18 #include "rtc_base/logging.h"
19 #include "rtc_base/numerics/safe_conversions.h"
20 #include "rtc_base/numerics/safe_minmax.h"
21
22 namespace webrtc {
23 namespace test {
24
25 namespace {
26
27 constexpr float kFloatSampleMin = -32768.f;
28 constexpr float kFloatSampleMax = 32767.0f;
29
30 } // namespace
31
32 // Abstract class for the different fake recording devices.
33 class FakeRecordingDeviceWorker {
34 public:
FakeRecordingDeviceWorker(const int initial_mic_level)35 explicit FakeRecordingDeviceWorker(const int initial_mic_level)
36 : mic_level_(initial_mic_level) {}
mic_level() const37 int mic_level() const { return mic_level_; }
set_mic_level(const int level)38 void set_mic_level(const int level) { mic_level_ = level; }
set_undo_mic_level(const int level)39 void set_undo_mic_level(const int level) { undo_mic_level_ = level; }
40 virtual ~FakeRecordingDeviceWorker() = default;
41 virtual void ModifyBufferInt16(rtc::ArrayView<int16_t> buffer) = 0;
42 virtual void ModifyBufferFloat(ChannelBuffer<float>* buffer) = 0;
43
44 protected:
45 // Mic level to simulate.
46 int mic_level_;
47 // Optional mic level to undo.
48 absl::optional<int> undo_mic_level_;
49 };
50
51 namespace {
52
53 // Identity fake recording device. The samples are not modified, which is
54 // equivalent to a constant gain curve at 1.0 - only used for testing.
55 class FakeRecordingDeviceIdentity final : public FakeRecordingDeviceWorker {
56 public:
FakeRecordingDeviceIdentity(const int initial_mic_level)57 explicit FakeRecordingDeviceIdentity(const int initial_mic_level)
58 : FakeRecordingDeviceWorker(initial_mic_level) {}
59 ~FakeRecordingDeviceIdentity() override = default;
ModifyBufferInt16(rtc::ArrayView<int16_t> buffer)60 void ModifyBufferInt16(rtc::ArrayView<int16_t> buffer) override {}
ModifyBufferFloat(ChannelBuffer<float> * buffer)61 void ModifyBufferFloat(ChannelBuffer<float>* buffer) override {}
62 };
63
64 // Linear fake recording device. The gain curve is a linear function mapping the
65 // mic levels range [0, 255] to [0.0, 1.0].
66 class FakeRecordingDeviceLinear final : public FakeRecordingDeviceWorker {
67 public:
FakeRecordingDeviceLinear(const int initial_mic_level)68 explicit FakeRecordingDeviceLinear(const int initial_mic_level)
69 : FakeRecordingDeviceWorker(initial_mic_level) {}
70 ~FakeRecordingDeviceLinear() override = default;
ModifyBufferInt16(rtc::ArrayView<int16_t> buffer)71 void ModifyBufferInt16(rtc::ArrayView<int16_t> buffer) override {
72 const size_t number_of_samples = buffer.size();
73 int16_t* data = buffer.data();
74 // If an undo level is specified, virtually restore the unmodified
75 // microphone level; otherwise simulate the mic gain only.
76 const float divisor =
77 (undo_mic_level_ && *undo_mic_level_ > 0) ? *undo_mic_level_ : 255.f;
78 for (size_t i = 0; i < number_of_samples; ++i) {
79 data[i] = rtc::saturated_cast<int16_t>(data[i] * mic_level_ / divisor);
80 }
81 }
ModifyBufferFloat(ChannelBuffer<float> * buffer)82 void ModifyBufferFloat(ChannelBuffer<float>* buffer) override {
83 // If an undo level is specified, virtually restore the unmodified
84 // microphone level; otherwise simulate the mic gain only.
85 const float divisor =
86 (undo_mic_level_ && *undo_mic_level_ > 0) ? *undo_mic_level_ : 255.f;
87 for (size_t c = 0; c < buffer->num_channels(); ++c) {
88 for (size_t i = 0; i < buffer->num_frames(); ++i) {
89 buffer->channels()[c][i] =
90 rtc::SafeClamp(buffer->channels()[c][i] * mic_level_ / divisor,
91 kFloatSampleMin, kFloatSampleMax);
92 }
93 }
94 }
95 };
96
ComputeAgc1LinearFactor(const absl::optional<int> & undo_mic_level,int mic_level)97 float ComputeAgc1LinearFactor(const absl::optional<int>& undo_mic_level,
98 int mic_level) {
99 // If an undo level is specified, virtually restore the unmodified
100 // microphone level; otherwise simulate the mic gain only.
101 const int undo_level =
102 (undo_mic_level && *undo_mic_level > 0) ? *undo_mic_level : 100;
103 return DbToRatio(kGainMap[mic_level] - kGainMap[undo_level]);
104 }
105
106 // Roughly dB-scale fake recording device. Valid levels are [0, 255]. The mic
107 // applies a gain from kGainMap in agc/gain_map_internal.h.
108 class FakeRecordingDeviceAgc1 final : public FakeRecordingDeviceWorker {
109 public:
FakeRecordingDeviceAgc1(const int initial_mic_level)110 explicit FakeRecordingDeviceAgc1(const int initial_mic_level)
111 : FakeRecordingDeviceWorker(initial_mic_level) {}
112 ~FakeRecordingDeviceAgc1() override = default;
ModifyBufferInt16(rtc::ArrayView<int16_t> buffer)113 void ModifyBufferInt16(rtc::ArrayView<int16_t> buffer) override {
114 const float scaling_factor =
115 ComputeAgc1LinearFactor(undo_mic_level_, mic_level_);
116 const size_t number_of_samples = buffer.size();
117 int16_t* data = buffer.data();
118 for (size_t i = 0; i < number_of_samples; ++i) {
119 data[i] = rtc::saturated_cast<int16_t>(data[i] * scaling_factor);
120 }
121 }
ModifyBufferFloat(ChannelBuffer<float> * buffer)122 void ModifyBufferFloat(ChannelBuffer<float>* buffer) override {
123 const float scaling_factor =
124 ComputeAgc1LinearFactor(undo_mic_level_, mic_level_);
125 for (size_t c = 0; c < buffer->num_channels(); ++c) {
126 for (size_t i = 0; i < buffer->num_frames(); ++i) {
127 buffer->channels()[c][i] =
128 rtc::SafeClamp(buffer->channels()[c][i] * scaling_factor,
129 kFloatSampleMin, kFloatSampleMax);
130 }
131 }
132 }
133 };
134
135 } // namespace
136
FakeRecordingDevice(int initial_mic_level,int device_kind)137 FakeRecordingDevice::FakeRecordingDevice(int initial_mic_level,
138 int device_kind) {
139 switch (device_kind) {
140 case 0:
141 worker_ =
142 std::make_unique<FakeRecordingDeviceIdentity>(initial_mic_level);
143 break;
144 case 1:
145 worker_ = std::make_unique<FakeRecordingDeviceLinear>(initial_mic_level);
146 break;
147 case 2:
148 worker_ = std::make_unique<FakeRecordingDeviceAgc1>(initial_mic_level);
149 break;
150 default:
151 RTC_NOTREACHED();
152 break;
153 }
154 }
155
156 FakeRecordingDevice::~FakeRecordingDevice() = default;
157
MicLevel() const158 int FakeRecordingDevice::MicLevel() const {
159 RTC_CHECK(worker_);
160 return worker_->mic_level();
161 }
162
SetMicLevel(const int level)163 void FakeRecordingDevice::SetMicLevel(const int level) {
164 RTC_CHECK(worker_);
165 if (level != worker_->mic_level())
166 RTC_LOG(LS_INFO) << "Simulate mic level update: " << level;
167 worker_->set_mic_level(level);
168 }
169
SetUndoMicLevel(const int level)170 void FakeRecordingDevice::SetUndoMicLevel(const int level) {
171 RTC_DCHECK(worker_);
172 // TODO(alessiob): The behavior with undo level equal to zero is not clear yet
173 // and will be defined in future CLs once more FakeRecordingDeviceWorker
174 // implementations need to be added.
175 RTC_CHECK(level > 0) << "Zero undo mic level is unsupported";
176 worker_->set_undo_mic_level(level);
177 }
178
SimulateAnalogGain(rtc::ArrayView<int16_t> buffer)179 void FakeRecordingDevice::SimulateAnalogGain(rtc::ArrayView<int16_t> buffer) {
180 RTC_DCHECK(worker_);
181 worker_->ModifyBufferInt16(buffer);
182 }
183
SimulateAnalogGain(ChannelBuffer<float> * buffer)184 void FakeRecordingDevice::SimulateAnalogGain(ChannelBuffer<float>* buffer) {
185 RTC_DCHECK(worker_);
186 worker_->ModifyBufferFloat(buffer);
187 }
188
189 } // namespace test
190 } // namespace webrtc
191