blob: 06f37c2798ef7a5a0ae98f95fadaf1cf9b2c3397 [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
11#include "webrtc/voice_engine/transmit_mixer.h"
12
kwibergb7f89d62016-02-17 10:04:18 -080013#include <memory>
14
aleloi6321b492016-12-05 01:46:09 -080015#include "webrtc/audio/utility/audio_frame_operations.h"
Edward Lemurc20978e2017-07-06 19:44:34 +020016#include "webrtc/rtc_base/format_macros.h"
17#include "webrtc/rtc_base/location.h"
18#include "webrtc/rtc_base/logging.h"
Henrik Kjellander98f53512015-10-28 18:17:40 +010019#include "webrtc/system_wrappers/include/event_wrapper.h"
Henrik Kjellander98f53512015-10-28 18:17:40 +010020#include "webrtc/system_wrappers/include/trace.h"
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +000021#include "webrtc/voice_engine/channel.h"
22#include "webrtc/voice_engine/channel_manager.h"
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +000023#include "webrtc/voice_engine/statistics.h"
24#include "webrtc/voice_engine/utility.h"
25#include "webrtc/voice_engine/voe_base_impl.h"
26
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
70void TransmitMixer::PlayNotification(int32_t id,
71 uint32_t durationMs)
72{
73 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, -1),
74 "TransmitMixer::PlayNotification(id=%d, durationMs=%d)",
75 id, durationMs);
76
77 // Not implement yet
78}
79
80void TransmitMixer::RecordNotification(int32_t id,
81 uint32_t durationMs)
82{
83 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
84 "TransmitMixer::RecordNotification(id=%d, durationMs=%d)",
85 id, durationMs);
86
87 // Not implement yet
88}
89
90void TransmitMixer::PlayFileEnded(int32_t id)
91{
92 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, -1),
93 "TransmitMixer::PlayFileEnded(id=%d)", id);
94
95 assert(id == _filePlayerId);
96
tommi31fc21f2016-01-21 10:37:37 -080097 rtc::CritScope cs(&_critSect);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +000098
99 _filePlaying = false;
100 WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId, -1),
101 "TransmitMixer::PlayFileEnded() =>"
102 "file player module is shutdown");
103}
104
105void
106TransmitMixer::RecordFileEnded(int32_t id)
107{
108 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, -1),
109 "TransmitMixer::RecordFileEnded(id=%d)", id);
110
111 if (id == _fileRecorderId)
112 {
tommi31fc21f2016-01-21 10:37:37 -0800113 rtc::CritScope cs(&_critSect);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000114 _fileRecording = false;
115 WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId, -1),
116 "TransmitMixer::RecordFileEnded() => fileRecorder module"
117 "is shutdown");
118 } else if (id == _fileCallRecorderId)
119 {
tommi31fc21f2016-01-21 10:37:37 -0800120 rtc::CritScope cs(&_critSect);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000121 _fileCallRecording = false;
122 WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId, -1),
123 "TransmitMixer::RecordFileEnded() => fileCallRecorder"
124 "module is shutdown");
125 }
126}
127
128int32_t
129TransmitMixer::Create(TransmitMixer*& mixer, uint32_t instanceId)
130{
131 WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(instanceId, -1),
132 "TransmitMixer::Create(instanceId=%d)", instanceId);
133 mixer = new TransmitMixer(instanceId);
134 if (mixer == NULL)
135 {
136 WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(instanceId, -1),
137 "TransmitMixer::Create() unable to allocate memory"
138 "for mixer");
139 return -1;
140 }
141 return 0;
142}
143
144void
145TransmitMixer::Destroy(TransmitMixer*& mixer)
146{
147 if (mixer)
148 {
149 delete mixer;
150 mixer = NULL;
151 }
152}
153
154TransmitMixer::TransmitMixer(uint32_t instanceId) :
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000155 // Avoid conflict with other channels by adding 1024 - 1026,
156 // won't use as much as 1024 channels.
157 _filePlayerId(instanceId + 1024),
158 _fileRecorderId(instanceId + 1025),
159 _fileCallRecorderId(instanceId + 1026),
tommiba08a142017-02-28 08:25:11 -0800160#if WEBRTC_VOICE_ENGINE_TYPING_DETECTION
161 _monitorModule(this),
162#endif
solenberg76377c52017-02-21 00:54:31 -0800163 _instanceId(instanceId)
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000164{
165 WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId, -1),
166 "TransmitMixer::TransmitMixer() - ctor");
167}
168
169TransmitMixer::~TransmitMixer()
170{
171 WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId, -1),
172 "TransmitMixer::~TransmitMixer() - dtor");
tommiba08a142017-02-28 08:25:11 -0800173#if WEBRTC_VOICE_ENGINE_TYPING_DETECTION
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000174 if (_processThreadPtr)
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000175 _processThreadPtr->DeRegisterModule(&_monitorModule);
tommiba08a142017-02-28 08:25:11 -0800176#endif
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000177 {
tommi31fc21f2016-01-21 10:37:37 -0800178 rtc::CritScope cs(&_critSect);
kwiberg5a25d952016-08-17 07:31:12 -0700179 if (file_recorder_) {
180 file_recorder_->RegisterModuleFileCallback(NULL);
181 file_recorder_->StopRecording();
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000182 }
kwiberg5a25d952016-08-17 07:31:12 -0700183 if (file_call_recorder_) {
184 file_call_recorder_->RegisterModuleFileCallback(NULL);
185 file_call_recorder_->StopRecording();
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000186 }
kwiberg5a25d952016-08-17 07:31:12 -0700187 if (file_player_) {
188 file_player_->RegisterModuleFileCallback(NULL);
189 file_player_->StopPlayingFile();
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000190 }
191 }
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000192}
193
194int32_t
195TransmitMixer::SetEngineInformation(ProcessThread& processThread,
196 Statistics& engineStatistics,
197 ChannelManager& channelManager)
198{
199 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
200 "TransmitMixer::SetEngineInformation()");
201
202 _processThreadPtr = &processThread;
203 _engineStatisticsPtr = &engineStatistics;
204 _channelManagerPtr = &channelManager;
205
tommiba08a142017-02-28 08:25:11 -0800206#if WEBRTC_VOICE_ENGINE_TYPING_DETECTION
tommidea489f2017-03-03 03:20:24 -0800207 _processThreadPtr->RegisterModule(&_monitorModule, RTC_FROM_HERE);
tommiba08a142017-02-28 08:25:11 -0800208#endif
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000209 return 0;
210}
211
212int32_t
213TransmitMixer::RegisterVoiceEngineObserver(VoiceEngineObserver& observer)
214{
215 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
216 "TransmitMixer::RegisterVoiceEngineObserver()");
tommi31fc21f2016-01-21 10:37:37 -0800217 rtc::CritScope cs(&_callbackCritSect);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000218
219 if (_voiceEngineObserverPtr)
220 {
221 _engineStatisticsPtr->SetLastError(
222 VE_INVALID_OPERATION, kTraceError,
223 "RegisterVoiceEngineObserver() observer already enabled");
224 return -1;
225 }
226 _voiceEngineObserverPtr = &observer;
227 return 0;
228}
229
230int32_t
231TransmitMixer::SetAudioProcessingModule(AudioProcessing* audioProcessingModule)
232{
233 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
234 "TransmitMixer::SetAudioProcessingModule("
235 "audioProcessingModule=0x%x)",
236 audioProcessingModule);
237 audioproc_ = audioProcessingModule;
238 return 0;
239}
240
Peter Kasting69558702016-01-12 16:26:35 -0800241void TransmitMixer::GetSendCodecInfo(int* max_sample_rate,
242 size_t* max_channels) {
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000243 *max_sample_rate = 8000;
244 *max_channels = 1;
245 for (ChannelManager::Iterator it(_channelManagerPtr); it.IsValid();
246 it.Increment()) {
247 Channel* channel = it.GetChannel();
248 if (channel->Sending()) {
249 CodecInst codec;
ossu950c1c92017-07-11 08:19:31 -0700250 // TODO(ossu): Investigate how this could happen. b/62909493
251 if (channel->GetSendCodec(codec) == 0) {
252 *max_sample_rate = std::max(*max_sample_rate, codec.plfreq);
253 *max_channels = std::max(*max_channels, codec.channels);
254 } else {
255 LOG(LS_WARNING) << "Unable to get send codec for channel "
256 << channel->ChannelId();
257 RTC_NOTREACHED();
258 }
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000259 }
260 }
261}
262
263int32_t
264TransmitMixer::PrepareDemux(const void* audioSamples,
Peter Kastingdce40cf2015-08-24 14:52:23 -0700265 size_t nSamples,
Peter Kasting69558702016-01-12 16:26:35 -0800266 size_t nChannels,
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000267 uint32_t samplesPerSec,
268 uint16_t totalDelayMS,
269 int32_t clockDrift,
270 uint16_t currentMicLevel,
271 bool keyPressed)
272{
273 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, -1),
Peter Kastingdce40cf2015-08-24 14:52:23 -0700274 "TransmitMixer::PrepareDemux(nSamples=%" PRIuS ", "
Peter Kasting69558702016-01-12 16:26:35 -0800275 "nChannels=%" PRIuS ", samplesPerSec=%u, totalDelayMS=%u, "
Peter Kastingdce40cf2015-08-24 14:52:23 -0700276 "clockDrift=%d, currentMicLevel=%u)",
277 nSamples, nChannels, samplesPerSec, totalDelayMS, clockDrift,
278 currentMicLevel);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000279
280 // --- Resample input audio and create/store the initial audio frame
281 GenerateAudioFrame(static_cast<const int16_t*>(audioSamples),
282 nSamples,
283 nChannels,
284 samplesPerSec);
285
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000286 // --- Near-end audio processing.
287 ProcessAudio(totalDelayMS, clockDrift, currentMicLevel, keyPressed);
288
289 if (swap_stereo_channels_ && stereo_codec_)
290 // Only bother swapping if we're using a stereo codec.
291 AudioFrameOperations::SwapStereoChannels(&_audioFrame);
292
293 // --- Annoying typing detection (utilizes the APM/VAD decision)
henrik.lundinf00082d2016-12-05 02:22:12 -0800294#if WEBRTC_VOICE_ENGINE_TYPING_DETECTION
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000295 TypingDetection(keyPressed);
296#endif
297
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000298 // --- Mix with file (does not affect the mixing frequency)
299 if (_filePlaying)
300 {
301 MixOrReplaceAudioWithFile(_audioFrame.sample_rate_hz_);
302 }
303
304 // --- Record to file
305 bool file_recording = false;
306 {
tommi31fc21f2016-01-21 10:37:37 -0800307 rtc::CritScope cs(&_critSect);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000308 file_recording = _fileRecording;
309 }
310 if (file_recording)
311 {
312 RecordAudioToFile(_audioFrame.sample_rate_hz_);
313 }
314
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000315 // --- Measure audio level of speech after all processing.
316 _audioLevel.ComputeLevel(_audioFrame);
zsteine76bd3a2017-07-14 12:17:49 -0700317
318 // See the description for "totalAudioEnergy" in the WebRTC stats spec
319 // (https://w3c.github.io/webrtc-stats/#dom-rtcmediastreamtrackstats-totalaudioenergy)
320 // for an explanation of these formulas. In short, we need a value that can
321 // be used to compute RMS audio levels over different time intervals, by
322 // taking the difference between the results from two getStats calls. To do
323 // this, the value needs to be of units "squared sample value * time".
324 double additional_energy =
325 static_cast<double>(_audioLevel.LevelFullRange()) / INT16_MAX;
326 additional_energy *= additional_energy;
327 double sample_duration = static_cast<double>(nSamples) / samplesPerSec;
328 totalInputEnergy_ += additional_energy * sample_duration;
329 totalInputDuration_ += sample_duration;
330
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000331 return 0;
332}
333
henrikaec6fbd22017-03-31 05:43:36 -0700334void TransmitMixer::ProcessAndEncodeAudio() {
335 RTC_DCHECK_GT(_audioFrame.samples_per_channel_, 0);
336 for (ChannelManager::Iterator it(_channelManagerPtr); it.IsValid();
337 it.Increment()) {
338 Channel* const channel = it.GetChannel();
339 if (channel->Sending()) {
340 channel->ProcessAndEncodeAudio(_audioFrame);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000341 }
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000342 }
343}
344
345uint32_t TransmitMixer::CaptureLevel() const
346{
347 return _captureLevel;
348}
349
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000350int32_t
351TransmitMixer::StopSend()
352{
353 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
354 "TransmitMixer::StopSend()");
355 _audioLevel.Clear();
356 return 0;
357}
358
359int TransmitMixer::StartPlayingFileAsMicrophone(const char* fileName,
360 bool loop,
361 FileFormats format,
362 int startPosition,
363 float volumeScaling,
364 int stopPosition,
365 const CodecInst* codecInst)
366{
367 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
368 "TransmitMixer::StartPlayingFileAsMicrophone("
369 "fileNameUTF8[]=%s,loop=%d, format=%d, volumeScaling=%5.3f,"
370 " startPosition=%d, stopPosition=%d)", fileName, loop,
371 format, volumeScaling, startPosition, stopPosition);
372
373 if (_filePlaying)
374 {
375 _engineStatisticsPtr->SetLastError(
376 VE_ALREADY_PLAYING, kTraceWarning,
377 "StartPlayingFileAsMicrophone() is already playing");
378 return 0;
379 }
380
tommi31fc21f2016-01-21 10:37:37 -0800381 rtc::CritScope cs(&_critSect);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000382
383 // Destroy the old instance
kwiberg5a25d952016-08-17 07:31:12 -0700384 if (file_player_) {
385 file_player_->RegisterModuleFileCallback(NULL);
386 file_player_.reset();
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000387 }
388
389 // Dynamically create the instance
kwiberg5a25d952016-08-17 07:31:12 -0700390 file_player_ =
kwiberg5b356f42016-09-08 04:32:33 -0700391 FilePlayer::CreateFilePlayer(_filePlayerId, (const FileFormats)format);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000392
kwiberg5a25d952016-08-17 07:31:12 -0700393 if (!file_player_) {
394 _engineStatisticsPtr->SetLastError(
395 VE_INVALID_ARGUMENT, kTraceError,
396 "StartPlayingFileAsMicrophone() filePlayer format isnot correct");
397 return -1;
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000398 }
399
400 const uint32_t notificationTime(0);
401
kwiberg5a25d952016-08-17 07:31:12 -0700402 if (file_player_->StartPlayingFile(
403 fileName, loop, startPosition, volumeScaling, notificationTime,
404 stopPosition, (const CodecInst*)codecInst) != 0) {
405 _engineStatisticsPtr->SetLastError(
406 VE_BAD_FILE, kTraceError,
407 "StartPlayingFile() failed to start file playout");
408 file_player_->StopPlayingFile();
409 file_player_.reset();
410 return -1;
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000411 }
412
kwiberg5a25d952016-08-17 07:31:12 -0700413 file_player_->RegisterModuleFileCallback(this);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000414 _filePlaying = true;
415
416 return 0;
417}
418
419int TransmitMixer::StartPlayingFileAsMicrophone(InStream* stream,
420 FileFormats format,
421 int startPosition,
422 float volumeScaling,
423 int stopPosition,
424 const CodecInst* codecInst)
425{
426 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
427 "TransmitMixer::StartPlayingFileAsMicrophone(format=%d,"
428 " volumeScaling=%5.3f, startPosition=%d, stopPosition=%d)",
429 format, volumeScaling, startPosition, stopPosition);
430
431 if (stream == NULL)
432 {
433 _engineStatisticsPtr->SetLastError(
434 VE_BAD_FILE, kTraceError,
435 "StartPlayingFileAsMicrophone() NULL as input stream");
436 return -1;
437 }
438
439 if (_filePlaying)
440 {
441 _engineStatisticsPtr->SetLastError(
442 VE_ALREADY_PLAYING, kTraceWarning,
443 "StartPlayingFileAsMicrophone() is already playing");
444 return 0;
445 }
446
tommi31fc21f2016-01-21 10:37:37 -0800447 rtc::CritScope cs(&_critSect);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000448
449 // Destroy the old instance
kwiberg5a25d952016-08-17 07:31:12 -0700450 if (file_player_) {
451 file_player_->RegisterModuleFileCallback(NULL);
452 file_player_.reset();
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000453 }
454
455 // Dynamically create the instance
kwiberg5a25d952016-08-17 07:31:12 -0700456 file_player_ =
kwiberg5b356f42016-09-08 04:32:33 -0700457 FilePlayer::CreateFilePlayer(_filePlayerId, (const FileFormats)format);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000458
kwiberg5a25d952016-08-17 07:31:12 -0700459 if (!file_player_) {
460 _engineStatisticsPtr->SetLastError(
461 VE_INVALID_ARGUMENT, kTraceWarning,
462 "StartPlayingFileAsMicrophone() filePlayer format isnot correct");
463 return -1;
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000464 }
465
466 const uint32_t notificationTime(0);
467
kwiberg4ec01d92016-08-22 08:43:54 -0700468 if (file_player_->StartPlayingFile(stream, startPosition, volumeScaling,
469 notificationTime, stopPosition,
470 (const CodecInst*)codecInst) != 0) {
kwiberg5a25d952016-08-17 07:31:12 -0700471 _engineStatisticsPtr->SetLastError(
472 VE_BAD_FILE, kTraceError,
473 "StartPlayingFile() failed to start file playout");
474 file_player_->StopPlayingFile();
475 file_player_.reset();
476 return -1;
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000477 }
kwiberg5a25d952016-08-17 07:31:12 -0700478 file_player_->RegisterModuleFileCallback(this);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000479 _filePlaying = true;
480
481 return 0;
482}
483
484int TransmitMixer::StopPlayingFileAsMicrophone()
485{
486 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
487 "TransmitMixer::StopPlayingFileAsMicrophone()");
488
489 if (!_filePlaying)
490 {
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000491 return 0;
492 }
493
tommi31fc21f2016-01-21 10:37:37 -0800494 rtc::CritScope cs(&_critSect);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000495
kwiberg5a25d952016-08-17 07:31:12 -0700496 if (file_player_->StopPlayingFile() != 0) {
497 _engineStatisticsPtr->SetLastError(
498 VE_CANNOT_STOP_PLAYOUT, kTraceError,
499 "StopPlayingFile() couldnot stop playing file");
500 return -1;
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000501 }
502
kwiberg5a25d952016-08-17 07:31:12 -0700503 file_player_->RegisterModuleFileCallback(NULL);
504 file_player_.reset();
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000505 _filePlaying = false;
506
507 return 0;
508}
509
510int TransmitMixer::IsPlayingFileAsMicrophone() const
511{
512 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
513 "TransmitMixer::IsPlayingFileAsMicrophone()");
514 return _filePlaying;
515}
516
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000517int TransmitMixer::StartRecordingMicrophone(const char* fileName,
518 const CodecInst* codecInst)
519{
520 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
521 "TransmitMixer::StartRecordingMicrophone(fileName=%s)",
522 fileName);
523
tommi31fc21f2016-01-21 10:37:37 -0800524 rtc::CritScope cs(&_critSect);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000525
526 if (_fileRecording)
527 {
528 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
529 "StartRecordingMicrophone() is already recording");
530 return 0;
531 }
532
533 FileFormats format;
534 const uint32_t notificationTime(0); // Not supported in VoE
535 CodecInst dummyCodec = { 100, "L16", 16000, 320, 1, 320000 };
536
Peter Kasting69558702016-01-12 16:26:35 -0800537 if (codecInst != NULL && codecInst->channels > 2)
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000538 {
539 _engineStatisticsPtr->SetLastError(
540 VE_BAD_ARGUMENT, kTraceError,
541 "StartRecordingMicrophone() invalid compression");
542 return (-1);
543 }
544 if (codecInst == NULL)
545 {
546 format = kFileFormatPcm16kHzFile;
547 codecInst = &dummyCodec;
548 } else if ((STR_CASE_CMP(codecInst->plname,"L16") == 0) ||
549 (STR_CASE_CMP(codecInst->plname,"PCMU") == 0) ||
550 (STR_CASE_CMP(codecInst->plname,"PCMA") == 0))
551 {
552 format = kFileFormatWavFile;
553 } else
554 {
555 format = kFileFormatCompressedFile;
556 }
557
558 // Destroy the old instance
kwiberg5a25d952016-08-17 07:31:12 -0700559 if (file_recorder_) {
560 file_recorder_->RegisterModuleFileCallback(NULL);
561 file_recorder_.reset();
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000562 }
563
kwiberg5a25d952016-08-17 07:31:12 -0700564 file_recorder_ = FileRecorder::CreateFileRecorder(
565 _fileRecorderId, (const FileFormats)format);
566 if (!file_recorder_) {
567 _engineStatisticsPtr->SetLastError(
568 VE_INVALID_ARGUMENT, kTraceError,
569 "StartRecordingMicrophone() fileRecorder format isnot correct");
570 return -1;
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000571 }
572
kwiberg5a25d952016-08-17 07:31:12 -0700573 if (file_recorder_->StartRecordingAudioFile(
574 fileName, (const CodecInst&)*codecInst, notificationTime) != 0) {
575 _engineStatisticsPtr->SetLastError(
576 VE_BAD_FILE, kTraceError,
577 "StartRecordingAudioFile() failed to start file recording");
578 file_recorder_->StopRecording();
579 file_recorder_.reset();
580 return -1;
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000581 }
kwiberg5a25d952016-08-17 07:31:12 -0700582 file_recorder_->RegisterModuleFileCallback(this);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000583 _fileRecording = true;
584
585 return 0;
586}
587
588int TransmitMixer::StartRecordingMicrophone(OutStream* stream,
589 const CodecInst* codecInst)
590{
591 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
592 "TransmitMixer::StartRecordingMicrophone()");
593
tommi31fc21f2016-01-21 10:37:37 -0800594 rtc::CritScope cs(&_critSect);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000595
596 if (_fileRecording)
597 {
598 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
599 "StartRecordingMicrophone() is already recording");
600 return 0;
601 }
602
603 FileFormats format;
604 const uint32_t notificationTime(0); // Not supported in VoE
605 CodecInst dummyCodec = { 100, "L16", 16000, 320, 1, 320000 };
606
607 if (codecInst != NULL && codecInst->channels != 1)
608 {
609 _engineStatisticsPtr->SetLastError(
610 VE_BAD_ARGUMENT, kTraceError,
611 "StartRecordingMicrophone() invalid compression");
612 return (-1);
613 }
614 if (codecInst == NULL)
615 {
616 format = kFileFormatPcm16kHzFile;
617 codecInst = &dummyCodec;
618 } else if ((STR_CASE_CMP(codecInst->plname,"L16") == 0) ||
619 (STR_CASE_CMP(codecInst->plname,"PCMU") == 0) ||
620 (STR_CASE_CMP(codecInst->plname,"PCMA") == 0))
621 {
622 format = kFileFormatWavFile;
623 } else
624 {
625 format = kFileFormatCompressedFile;
626 }
627
628 // Destroy the old instance
kwiberg5a25d952016-08-17 07:31:12 -0700629 if (file_recorder_) {
630 file_recorder_->RegisterModuleFileCallback(NULL);
631 file_recorder_.reset();
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000632 }
633
kwiberg5a25d952016-08-17 07:31:12 -0700634 file_recorder_ = FileRecorder::CreateFileRecorder(
635 _fileRecorderId, (const FileFormats)format);
636 if (!file_recorder_) {
637 _engineStatisticsPtr->SetLastError(
638 VE_INVALID_ARGUMENT, kTraceError,
639 "StartRecordingMicrophone() fileRecorder format isnot correct");
640 return -1;
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000641 }
642
kwiberg4ec01d92016-08-22 08:43:54 -0700643 if (file_recorder_->StartRecordingAudioFile(stream, *codecInst,
kwiberg5a25d952016-08-17 07:31:12 -0700644 notificationTime) != 0) {
645 _engineStatisticsPtr->SetLastError(
646 VE_BAD_FILE, kTraceError,
647 "StartRecordingAudioFile() failed to start file recording");
648 file_recorder_->StopRecording();
649 file_recorder_.reset();
650 return -1;
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000651 }
652
kwiberg5a25d952016-08-17 07:31:12 -0700653 file_recorder_->RegisterModuleFileCallback(this);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000654 _fileRecording = true;
655
656 return 0;
657}
658
659
660int TransmitMixer::StopRecordingMicrophone()
661{
662 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
663 "TransmitMixer::StopRecordingMicrophone()");
664
tommi31fc21f2016-01-21 10:37:37 -0800665 rtc::CritScope cs(&_critSect);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000666
667 if (!_fileRecording)
668 {
669 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
670 "StopRecordingMicrophone() isnot recording");
671 return 0;
672 }
673
kwiberg5a25d952016-08-17 07:31:12 -0700674 if (file_recorder_->StopRecording() != 0) {
675 _engineStatisticsPtr->SetLastError(
676 VE_STOP_RECORDING_FAILED, kTraceError,
677 "StopRecording(), could not stop recording");
678 return -1;
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000679 }
kwiberg5a25d952016-08-17 07:31:12 -0700680 file_recorder_->RegisterModuleFileCallback(NULL);
681 file_recorder_.reset();
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000682 _fileRecording = false;
683
684 return 0;
685}
686
687int TransmitMixer::StartRecordingCall(const char* fileName,
688 const CodecInst* codecInst)
689{
690 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
691 "TransmitMixer::StartRecordingCall(fileName=%s)", fileName);
692
693 if (_fileCallRecording)
694 {
695 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
696 "StartRecordingCall() is already recording");
697 return 0;
698 }
699
700 FileFormats format;
701 const uint32_t notificationTime(0); // Not supported in VoE
702 CodecInst dummyCodec = { 100, "L16", 16000, 320, 1, 320000 };
703
704 if (codecInst != NULL && codecInst->channels != 1)
705 {
706 _engineStatisticsPtr->SetLastError(
707 VE_BAD_ARGUMENT, kTraceError,
708 "StartRecordingCall() invalid compression");
709 return (-1);
710 }
711 if (codecInst == NULL)
712 {
713 format = kFileFormatPcm16kHzFile;
714 codecInst = &dummyCodec;
715 } else if ((STR_CASE_CMP(codecInst->plname,"L16") == 0) ||
716 (STR_CASE_CMP(codecInst->plname,"PCMU") == 0) ||
717 (STR_CASE_CMP(codecInst->plname,"PCMA") == 0))
718 {
719 format = kFileFormatWavFile;
720 } else
721 {
722 format = kFileFormatCompressedFile;
723 }
724
tommi31fc21f2016-01-21 10:37:37 -0800725 rtc::CritScope cs(&_critSect);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000726
727 // Destroy the old instance
kwiberg5a25d952016-08-17 07:31:12 -0700728 if (file_call_recorder_) {
729 file_call_recorder_->RegisterModuleFileCallback(NULL);
730 file_call_recorder_.reset();
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000731 }
732
kwiberg5a25d952016-08-17 07:31:12 -0700733 file_call_recorder_ = FileRecorder::CreateFileRecorder(
734 _fileCallRecorderId, (const FileFormats)format);
735 if (!file_call_recorder_) {
736 _engineStatisticsPtr->SetLastError(
737 VE_INVALID_ARGUMENT, kTraceError,
738 "StartRecordingCall() fileRecorder format isnot correct");
739 return -1;
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000740 }
741
kwiberg5a25d952016-08-17 07:31:12 -0700742 if (file_call_recorder_->StartRecordingAudioFile(
743 fileName, (const CodecInst&)*codecInst, notificationTime) != 0) {
744 _engineStatisticsPtr->SetLastError(
745 VE_BAD_FILE, kTraceError,
746 "StartRecordingAudioFile() failed to start file recording");
747 file_call_recorder_->StopRecording();
748 file_call_recorder_.reset();
749 return -1;
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000750 }
kwiberg5a25d952016-08-17 07:31:12 -0700751 file_call_recorder_->RegisterModuleFileCallback(this);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000752 _fileCallRecording = true;
753
754 return 0;
755}
756
757int TransmitMixer::StartRecordingCall(OutStream* stream,
758 const CodecInst* codecInst)
759{
760 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
761 "TransmitMixer::StartRecordingCall()");
762
763 if (_fileCallRecording)
764 {
765 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
766 "StartRecordingCall() is already recording");
767 return 0;
768 }
769
770 FileFormats format;
771 const uint32_t notificationTime(0); // Not supported in VoE
772 CodecInst dummyCodec = { 100, "L16", 16000, 320, 1, 320000 };
773
774 if (codecInst != NULL && codecInst->channels != 1)
775 {
776 _engineStatisticsPtr->SetLastError(
777 VE_BAD_ARGUMENT, kTraceError,
778 "StartRecordingCall() invalid compression");
779 return (-1);
780 }
781 if (codecInst == NULL)
782 {
783 format = kFileFormatPcm16kHzFile;
784 codecInst = &dummyCodec;
785 } else if ((STR_CASE_CMP(codecInst->plname,"L16") == 0) ||
786 (STR_CASE_CMP(codecInst->plname,"PCMU") == 0) ||
787 (STR_CASE_CMP(codecInst->plname,"PCMA") == 0))
788 {
789 format = kFileFormatWavFile;
790 } else
791 {
792 format = kFileFormatCompressedFile;
793 }
794
tommi31fc21f2016-01-21 10:37:37 -0800795 rtc::CritScope cs(&_critSect);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000796
797 // Destroy the old instance
kwiberg5a25d952016-08-17 07:31:12 -0700798 if (file_call_recorder_) {
799 file_call_recorder_->RegisterModuleFileCallback(NULL);
800 file_call_recorder_.reset();
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000801 }
802
kwiberg5a25d952016-08-17 07:31:12 -0700803 file_call_recorder_ = FileRecorder::CreateFileRecorder(
804 _fileCallRecorderId, (const FileFormats)format);
805 if (!file_call_recorder_) {
806 _engineStatisticsPtr->SetLastError(
807 VE_INVALID_ARGUMENT, kTraceError,
808 "StartRecordingCall() fileRecorder format isnot correct");
809 return -1;
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000810 }
811
kwiberg4ec01d92016-08-22 08:43:54 -0700812 if (file_call_recorder_->StartRecordingAudioFile(stream, *codecInst,
kwiberg5a25d952016-08-17 07:31:12 -0700813 notificationTime) != 0) {
814 _engineStatisticsPtr->SetLastError(
815 VE_BAD_FILE, kTraceError,
816 "StartRecordingAudioFile() failed to start file recording");
817 file_call_recorder_->StopRecording();
818 file_call_recorder_.reset();
819 return -1;
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000820 }
821
kwiberg5a25d952016-08-17 07:31:12 -0700822 file_call_recorder_->RegisterModuleFileCallback(this);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000823 _fileCallRecording = true;
824
825 return 0;
826}
827
828int TransmitMixer::StopRecordingCall()
829{
830 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
831 "TransmitMixer::StopRecordingCall()");
832
833 if (!_fileCallRecording)
834 {
835 WEBRTC_TRACE(kTraceError, kTraceVoice, VoEId(_instanceId, -1),
836 "StopRecordingCall() file isnot recording");
837 return -1;
838 }
839
tommi31fc21f2016-01-21 10:37:37 -0800840 rtc::CritScope cs(&_critSect);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000841
kwiberg5a25d952016-08-17 07:31:12 -0700842 if (file_call_recorder_->StopRecording() != 0) {
843 _engineStatisticsPtr->SetLastError(
844 VE_STOP_RECORDING_FAILED, kTraceError,
845 "StopRecording(), could not stop recording");
846 return -1;
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000847 }
848
kwiberg5a25d952016-08-17 07:31:12 -0700849 file_call_recorder_->RegisterModuleFileCallback(NULL);
850 file_call_recorder_.reset();
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000851 _fileCallRecording = false;
852
853 return 0;
854}
855
856void
857TransmitMixer::SetMixWithMicStatus(bool mix)
858{
859 _mixFileWithMicrophone = mix;
860}
861
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000862int8_t TransmitMixer::AudioLevel() const
863{
864 // Speech + file level [0,9]
865 return _audioLevel.Level();
866}
867
868int16_t TransmitMixer::AudioLevelFullRange() const
869{
870 // Speech + file level [0,32767]
871 return _audioLevel.LevelFullRange();
872}
873
zsteine76bd3a2017-07-14 12:17:49 -0700874double TransmitMixer::GetTotalInputEnergy() const {
875 return totalInputEnergy_;
876}
877
878double TransmitMixer::GetTotalInputDuration() const {
879 return totalInputDuration_;
880}
881
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000882bool TransmitMixer::IsRecordingCall()
883{
884 return _fileCallRecording;
885}
886
887bool TransmitMixer::IsRecordingMic()
888{
tommi31fc21f2016-01-21 10:37:37 -0800889 rtc::CritScope cs(&_critSect);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000890 return _fileRecording;
891}
892
893void TransmitMixer::GenerateAudioFrame(const int16_t* audio,
Peter Kastingdce40cf2015-08-24 14:52:23 -0700894 size_t samples_per_channel,
Peter Kasting69558702016-01-12 16:26:35 -0800895 size_t num_channels,
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000896 int sample_rate_hz) {
897 int codec_rate;
Peter Kasting69558702016-01-12 16:26:35 -0800898 size_t num_codec_channels;
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000899 GetSendCodecInfo(&codec_rate, &num_codec_channels);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000900 stereo_codec_ = num_codec_channels == 2;
901
Alejandro Luebscdfe20b2015-09-23 12:49:12 -0700902 // We want to process at the lowest rate possible without losing information.
903 // Choose the lowest native rate at least equal to the input and codec rates.
904 const int min_processing_rate = std::min(sample_rate_hz, codec_rate);
905 for (size_t i = 0; i < AudioProcessing::kNumNativeSampleRates; ++i) {
906 _audioFrame.sample_rate_hz_ = AudioProcessing::kNativeSampleRatesHz[i];
907 if (_audioFrame.sample_rate_hz_ >= min_processing_rate) {
908 break;
909 }
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000910 }
Alejandro Luebscdfe20b2015-09-23 12:49:12 -0700911 _audioFrame.num_channels_ = std::min(num_channels, num_codec_channels);
912 RemixAndResample(audio, samples_per_channel, num_channels, sample_rate_hz,
913 &resampler_, &_audioFrame);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000914}
915
916int32_t TransmitMixer::RecordAudioToFile(
917 uint32_t mixingFrequency)
918{
tommi31fc21f2016-01-21 10:37:37 -0800919 rtc::CritScope cs(&_critSect);
kwiberg5a25d952016-08-17 07:31:12 -0700920 if (!file_recorder_) {
921 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
922 "TransmitMixer::RecordAudioToFile() filerecorder doesnot"
923 "exist");
924 return -1;
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000925 }
926
kwiberg5a25d952016-08-17 07:31:12 -0700927 if (file_recorder_->RecordAudioToFile(_audioFrame) != 0) {
928 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
929 "TransmitMixer::RecordAudioToFile() file recording"
930 "failed");
931 return -1;
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000932 }
933
934 return 0;
935}
936
937int32_t TransmitMixer::MixOrReplaceAudioWithFile(
938 int mixingFrequency)
939{
kwibergb7f89d62016-02-17 10:04:18 -0800940 std::unique_ptr<int16_t[]> fileBuffer(new int16_t[640]);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000941
Peter Kastingdce40cf2015-08-24 14:52:23 -0700942 size_t fileSamples(0);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000943 {
tommi31fc21f2016-01-21 10:37:37 -0800944 rtc::CritScope cs(&_critSect);
kwiberg5a25d952016-08-17 07:31:12 -0700945 if (!file_player_) {
946 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
947 "TransmitMixer::MixOrReplaceAudioWithFile()"
948 "fileplayer doesnot exist");
949 return -1;
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000950 }
951
kwiberg4ec01d92016-08-22 08:43:54 -0700952 if (file_player_->Get10msAudioFromFile(fileBuffer.get(), &fileSamples,
kwiberg5a25d952016-08-17 07:31:12 -0700953 mixingFrequency) == -1) {
954 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
955 "TransmitMixer::MixOrReplaceAudioWithFile() file"
956 " mixing failed");
957 return -1;
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000958 }
959 }
960
961 assert(_audioFrame.samples_per_channel_ == fileSamples);
962
963 if (_mixFileWithMicrophone)
964 {
965 // Currently file stream is always mono.
966 // TODO(xians): Change the code when FilePlayer supports real stereo.
yujo36b1a5f2017-06-12 12:45:32 -0700967 MixWithSat(_audioFrame.mutable_data(),
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000968 _audioFrame.num_channels_,
969 fileBuffer.get(),
970 1,
971 fileSamples);
972 } else
973 {
974 // Replace ACM audio with file.
975 // Currently file stream is always mono.
976 // TODO(xians): Change the code when FilePlayer supports real stereo.
977 _audioFrame.UpdateFrame(-1,
tommi@webrtc.orgeec6ecd2014-07-11 19:09:59 +0000978 0xFFFFFFFF,
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000979 fileBuffer.get(),
980 fileSamples,
981 mixingFrequency,
982 AudioFrame::kNormalSpeech,
983 AudioFrame::kVadUnknown,
984 1);
985 }
986 return 0;
987}
988
989void TransmitMixer::ProcessAudio(int delay_ms, int clock_drift,
990 int current_mic_level, bool key_pressed) {
991 if (audioproc_->set_stream_delay_ms(delay_ms) != 0) {
pbosad856222015-11-27 09:48:36 -0800992 // Silently ignore this failure to avoid flooding the logs.
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000993 }
994
995 GainControl* agc = audioproc_->gain_control();
996 if (agc->set_stream_analog_level(current_mic_level) != 0) {
pbosad856222015-11-27 09:48:36 -0800997 LOG(LS_ERROR) << "set_stream_analog_level failed: current_mic_level = "
998 << current_mic_level;
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000999 assert(false);
1000 }
1001
1002 EchoCancellation* aec = audioproc_->echo_cancellation();
1003 if (aec->is_drift_compensation_enabled()) {
1004 aec->set_stream_drift_samples(clock_drift);
1005 }
1006
1007 audioproc_->set_stream_key_pressed(key_pressed);
1008
1009 int err = audioproc_->ProcessStream(&_audioFrame);
1010 if (err != 0) {
1011 LOG(LS_ERROR) << "ProcessStream() error: " << err;
1012 assert(false);
1013 }
1014
1015 // Store new capture level. Only updated when analog AGC is enabled.
1016 _captureLevel = agc->stream_analog_level();
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +00001017}
1018
henrik.lundinf00082d2016-12-05 02:22:12 -08001019#if WEBRTC_VOICE_ENGINE_TYPING_DETECTION
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +00001020void TransmitMixer::TypingDetection(bool keyPressed)
1021{
1022 // We let the VAD determine if we're using this feature or not.
1023 if (_audioFrame.vad_activity_ == AudioFrame::kVadUnknown) {
1024 return;
1025 }
1026
1027 bool vadActive = _audioFrame.vad_activity_ == AudioFrame::kVadActive;
1028 if (_typingDetection.Process(keyPressed, vadActive)) {
tommi31fc21f2016-01-21 10:37:37 -08001029 rtc::CritScope cs(&_critSect);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +00001030 _typingNoiseWarningPending = true;
1031 _typingNoiseDetected = true;
1032 } else {
tommi31fc21f2016-01-21 10:37:37 -08001033 rtc::CritScope cs(&_critSect);
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +00001034 // If there is already a warning pending, do not change the state.
1035 // Otherwise set a warning pending if last callback was for noise detected.
1036 if (!_typingNoiseWarningPending && _typingNoiseDetected) {
1037 _typingNoiseWarningPending = true;
1038 _typingNoiseDetected = false;
1039 }
1040 }
1041}
1042#endif
1043
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +00001044void TransmitMixer::EnableStereoChannelSwapping(bool enable) {
1045 swap_stereo_channels_ = enable;
1046}
1047
1048bool TransmitMixer::IsStereoChannelSwappingEnabled() {
1049 return swap_stereo_channels_;
1050}
1051
1052} // namespace voe
1053} // namespace webrtc