• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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