blob: a888eb1887167e8da09496c6f70b0679f26b8f94 [file] [log] [blame]
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +00001/*
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
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#include "voice_engine/transmit_mixer.h"
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +000012
kwibergb7f89d62016-02-17 10:04:18 -080013#include <memory>
14
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020015#include "audio/utility/audio_frame_operations.h"
16#include "rtc_base/format_macros.h"
17#include "rtc_base/location.h"
18#include "rtc_base/logging.h"
19#include "system_wrappers/include/event_wrapper.h"
20#include "system_wrappers/include/trace.h"
21#include "voice_engine/channel.h"
22#include "voice_engine/channel_manager.h"
23#include "voice_engine/statistics.h"
24#include "voice_engine/utility.h"
25#include "voice_engine/voe_base_impl.h"
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +000026
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +000027namespace webrtc {
28namespace voe {
29
tommiba08a142017-02-28 08:25:11 -080030#if WEBRTC_VOICE_ENGINE_TYPING_DETECTION
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +000031// TODO(ajm): The thread safety of this is dubious...
tommiba08a142017-02-28 08:25:11 -080032void TransmitMixer::OnPeriodicProcess()
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +000033{
34 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, -1),
35 "TransmitMixer::OnPeriodicProcess()");
36
solenberg302c9782015-11-24 06:28:22 -080037 bool send_typing_noise_warning = false;
38 bool typing_noise_detected = false;
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +000039 {
tommi31fc21f2016-01-21 10:37:37 -080040 rtc::CritScope cs(&_critSect);
solenberg302c9782015-11-24 06:28:22 -080041 if (_typingNoiseWarningPending) {
42 send_typing_noise_warning = true;
43 typing_noise_detected = _typingNoiseDetected;
44 _typingNoiseWarningPending = false;
45 }
46 }
47 if (send_typing_noise_warning) {
tommi31fc21f2016-01-21 10:37:37 -080048 rtc::CritScope cs(&_callbackCritSect);
solenberg302c9782015-11-24 06:28:22 -080049 if (_voiceEngineObserverPtr) {
50 if (typing_noise_detected) {
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +000051 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
52 "TransmitMixer::OnPeriodicProcess() => "
53 "CallbackOnError(VE_TYPING_NOISE_WARNING)");
54 _voiceEngineObserverPtr->CallbackOnError(
55 -1,
56 VE_TYPING_NOISE_WARNING);
57 } else {
58 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
59 "TransmitMixer::OnPeriodicProcess() => "
60 "CallbackOnError(VE_TYPING_NOISE_OFF_WARNING)");
61 _voiceEngineObserverPtr->CallbackOnError(
62 -1,
63 VE_TYPING_NOISE_OFF_WARNING);
64 }
65 }
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +000066 }
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +000067}
tommiba08a142017-02-28 08:25:11 -080068#endif // WEBRTC_VOICE_ENGINE_TYPING_DETECTION
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +000069
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +000070int32_t
71TransmitMixer::Create(TransmitMixer*& mixer, uint32_t instanceId)
72{
73 WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(instanceId, -1),
74 "TransmitMixer::Create(instanceId=%d)", instanceId);
75 mixer = new TransmitMixer(instanceId);
76 if (mixer == NULL)
77 {
78 WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(instanceId, -1),
79 "TransmitMixer::Create() unable to allocate memory"
80 "for mixer");
81 return -1;
82 }
83 return 0;
84}
85
86void
87TransmitMixer::Destroy(TransmitMixer*& mixer)
88{
89 if (mixer)
90 {
91 delete mixer;
92 mixer = NULL;
93 }
94}
95
96TransmitMixer::TransmitMixer(uint32_t instanceId) :
tommiba08a142017-02-28 08:25:11 -080097#if WEBRTC_VOICE_ENGINE_TYPING_DETECTION
98 _monitorModule(this),
99#endif
solenberg76377c52017-02-21 00:54:31 -0800100 _instanceId(instanceId)
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000101{
102 WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId, -1),
103 "TransmitMixer::TransmitMixer() - ctor");
104}
105
106TransmitMixer::~TransmitMixer()
107{
108 WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId, -1),
109 "TransmitMixer::~TransmitMixer() - dtor");
tommiba08a142017-02-28 08:25:11 -0800110#if WEBRTC_VOICE_ENGINE_TYPING_DETECTION
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000111 if (_processThreadPtr)
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000112 _processThreadPtr->DeRegisterModule(&_monitorModule);
tommiba08a142017-02-28 08:25:11 -0800113#endif
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000114}
115
116int32_t
117TransmitMixer::SetEngineInformation(ProcessThread& processThread,
118 Statistics& engineStatistics,
119 ChannelManager& channelManager)
120{
121 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
122 "TransmitMixer::SetEngineInformation()");
123
124 _processThreadPtr = &processThread;
125 _engineStatisticsPtr = &engineStatistics;
126 _channelManagerPtr = &channelManager;
127
tommiba08a142017-02-28 08:25:11 -0800128#if WEBRTC_VOICE_ENGINE_TYPING_DETECTION
tommidea489f2017-03-03 03:20:24 -0800129 _processThreadPtr->RegisterModule(&_monitorModule, RTC_FROM_HERE);
tommiba08a142017-02-28 08:25:11 -0800130#endif
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000131 return 0;
132}
133
134int32_t
135TransmitMixer::RegisterVoiceEngineObserver(VoiceEngineObserver& observer)
136{
137 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
138 "TransmitMixer::RegisterVoiceEngineObserver()");
tommi31fc21f2016-01-21 10:37:37 -0800139 rtc::CritScope cs(&_callbackCritSect);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000140
141 if (_voiceEngineObserverPtr)
142 {
143 _engineStatisticsPtr->SetLastError(
144 VE_INVALID_OPERATION, kTraceError,
145 "RegisterVoiceEngineObserver() observer already enabled");
146 return -1;
147 }
148 _voiceEngineObserverPtr = &observer;
149 return 0;
150}
151
152int32_t
153TransmitMixer::SetAudioProcessingModule(AudioProcessing* audioProcessingModule)
154{
155 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
156 "TransmitMixer::SetAudioProcessingModule("
157 "audioProcessingModule=0x%x)",
158 audioProcessingModule);
159 audioproc_ = audioProcessingModule;
160 return 0;
161}
162
Peter Kasting69558702016-01-12 16:26:35 -0800163void TransmitMixer::GetSendCodecInfo(int* max_sample_rate,
164 size_t* max_channels) {
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000165 *max_sample_rate = 8000;
166 *max_channels = 1;
167 for (ChannelManager::Iterator it(_channelManagerPtr); it.IsValid();
168 it.Increment()) {
169 Channel* channel = it.GetChannel();
170 if (channel->Sending()) {
171 CodecInst codec;
ossu950c1c92017-07-11 08:19:31 -0700172 // TODO(ossu): Investigate how this could happen. b/62909493
173 if (channel->GetSendCodec(codec) == 0) {
174 *max_sample_rate = std::max(*max_sample_rate, codec.plfreq);
175 *max_channels = std::max(*max_channels, codec.channels);
176 } else {
177 LOG(LS_WARNING) << "Unable to get send codec for channel "
178 << channel->ChannelId();
179 RTC_NOTREACHED();
180 }
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000181 }
182 }
183}
184
185int32_t
186TransmitMixer::PrepareDemux(const void* audioSamples,
Peter Kastingdce40cf2015-08-24 14:52:23 -0700187 size_t nSamples,
Peter Kasting69558702016-01-12 16:26:35 -0800188 size_t nChannels,
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000189 uint32_t samplesPerSec,
190 uint16_t totalDelayMS,
191 int32_t clockDrift,
192 uint16_t currentMicLevel,
193 bool keyPressed)
194{
195 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, -1),
Peter Kastingdce40cf2015-08-24 14:52:23 -0700196 "TransmitMixer::PrepareDemux(nSamples=%" PRIuS ", "
Peter Kasting69558702016-01-12 16:26:35 -0800197 "nChannels=%" PRIuS ", samplesPerSec=%u, totalDelayMS=%u, "
Peter Kastingdce40cf2015-08-24 14:52:23 -0700198 "clockDrift=%d, currentMicLevel=%u)",
199 nSamples, nChannels, samplesPerSec, totalDelayMS, clockDrift,
200 currentMicLevel);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000201
202 // --- Resample input audio and create/store the initial audio frame
203 GenerateAudioFrame(static_cast<const int16_t*>(audioSamples),
204 nSamples,
205 nChannels,
206 samplesPerSec);
207
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000208 // --- Near-end audio processing.
209 ProcessAudio(totalDelayMS, clockDrift, currentMicLevel, keyPressed);
210
211 if (swap_stereo_channels_ && stereo_codec_)
212 // Only bother swapping if we're using a stereo codec.
213 AudioFrameOperations::SwapStereoChannels(&_audioFrame);
214
215 // --- Annoying typing detection (utilizes the APM/VAD decision)
henrik.lundinf00082d2016-12-05 02:22:12 -0800216#if WEBRTC_VOICE_ENGINE_TYPING_DETECTION
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000217 TypingDetection(keyPressed);
218#endif
219
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000220 // --- Measure audio level of speech after all processing.
zsteine76bd3a2017-07-14 12:17:49 -0700221 double sample_duration = static_cast<double>(nSamples) / samplesPerSec;
zstein3c451862017-07-20 09:57:42 -0700222 _audioLevel.ComputeLevel(_audioFrame, sample_duration);
zsteine76bd3a2017-07-14 12:17:49 -0700223
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000224 return 0;
225}
226
henrikaec6fbd22017-03-31 05:43:36 -0700227void TransmitMixer::ProcessAndEncodeAudio() {
228 RTC_DCHECK_GT(_audioFrame.samples_per_channel_, 0);
229 for (ChannelManager::Iterator it(_channelManagerPtr); it.IsValid();
230 it.Increment()) {
231 Channel* const channel = it.GetChannel();
232 if (channel->Sending()) {
233 channel->ProcessAndEncodeAudio(_audioFrame);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000234 }
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000235 }
236}
237
238uint32_t TransmitMixer::CaptureLevel() const
239{
240 return _captureLevel;
241}
242
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000243int32_t
244TransmitMixer::StopSend()
245{
246 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
247 "TransmitMixer::StopSend()");
248 _audioLevel.Clear();
249 return 0;
250}
251
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000252int8_t TransmitMixer::AudioLevel() const
253{
254 // Speech + file level [0,9]
255 return _audioLevel.Level();
256}
257
258int16_t TransmitMixer::AudioLevelFullRange() const
259{
260 // Speech + file level [0,32767]
261 return _audioLevel.LevelFullRange();
262}
263
zsteine76bd3a2017-07-14 12:17:49 -0700264double TransmitMixer::GetTotalInputEnergy() const {
zstein3c451862017-07-20 09:57:42 -0700265 return _audioLevel.TotalEnergy();
zsteine76bd3a2017-07-14 12:17:49 -0700266}
267
268double TransmitMixer::GetTotalInputDuration() const {
zstein3c451862017-07-20 09:57:42 -0700269 return _audioLevel.TotalDuration();
zsteine76bd3a2017-07-14 12:17:49 -0700270}
271
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000272void TransmitMixer::GenerateAudioFrame(const int16_t* audio,
Peter Kastingdce40cf2015-08-24 14:52:23 -0700273 size_t samples_per_channel,
Peter Kasting69558702016-01-12 16:26:35 -0800274 size_t num_channels,
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000275 int sample_rate_hz) {
276 int codec_rate;
Peter Kasting69558702016-01-12 16:26:35 -0800277 size_t num_codec_channels;
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000278 GetSendCodecInfo(&codec_rate, &num_codec_channels);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000279 stereo_codec_ = num_codec_channels == 2;
280
Alejandro Luebscdfe20b2015-09-23 12:49:12 -0700281 // We want to process at the lowest rate possible without losing information.
282 // Choose the lowest native rate at least equal to the input and codec rates.
283 const int min_processing_rate = std::min(sample_rate_hz, codec_rate);
284 for (size_t i = 0; i < AudioProcessing::kNumNativeSampleRates; ++i) {
285 _audioFrame.sample_rate_hz_ = AudioProcessing::kNativeSampleRatesHz[i];
286 if (_audioFrame.sample_rate_hz_ >= min_processing_rate) {
287 break;
288 }
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000289 }
Alejandro Luebscdfe20b2015-09-23 12:49:12 -0700290 _audioFrame.num_channels_ = std::min(num_channels, num_codec_channels);
291 RemixAndResample(audio, samples_per_channel, num_channels, sample_rate_hz,
292 &resampler_, &_audioFrame);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000293}
294
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000295void TransmitMixer::ProcessAudio(int delay_ms, int clock_drift,
296 int current_mic_level, bool key_pressed) {
297 if (audioproc_->set_stream_delay_ms(delay_ms) != 0) {
pbosad856222015-11-27 09:48:36 -0800298 // Silently ignore this failure to avoid flooding the logs.
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000299 }
300
301 GainControl* agc = audioproc_->gain_control();
302 if (agc->set_stream_analog_level(current_mic_level) != 0) {
pbosad856222015-11-27 09:48:36 -0800303 LOG(LS_ERROR) << "set_stream_analog_level failed: current_mic_level = "
304 << current_mic_level;
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000305 assert(false);
306 }
307
308 EchoCancellation* aec = audioproc_->echo_cancellation();
309 if (aec->is_drift_compensation_enabled()) {
310 aec->set_stream_drift_samples(clock_drift);
311 }
312
313 audioproc_->set_stream_key_pressed(key_pressed);
314
315 int err = audioproc_->ProcessStream(&_audioFrame);
316 if (err != 0) {
317 LOG(LS_ERROR) << "ProcessStream() error: " << err;
318 assert(false);
319 }
320
321 // Store new capture level. Only updated when analog AGC is enabled.
322 _captureLevel = agc->stream_analog_level();
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000323}
324
henrik.lundinf00082d2016-12-05 02:22:12 -0800325#if WEBRTC_VOICE_ENGINE_TYPING_DETECTION
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000326void TransmitMixer::TypingDetection(bool keyPressed)
327{
328 // We let the VAD determine if we're using this feature or not.
329 if (_audioFrame.vad_activity_ == AudioFrame::kVadUnknown) {
330 return;
331 }
332
333 bool vadActive = _audioFrame.vad_activity_ == AudioFrame::kVadActive;
334 if (_typingDetection.Process(keyPressed, vadActive)) {
tommi31fc21f2016-01-21 10:37:37 -0800335 rtc::CritScope cs(&_critSect);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000336 _typingNoiseWarningPending = true;
337 _typingNoiseDetected = true;
338 } else {
tommi31fc21f2016-01-21 10:37:37 -0800339 rtc::CritScope cs(&_critSect);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000340 // If there is already a warning pending, do not change the state.
341 // Otherwise set a warning pending if last callback was for noise detected.
342 if (!_typingNoiseWarningPending && _typingNoiseDetected) {
343 _typingNoiseWarningPending = true;
344 _typingNoiseDetected = false;
345 }
346 }
347}
348#endif
349
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000350void TransmitMixer::EnableStereoChannelSwapping(bool enable) {
351 swap_stereo_channels_ = enable;
352}
353
354bool TransmitMixer::IsStereoChannelSwappingEnabled() {
355 return swap_stereo_channels_;
356}
357
358} // namespace voe
359} // namespace webrtc