OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (c) 2012 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 "webrtc/tools/agc/agc_manager.h" | |
12 | |
13 #include <assert.h> | |
14 | |
15 #include "webrtc/modules/audio_processing/agc/agc.h" | |
16 #include "webrtc/modules/audio_processing/include/audio_processing.h" | |
17 #include "webrtc/modules/interface/module_common_types.h" | |
18 #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" | |
19 #include "webrtc/system_wrappers/interface/logging.h" | |
20 #include "webrtc/voice_engine/include/voe_external_media.h" | |
21 #include "webrtc/voice_engine/include/voe_volume_control.h" | |
22 | |
23 namespace webrtc { | |
24 | |
25 class AgcManagerVolume : public VolumeCallbacks { | |
26 public: | |
27 // AgcManagerVolume acquires ownership of |volume|. | |
28 explicit AgcManagerVolume(VoEVolumeControl* volume) | |
29 : volume_(volume) { | |
30 } | |
31 | |
32 ~AgcManagerVolume() { | |
33 if (volume_) { | |
34 volume_->Release(); | |
35 } | |
36 } | |
37 | |
38 virtual void SetMicVolume(int volume) { | |
39 if (volume_->SetMicVolume(volume) != 0) { | |
40 LOG_FERR1(LS_WARNING, SetMicVolume, volume); | |
41 } | |
42 } | |
43 | |
44 int GetMicVolume() { | |
45 unsigned int volume = 0; | |
46 if (volume_->GetMicVolume(volume) != 0) { | |
47 LOG_FERR0(LS_WARNING, GetMicVolume); | |
48 return -1; | |
49 } | |
50 return volume; | |
51 } | |
52 | |
53 private: | |
54 VoEVolumeControl* volume_; | |
55 }; | |
56 | |
57 class MediaCallback : public VoEMediaProcess { | |
58 public: | |
59 MediaCallback(AgcManagerDirect* direct, AudioProcessing* audioproc, | |
60 CriticalSectionWrapper* crit) | |
61 : direct_(direct), | |
62 audioproc_(audioproc), | |
63 crit_(crit), | |
64 frame_() { | |
65 } | |
66 | |
67 protected: | |
68 virtual void Process(const int channel, const ProcessingTypes type, | |
69 int16_t audio[], const size_t samples_per_channel, | |
70 const int sample_rate_hz, const bool is_stereo) { | |
71 CriticalSectionScoped cs(crit_); | |
72 if (direct_->capture_muted()) { | |
73 return; | |
74 } | |
75 | |
76 // Extract the first channel. | |
77 const int kMaxSampleRateHz = 48000; | |
78 const int kMaxSamplesPerChannel = kMaxSampleRateHz / 100; | |
79 assert(samples_per_channel < kMaxSamplesPerChannel && | |
80 sample_rate_hz < kMaxSampleRateHz); | |
81 int16_t mono[kMaxSamplesPerChannel]; | |
82 int16_t* mono_ptr = audio; | |
83 if (is_stereo) { | |
84 for (size_t n = 0; n < samples_per_channel; n++) { | |
85 mono[n] = audio[n * 2]; | |
86 } | |
87 mono_ptr = mono; | |
88 } | |
89 | |
90 direct_->Process(mono_ptr, samples_per_channel, sample_rate_hz); | |
91 | |
92 // TODO(ajm): It's unfortunate we have to memcpy to this frame here, but | |
93 // it's needed for use with AudioProcessing. | |
94 frame_.num_channels_ = is_stereo ? 2 : 1; | |
95 frame_.samples_per_channel_ = samples_per_channel; | |
96 frame_.sample_rate_hz_ = sample_rate_hz; | |
97 const size_t length_samples = frame_.num_channels_ * samples_per_channel; | |
98 memcpy(frame_.data_, audio, length_samples * sizeof(int16_t)); | |
99 | |
100 // Apply compression to the audio. | |
101 if (audioproc_->ProcessStream(&frame_) != 0) { | |
102 LOG_FERR0(LS_ERROR, ProcessStream); | |
103 } | |
104 | |
105 // Copy the compressed audio back to voice engine's array. | |
106 memcpy(audio, frame_.data_, length_samples * sizeof(int16_t)); | |
107 } | |
108 | |
109 private: | |
110 AgcManagerDirect* direct_; | |
111 AudioProcessing* audioproc_; | |
112 CriticalSectionWrapper* crit_; | |
113 AudioFrame frame_; | |
114 }; | |
115 | |
116 class PreprocCallback : public VoEMediaProcess { | |
117 public: | |
118 PreprocCallback(AgcManagerDirect* direct, CriticalSectionWrapper* crit) | |
119 : direct_(direct), | |
120 crit_(crit) { | |
121 } | |
122 | |
123 protected: | |
124 virtual void Process(const int channel, const ProcessingTypes type, | |
125 int16_t audio[], const size_t samples_per_channel, | |
126 const int sample_rate_hz, const bool is_stereo) { | |
127 CriticalSectionScoped cs(crit_); | |
128 if (direct_->capture_muted()) { | |
129 return; | |
130 } | |
131 direct_->AnalyzePreProcess(audio, is_stereo ? 2 : 1, samples_per_channel); | |
132 } | |
133 | |
134 private: | |
135 AgcManagerDirect* direct_; | |
136 CriticalSectionWrapper* crit_; | |
137 }; | |
138 | |
139 AgcManager::AgcManager(VoiceEngine* voe) | |
140 : media_(VoEExternalMedia::GetInterface(voe)), | |
141 volume_callbacks_(new AgcManagerVolume(VoEVolumeControl::GetInterface( | |
142 voe))), | |
143 crit_(CriticalSectionWrapper::CreateCriticalSection()), | |
144 enabled_(false), | |
145 initialized_(false) { | |
146 Config config; | |
147 config.Set<ExperimentalAgc>(new ExperimentalAgc(false)); | |
148 audioproc_.reset(AudioProcessing::Create(config)); | |
149 direct_.reset(new AgcManagerDirect(audioproc_->gain_control(), | |
150 volume_callbacks_.get(), | |
151 kAgcStartupMinVolume)); | |
152 media_callback_.reset(new MediaCallback(direct_.get(), | |
153 audioproc_.get(), | |
154 crit_.get())); | |
155 preproc_callback_.reset(new PreprocCallback(direct_.get(), crit_.get())); | |
156 } | |
157 | |
158 AgcManager::AgcManager(VoEExternalMedia* media, | |
159 VoEVolumeControl* volume, | |
160 Agc* agc, | |
161 AudioProcessing* audioproc) | |
162 : media_(media), | |
163 volume_callbacks_(new AgcManagerVolume(volume)), | |
164 crit_(CriticalSectionWrapper::CreateCriticalSection()), | |
165 audioproc_(audioproc), | |
166 direct_(new AgcManagerDirect(agc, | |
167 audioproc_->gain_control(), | |
168 volume_callbacks_.get(), | |
169 kAgcStartupMinVolume)), | |
170 media_callback_( | |
171 new MediaCallback(direct_.get(), audioproc_.get(), crit_.get())), | |
172 preproc_callback_(new PreprocCallback(direct_.get(), crit_.get())), | |
173 enabled_(false), | |
174 initialized_(false) { | |
175 } | |
176 | |
177 AgcManager::AgcManager() | |
178 : media_(NULL), | |
179 enabled_(false), | |
180 initialized_(false) { | |
181 } | |
182 | |
183 AgcManager::~AgcManager() { | |
184 if (media_) { | |
185 if (enabled_) { | |
186 DeregisterCallbacks(); | |
187 } | |
188 media_->Release(); | |
189 } | |
190 } | |
191 | |
192 int AgcManager::Enable(bool enable) { | |
193 if (enable == enabled_) { | |
194 return 0; | |
195 } | |
196 if (!initialized_) { | |
197 CriticalSectionScoped cs(crit_.get()); | |
198 if (audioproc_->gain_control()->Enable(true) != 0) { | |
199 LOG_FERR1(LS_ERROR, gain_control()->Enable, true); | |
200 return -1; | |
201 } | |
202 if (direct_->Initialize() != 0) { | |
203 assert(false); | |
204 return -1; | |
205 } | |
206 initialized_ = true; | |
207 } | |
208 | |
209 if (enable) { | |
210 if (media_->RegisterExternalMediaProcessing(0, kRecordingAllChannelsMixed, | |
211 *media_callback_) != 0) { | |
212 LOG(LS_ERROR) << "Failed to register postproc callback"; | |
213 return -1; | |
214 } | |
215 if (media_->RegisterExternalMediaProcessing(0, kRecordingPreprocessing, | |
216 *preproc_callback_) != 0) { | |
217 LOG(LS_ERROR) << "Failed to register preproc callback"; | |
218 return -1; | |
219 } | |
220 } else { | |
221 if (DeregisterCallbacks() != 0) | |
222 return -1; | |
223 } | |
224 enabled_ = enable; | |
225 return 0; | |
226 } | |
227 | |
228 void AgcManager::CaptureDeviceChanged() { | |
229 CriticalSectionScoped cs(crit_.get()); | |
230 direct_->Initialize(); | |
231 } | |
232 | |
233 void AgcManager::SetCaptureMuted(bool muted) { | |
234 CriticalSectionScoped cs(crit_.get()); | |
235 direct_->SetCaptureMuted(muted); | |
236 } | |
237 | |
238 int AgcManager::DeregisterCallbacks() { | |
239 // DeRegister shares a lock with the Process() callback. This call will block | |
240 // until the callback is finished and it's safe to continue teardown. | |
241 int err = 0; | |
242 if (media_->DeRegisterExternalMediaProcessing(0, | |
243 kRecordingAllChannelsMixed) != 0) { | |
244 LOG(LS_ERROR) << "Failed to deregister postproc callback"; | |
245 err = -1; | |
246 } | |
247 if (media_->DeRegisterExternalMediaProcessing(0, | |
248 kRecordingPreprocessing) != 0) { | |
249 LOG(LS_ERROR) << "Failed to deregister preproc callback"; | |
250 err = -1; | |
251 } | |
252 return err; | |
253 } | |
254 | |
255 } // namespace webrtc | |
OLD | NEW |