blob: d2467cf979e272fe4434264a940f8fcd4f6aa4a3 [file] [log] [blame]
andrew@webrtc.orgf7c73b52014-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
13#include "webrtc/modules/utility/interface/audio_frame_operations.h"
14#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
15#include "webrtc/system_wrappers/interface/event_wrapper.h"
16#include "webrtc/system_wrappers/interface/logging.h"
17#include "webrtc/system_wrappers/interface/trace.h"
18#include "webrtc/voice_engine/channel.h"
19#include "webrtc/voice_engine/channel_manager.h"
20#include "webrtc/voice_engine/include/voe_external_media.h"
21#include "webrtc/voice_engine/statistics.h"
22#include "webrtc/voice_engine/utility.h"
23#include "webrtc/voice_engine/voe_base_impl.h"
24
25#define WEBRTC_ABS(a) (((a) < 0) ? -(a) : (a))
26
27namespace webrtc {
28namespace voe {
29
30// TODO(ajm): The thread safety of this is dubious...
31void
32TransmitMixer::OnPeriodicProcess()
33{
34 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, -1),
35 "TransmitMixer::OnPeriodicProcess()");
36
37#if defined(WEBRTC_VOICE_ENGINE_TYPING_DETECTION)
38 if (_typingNoiseWarningPending)
39 {
40 CriticalSectionScoped cs(&_callbackCritSect);
41 if (_voiceEngineObserverPtr)
42 {
43 if (_typingNoiseDetected) {
44 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
45 "TransmitMixer::OnPeriodicProcess() => "
46 "CallbackOnError(VE_TYPING_NOISE_WARNING)");
47 _voiceEngineObserverPtr->CallbackOnError(
48 -1,
49 VE_TYPING_NOISE_WARNING);
50 } else {
51 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
52 "TransmitMixer::OnPeriodicProcess() => "
53 "CallbackOnError(VE_TYPING_NOISE_OFF_WARNING)");
54 _voiceEngineObserverPtr->CallbackOnError(
55 -1,
56 VE_TYPING_NOISE_OFF_WARNING);
57 }
58 }
59 _typingNoiseWarningPending = false;
60 }
61#endif
62
63 bool saturationWarning = false;
64 {
65 // Modify |_saturationWarning| under lock to avoid conflict with write op
66 // in ProcessAudio and also ensure that we don't hold the lock during the
67 // callback.
68 CriticalSectionScoped cs(&_critSect);
69 saturationWarning = _saturationWarning;
70 if (_saturationWarning)
71 _saturationWarning = false;
72 }
73
74 if (saturationWarning)
75 {
76 CriticalSectionScoped cs(&_callbackCritSect);
77 if (_voiceEngineObserverPtr)
78 {
79 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
80 "TransmitMixer::OnPeriodicProcess() =>"
81 " CallbackOnError(VE_SATURATION_WARNING)");
82 _voiceEngineObserverPtr->CallbackOnError(-1, VE_SATURATION_WARNING);
83 }
84 }
85}
86
87
88void TransmitMixer::PlayNotification(int32_t id,
89 uint32_t durationMs)
90{
91 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, -1),
92 "TransmitMixer::PlayNotification(id=%d, durationMs=%d)",
93 id, durationMs);
94
95 // Not implement yet
96}
97
98void TransmitMixer::RecordNotification(int32_t id,
99 uint32_t durationMs)
100{
101 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
102 "TransmitMixer::RecordNotification(id=%d, durationMs=%d)",
103 id, durationMs);
104
105 // Not implement yet
106}
107
108void TransmitMixer::PlayFileEnded(int32_t id)
109{
110 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, -1),
111 "TransmitMixer::PlayFileEnded(id=%d)", id);
112
113 assert(id == _filePlayerId);
114
115 CriticalSectionScoped cs(&_critSect);
116
117 _filePlaying = false;
118 WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId, -1),
119 "TransmitMixer::PlayFileEnded() =>"
120 "file player module is shutdown");
121}
122
123void
124TransmitMixer::RecordFileEnded(int32_t id)
125{
126 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, -1),
127 "TransmitMixer::RecordFileEnded(id=%d)", id);
128
129 if (id == _fileRecorderId)
130 {
131 CriticalSectionScoped cs(&_critSect);
132 _fileRecording = false;
133 WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId, -1),
134 "TransmitMixer::RecordFileEnded() => fileRecorder module"
135 "is shutdown");
136 } else if (id == _fileCallRecorderId)
137 {
138 CriticalSectionScoped cs(&_critSect);
139 _fileCallRecording = false;
140 WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId, -1),
141 "TransmitMixer::RecordFileEnded() => fileCallRecorder"
142 "module is shutdown");
143 }
144}
145
146int32_t
147TransmitMixer::Create(TransmitMixer*& mixer, uint32_t instanceId)
148{
149 WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(instanceId, -1),
150 "TransmitMixer::Create(instanceId=%d)", instanceId);
151 mixer = new TransmitMixer(instanceId);
152 if (mixer == NULL)
153 {
154 WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(instanceId, -1),
155 "TransmitMixer::Create() unable to allocate memory"
156 "for mixer");
157 return -1;
158 }
159 return 0;
160}
161
162void
163TransmitMixer::Destroy(TransmitMixer*& mixer)
164{
165 if (mixer)
166 {
167 delete mixer;
168 mixer = NULL;
169 }
170}
171
172TransmitMixer::TransmitMixer(uint32_t instanceId) :
173 _engineStatisticsPtr(NULL),
174 _channelManagerPtr(NULL),
175 audioproc_(NULL),
176 _voiceEngineObserverPtr(NULL),
177 _processThreadPtr(NULL),
178 _filePlayerPtr(NULL),
179 _fileRecorderPtr(NULL),
180 _fileCallRecorderPtr(NULL),
181 // Avoid conflict with other channels by adding 1024 - 1026,
182 // won't use as much as 1024 channels.
183 _filePlayerId(instanceId + 1024),
184 _fileRecorderId(instanceId + 1025),
185 _fileCallRecorderId(instanceId + 1026),
186 _filePlaying(false),
187 _fileRecording(false),
188 _fileCallRecording(false),
189 _audioLevel(),
190 _critSect(*CriticalSectionWrapper::CreateCriticalSection()),
191 _callbackCritSect(*CriticalSectionWrapper::CreateCriticalSection()),
192#ifdef WEBRTC_VOICE_ENGINE_TYPING_DETECTION
193 _typingNoiseWarningPending(false),
194 _typingNoiseDetected(false),
195#endif
196 _saturationWarning(false),
197 _instanceId(instanceId),
198 _mixFileWithMicrophone(false),
199 _captureLevel(0),
200 external_postproc_ptr_(NULL),
201 external_preproc_ptr_(NULL),
202 _mute(false),
203 _remainingMuteMicTimeMs(0),
204 stereo_codec_(false),
205 swap_stereo_channels_(false)
206{
207 WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId, -1),
208 "TransmitMixer::TransmitMixer() - ctor");
209}
210
211TransmitMixer::~TransmitMixer()
212{
213 WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId, -1),
214 "TransmitMixer::~TransmitMixer() - dtor");
215 _monitorModule.DeRegisterObserver();
216 if (_processThreadPtr)
217 {
218 _processThreadPtr->DeRegisterModule(&_monitorModule);
219 }
220 DeRegisterExternalMediaProcessing(kRecordingAllChannelsMixed);
221 DeRegisterExternalMediaProcessing(kRecordingPreprocessing);
222 {
223 CriticalSectionScoped cs(&_critSect);
224 if (_fileRecorderPtr)
225 {
226 _fileRecorderPtr->RegisterModuleFileCallback(NULL);
227 _fileRecorderPtr->StopRecording();
228 FileRecorder::DestroyFileRecorder(_fileRecorderPtr);
229 _fileRecorderPtr = NULL;
230 }
231 if (_fileCallRecorderPtr)
232 {
233 _fileCallRecorderPtr->RegisterModuleFileCallback(NULL);
234 _fileCallRecorderPtr->StopRecording();
235 FileRecorder::DestroyFileRecorder(_fileCallRecorderPtr);
236 _fileCallRecorderPtr = NULL;
237 }
238 if (_filePlayerPtr)
239 {
240 _filePlayerPtr->RegisterModuleFileCallback(NULL);
241 _filePlayerPtr->StopPlayingFile();
242 FilePlayer::DestroyFilePlayer(_filePlayerPtr);
243 _filePlayerPtr = NULL;
244 }
245 }
246 delete &_critSect;
247 delete &_callbackCritSect;
248}
249
250int32_t
251TransmitMixer::SetEngineInformation(ProcessThread& processThread,
252 Statistics& engineStatistics,
253 ChannelManager& channelManager)
254{
255 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
256 "TransmitMixer::SetEngineInformation()");
257
258 _processThreadPtr = &processThread;
259 _engineStatisticsPtr = &engineStatistics;
260 _channelManagerPtr = &channelManager;
261
262 if (_processThreadPtr->RegisterModule(&_monitorModule) == -1)
263 {
264 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
265 "TransmitMixer::SetEngineInformation() failed to"
266 "register the monitor module");
267 } else
268 {
269 _monitorModule.RegisterObserver(*this);
270 }
271
272 return 0;
273}
274
275int32_t
276TransmitMixer::RegisterVoiceEngineObserver(VoiceEngineObserver& observer)
277{
278 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
279 "TransmitMixer::RegisterVoiceEngineObserver()");
280 CriticalSectionScoped cs(&_callbackCritSect);
281
282 if (_voiceEngineObserverPtr)
283 {
284 _engineStatisticsPtr->SetLastError(
285 VE_INVALID_OPERATION, kTraceError,
286 "RegisterVoiceEngineObserver() observer already enabled");
287 return -1;
288 }
289 _voiceEngineObserverPtr = &observer;
290 return 0;
291}
292
293int32_t
294TransmitMixer::SetAudioProcessingModule(AudioProcessing* audioProcessingModule)
295{
296 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
297 "TransmitMixer::SetAudioProcessingModule("
298 "audioProcessingModule=0x%x)",
299 audioProcessingModule);
300 audioproc_ = audioProcessingModule;
301 return 0;
302}
303
304void TransmitMixer::GetSendCodecInfo(int* max_sample_rate, int* max_channels) {
305 *max_sample_rate = 8000;
306 *max_channels = 1;
307 for (ChannelManager::Iterator it(_channelManagerPtr); it.IsValid();
308 it.Increment()) {
309 Channel* channel = it.GetChannel();
310 if (channel->Sending()) {
311 CodecInst codec;
312 channel->GetSendCodec(codec);
313 *max_sample_rate = std::max(*max_sample_rate, codec.plfreq);
314 *max_channels = std::max(*max_channels, codec.channels);
315 }
316 }
317}
318
319int32_t
320TransmitMixer::PrepareDemux(const void* audioSamples,
321 uint32_t nSamples,
322 uint8_t nChannels,
323 uint32_t samplesPerSec,
324 uint16_t totalDelayMS,
325 int32_t clockDrift,
326 uint16_t currentMicLevel,
327 bool keyPressed)
328{
329 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, -1),
330 "TransmitMixer::PrepareDemux(nSamples=%u, nChannels=%u,"
331 "samplesPerSec=%u, totalDelayMS=%u, clockDrift=%d,"
332 "currentMicLevel=%u)", nSamples, nChannels, samplesPerSec,
333 totalDelayMS, clockDrift, currentMicLevel);
334
335 // --- Resample input audio and create/store the initial audio frame
336 GenerateAudioFrame(static_cast<const int16_t*>(audioSamples),
337 nSamples,
338 nChannels,
339 samplesPerSec);
340
341 {
342 CriticalSectionScoped cs(&_callbackCritSect);
343 if (external_preproc_ptr_) {
344 external_preproc_ptr_->Process(-1, kRecordingPreprocessing,
345 _audioFrame.data_,
346 _audioFrame.samples_per_channel_,
347 _audioFrame.sample_rate_hz_,
348 _audioFrame.num_channels_ == 2);
349 }
350 }
351
352 // --- Near-end audio processing.
353 ProcessAudio(totalDelayMS, clockDrift, currentMicLevel, keyPressed);
354
355 if (swap_stereo_channels_ && stereo_codec_)
356 // Only bother swapping if we're using a stereo codec.
357 AudioFrameOperations::SwapStereoChannels(&_audioFrame);
358
359 // --- Annoying typing detection (utilizes the APM/VAD decision)
360#ifdef WEBRTC_VOICE_ENGINE_TYPING_DETECTION
361 TypingDetection(keyPressed);
362#endif
363
364 // --- Mute during DTMF tone if direct feedback is enabled
365 if (_remainingMuteMicTimeMs > 0)
366 {
367 AudioFrameOperations::Mute(_audioFrame);
368 _remainingMuteMicTimeMs -= 10;
369 if (_remainingMuteMicTimeMs < 0)
370 {
371 _remainingMuteMicTimeMs = 0;
372 }
373 }
374
375 // --- Mute signal
376 if (_mute)
377 {
378 AudioFrameOperations::Mute(_audioFrame);
379 }
380
381 // --- Mix with file (does not affect the mixing frequency)
382 if (_filePlaying)
383 {
384 MixOrReplaceAudioWithFile(_audioFrame.sample_rate_hz_);
385 }
386
387 // --- Record to file
388 bool file_recording = false;
389 {
390 CriticalSectionScoped cs(&_critSect);
391 file_recording = _fileRecording;
392 }
393 if (file_recording)
394 {
395 RecordAudioToFile(_audioFrame.sample_rate_hz_);
396 }
397
398 {
399 CriticalSectionScoped cs(&_callbackCritSect);
400 if (external_postproc_ptr_) {
401 external_postproc_ptr_->Process(-1, kRecordingAllChannelsMixed,
402 _audioFrame.data_,
403 _audioFrame.samples_per_channel_,
404 _audioFrame.sample_rate_hz_,
405 _audioFrame.num_channels_ == 2);
406 }
407 }
408
409 // --- Measure audio level of speech after all processing.
410 _audioLevel.ComputeLevel(_audioFrame);
411 return 0;
412}
413
414int32_t
415TransmitMixer::DemuxAndMix()
416{
417 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, -1),
418 "TransmitMixer::DemuxAndMix()");
419
420 for (ChannelManager::Iterator it(_channelManagerPtr); it.IsValid();
421 it.Increment())
422 {
423 Channel* channelPtr = it.GetChannel();
henrika@webrtc.org692224a2014-04-17 10:45:01 +0000424 if (channelPtr->Sending())
andrew@webrtc.orgf7c73b52014-04-03 21:56:01 +0000425 {
426 // Demultiplex makes a copy of its input.
427 channelPtr->Demultiplex(_audioFrame);
428 channelPtr->PrepareEncodeAndSend(_audioFrame.sample_rate_hz_);
429 }
430 }
431 return 0;
432}
433
434void TransmitMixer::DemuxAndMix(const int voe_channels[],
435 int number_of_voe_channels) {
436 for (int i = 0; i < number_of_voe_channels; ++i) {
437 voe::ChannelOwner ch = _channelManagerPtr->GetChannel(voe_channels[i]);
438 voe::Channel* channel_ptr = ch.channel();
439 if (channel_ptr) {
henrika@webrtc.org692224a2014-04-17 10:45:01 +0000440 if (channel_ptr->Sending()) {
andrew@webrtc.orgf7c73b52014-04-03 21:56:01 +0000441 // Demultiplex makes a copy of its input.
442 channel_ptr->Demultiplex(_audioFrame);
443 channel_ptr->PrepareEncodeAndSend(_audioFrame.sample_rate_hz_);
444 }
445 }
446 }
447}
448
449int32_t
450TransmitMixer::EncodeAndSend()
451{
452 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, -1),
453 "TransmitMixer::EncodeAndSend()");
454
455 for (ChannelManager::Iterator it(_channelManagerPtr); it.IsValid();
456 it.Increment())
457 {
458 Channel* channelPtr = it.GetChannel();
henrika@webrtc.org692224a2014-04-17 10:45:01 +0000459 if (channelPtr->Sending())
andrew@webrtc.orgf7c73b52014-04-03 21:56:01 +0000460 {
461 channelPtr->EncodeAndSend();
462 }
463 }
464 return 0;
465}
466
467void TransmitMixer::EncodeAndSend(const int voe_channels[],
468 int number_of_voe_channels) {
469 for (int i = 0; i < number_of_voe_channels; ++i) {
470 voe::ChannelOwner ch = _channelManagerPtr->GetChannel(voe_channels[i]);
471 voe::Channel* channel_ptr = ch.channel();
henrika@webrtc.org692224a2014-04-17 10:45:01 +0000472 if (channel_ptr && channel_ptr->Sending())
andrew@webrtc.orgf7c73b52014-04-03 21:56:01 +0000473 channel_ptr->EncodeAndSend();
474 }
475}
476
477uint32_t TransmitMixer::CaptureLevel() const
478{
479 return _captureLevel;
480}
481
482void
483TransmitMixer::UpdateMuteMicrophoneTime(uint32_t lengthMs)
484{
485 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
486 "TransmitMixer::UpdateMuteMicrophoneTime(lengthMs=%d)",
487 lengthMs);
488 _remainingMuteMicTimeMs = lengthMs;
489}
490
491int32_t
492TransmitMixer::StopSend()
493{
494 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
495 "TransmitMixer::StopSend()");
496 _audioLevel.Clear();
497 return 0;
498}
499
500int TransmitMixer::StartPlayingFileAsMicrophone(const char* fileName,
501 bool loop,
502 FileFormats format,
503 int startPosition,
504 float volumeScaling,
505 int stopPosition,
506 const CodecInst* codecInst)
507{
508 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
509 "TransmitMixer::StartPlayingFileAsMicrophone("
510 "fileNameUTF8[]=%s,loop=%d, format=%d, volumeScaling=%5.3f,"
511 " startPosition=%d, stopPosition=%d)", fileName, loop,
512 format, volumeScaling, startPosition, stopPosition);
513
514 if (_filePlaying)
515 {
516 _engineStatisticsPtr->SetLastError(
517 VE_ALREADY_PLAYING, kTraceWarning,
518 "StartPlayingFileAsMicrophone() is already playing");
519 return 0;
520 }
521
522 CriticalSectionScoped cs(&_critSect);
523
524 // Destroy the old instance
525 if (_filePlayerPtr)
526 {
527 _filePlayerPtr->RegisterModuleFileCallback(NULL);
528 FilePlayer::DestroyFilePlayer(_filePlayerPtr);
529 _filePlayerPtr = NULL;
530 }
531
532 // Dynamically create the instance
533 _filePlayerPtr
534 = FilePlayer::CreateFilePlayer(_filePlayerId,
535 (const FileFormats) format);
536
537 if (_filePlayerPtr == NULL)
538 {
539 _engineStatisticsPtr->SetLastError(
540 VE_INVALID_ARGUMENT, kTraceError,
541 "StartPlayingFileAsMicrophone() filePlayer format isnot correct");
542 return -1;
543 }
544
545 const uint32_t notificationTime(0);
546
547 if (_filePlayerPtr->StartPlayingFile(
548 fileName,
549 loop,
550 startPosition,
551 volumeScaling,
552 notificationTime,
553 stopPosition,
554 (const CodecInst*) codecInst) != 0)
555 {
556 _engineStatisticsPtr->SetLastError(
557 VE_BAD_FILE, kTraceError,
558 "StartPlayingFile() failed to start file playout");
559 _filePlayerPtr->StopPlayingFile();
560 FilePlayer::DestroyFilePlayer(_filePlayerPtr);
561 _filePlayerPtr = NULL;
562 return -1;
563 }
564
565 _filePlayerPtr->RegisterModuleFileCallback(this);
566 _filePlaying = true;
567
568 return 0;
569}
570
571int TransmitMixer::StartPlayingFileAsMicrophone(InStream* stream,
572 FileFormats format,
573 int startPosition,
574 float volumeScaling,
575 int stopPosition,
576 const CodecInst* codecInst)
577{
578 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
579 "TransmitMixer::StartPlayingFileAsMicrophone(format=%d,"
580 " volumeScaling=%5.3f, startPosition=%d, stopPosition=%d)",
581 format, volumeScaling, startPosition, stopPosition);
582
583 if (stream == NULL)
584 {
585 _engineStatisticsPtr->SetLastError(
586 VE_BAD_FILE, kTraceError,
587 "StartPlayingFileAsMicrophone() NULL as input stream");
588 return -1;
589 }
590
591 if (_filePlaying)
592 {
593 _engineStatisticsPtr->SetLastError(
594 VE_ALREADY_PLAYING, kTraceWarning,
595 "StartPlayingFileAsMicrophone() is already playing");
596 return 0;
597 }
598
599 CriticalSectionScoped cs(&_critSect);
600
601 // Destroy the old instance
602 if (_filePlayerPtr)
603 {
604 _filePlayerPtr->RegisterModuleFileCallback(NULL);
605 FilePlayer::DestroyFilePlayer(_filePlayerPtr);
606 _filePlayerPtr = NULL;
607 }
608
609 // Dynamically create the instance
610 _filePlayerPtr
611 = FilePlayer::CreateFilePlayer(_filePlayerId,
612 (const FileFormats) format);
613
614 if (_filePlayerPtr == NULL)
615 {
616 _engineStatisticsPtr->SetLastError(
617 VE_INVALID_ARGUMENT, kTraceWarning,
618 "StartPlayingFileAsMicrophone() filePlayer format isnot correct");
619 return -1;
620 }
621
622 const uint32_t notificationTime(0);
623
624 if (_filePlayerPtr->StartPlayingFile(
625 (InStream&) *stream,
626 startPosition,
627 volumeScaling,
628 notificationTime,
629 stopPosition,
630 (const CodecInst*) codecInst) != 0)
631 {
632 _engineStatisticsPtr->SetLastError(
633 VE_BAD_FILE, kTraceError,
634 "StartPlayingFile() failed to start file playout");
635 _filePlayerPtr->StopPlayingFile();
636 FilePlayer::DestroyFilePlayer(_filePlayerPtr);
637 _filePlayerPtr = NULL;
638 return -1;
639 }
640 _filePlayerPtr->RegisterModuleFileCallback(this);
641 _filePlaying = true;
642
643 return 0;
644}
645
646int TransmitMixer::StopPlayingFileAsMicrophone()
647{
648 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
649 "TransmitMixer::StopPlayingFileAsMicrophone()");
650
651 if (!_filePlaying)
652 {
653 _engineStatisticsPtr->SetLastError(
654 VE_INVALID_OPERATION, kTraceWarning,
655 "StopPlayingFileAsMicrophone() isnot playing");
656 return 0;
657 }
658
659 CriticalSectionScoped cs(&_critSect);
660
661 if (_filePlayerPtr->StopPlayingFile() != 0)
662 {
663 _engineStatisticsPtr->SetLastError(
664 VE_CANNOT_STOP_PLAYOUT, kTraceError,
665 "StopPlayingFile() couldnot stop playing file");
666 return -1;
667 }
668
669 _filePlayerPtr->RegisterModuleFileCallback(NULL);
670 FilePlayer::DestroyFilePlayer(_filePlayerPtr);
671 _filePlayerPtr = NULL;
672 _filePlaying = false;
673
674 return 0;
675}
676
677int TransmitMixer::IsPlayingFileAsMicrophone() const
678{
679 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
680 "TransmitMixer::IsPlayingFileAsMicrophone()");
681 return _filePlaying;
682}
683
684int TransmitMixer::ScaleFileAsMicrophonePlayout(float scale)
685{
686 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
687 "TransmitMixer::ScaleFileAsMicrophonePlayout(scale=%5.3f)",
688 scale);
689
690 CriticalSectionScoped cs(&_critSect);
691
692 if (!_filePlaying)
693 {
694 _engineStatisticsPtr->SetLastError(
695 VE_INVALID_OPERATION, kTraceError,
696 "ScaleFileAsMicrophonePlayout() isnot playing file");
697 return -1;
698 }
699
700 if ((_filePlayerPtr == NULL) ||
701 (_filePlayerPtr->SetAudioScaling(scale) != 0))
702 {
703 _engineStatisticsPtr->SetLastError(
704 VE_BAD_ARGUMENT, kTraceError,
705 "SetAudioScaling() failed to scale playout");
706 return -1;
707 }
708
709 return 0;
710}
711
712int TransmitMixer::StartRecordingMicrophone(const char* fileName,
713 const CodecInst* codecInst)
714{
715 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
716 "TransmitMixer::StartRecordingMicrophone(fileName=%s)",
717 fileName);
718
719 CriticalSectionScoped cs(&_critSect);
720
721 if (_fileRecording)
722 {
723 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
724 "StartRecordingMicrophone() is already recording");
725 return 0;
726 }
727
728 FileFormats format;
729 const uint32_t notificationTime(0); // Not supported in VoE
730 CodecInst dummyCodec = { 100, "L16", 16000, 320, 1, 320000 };
731
732 if (codecInst != NULL &&
733 (codecInst->channels < 0 || codecInst->channels > 2))
734 {
735 _engineStatisticsPtr->SetLastError(
736 VE_BAD_ARGUMENT, kTraceError,
737 "StartRecordingMicrophone() invalid compression");
738 return (-1);
739 }
740 if (codecInst == NULL)
741 {
742 format = kFileFormatPcm16kHzFile;
743 codecInst = &dummyCodec;
744 } else if ((STR_CASE_CMP(codecInst->plname,"L16") == 0) ||
745 (STR_CASE_CMP(codecInst->plname,"PCMU") == 0) ||
746 (STR_CASE_CMP(codecInst->plname,"PCMA") == 0))
747 {
748 format = kFileFormatWavFile;
749 } else
750 {
751 format = kFileFormatCompressedFile;
752 }
753
754 // Destroy the old instance
755 if (_fileRecorderPtr)
756 {
757 _fileRecorderPtr->RegisterModuleFileCallback(NULL);
758 FileRecorder::DestroyFileRecorder(_fileRecorderPtr);
759 _fileRecorderPtr = NULL;
760 }
761
762 _fileRecorderPtr =
763 FileRecorder::CreateFileRecorder(_fileRecorderId,
764 (const FileFormats) format);
765 if (_fileRecorderPtr == NULL)
766 {
767 _engineStatisticsPtr->SetLastError(
768 VE_INVALID_ARGUMENT, kTraceError,
769 "StartRecordingMicrophone() fileRecorder format isnot correct");
770 return -1;
771 }
772
773 if (_fileRecorderPtr->StartRecordingAudioFile(
774 fileName,
775 (const CodecInst&) *codecInst,
776 notificationTime) != 0)
777 {
778 _engineStatisticsPtr->SetLastError(
779 VE_BAD_FILE, kTraceError,
780 "StartRecordingAudioFile() failed to start file recording");
781 _fileRecorderPtr->StopRecording();
782 FileRecorder::DestroyFileRecorder(_fileRecorderPtr);
783 _fileRecorderPtr = NULL;
784 return -1;
785 }
786 _fileRecorderPtr->RegisterModuleFileCallback(this);
787 _fileRecording = true;
788
789 return 0;
790}
791
792int TransmitMixer::StartRecordingMicrophone(OutStream* stream,
793 const CodecInst* codecInst)
794{
795 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
796 "TransmitMixer::StartRecordingMicrophone()");
797
798 CriticalSectionScoped cs(&_critSect);
799
800 if (_fileRecording)
801 {
802 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
803 "StartRecordingMicrophone() is already recording");
804 return 0;
805 }
806
807 FileFormats format;
808 const uint32_t notificationTime(0); // Not supported in VoE
809 CodecInst dummyCodec = { 100, "L16", 16000, 320, 1, 320000 };
810
811 if (codecInst != NULL && codecInst->channels != 1)
812 {
813 _engineStatisticsPtr->SetLastError(
814 VE_BAD_ARGUMENT, kTraceError,
815 "StartRecordingMicrophone() invalid compression");
816 return (-1);
817 }
818 if (codecInst == NULL)
819 {
820 format = kFileFormatPcm16kHzFile;
821 codecInst = &dummyCodec;
822 } else if ((STR_CASE_CMP(codecInst->plname,"L16") == 0) ||
823 (STR_CASE_CMP(codecInst->plname,"PCMU") == 0) ||
824 (STR_CASE_CMP(codecInst->plname,"PCMA") == 0))
825 {
826 format = kFileFormatWavFile;
827 } else
828 {
829 format = kFileFormatCompressedFile;
830 }
831
832 // Destroy the old instance
833 if (_fileRecorderPtr)
834 {
835 _fileRecorderPtr->RegisterModuleFileCallback(NULL);
836 FileRecorder::DestroyFileRecorder(_fileRecorderPtr);
837 _fileRecorderPtr = NULL;
838 }
839
840 _fileRecorderPtr =
841 FileRecorder::CreateFileRecorder(_fileRecorderId,
842 (const FileFormats) format);
843 if (_fileRecorderPtr == NULL)
844 {
845 _engineStatisticsPtr->SetLastError(
846 VE_INVALID_ARGUMENT, kTraceError,
847 "StartRecordingMicrophone() fileRecorder format isnot correct");
848 return -1;
849 }
850
851 if (_fileRecorderPtr->StartRecordingAudioFile(*stream,
852 *codecInst,
853 notificationTime) != 0)
854 {
855 _engineStatisticsPtr->SetLastError(VE_BAD_FILE, kTraceError,
856 "StartRecordingAudioFile() failed to start file recording");
857 _fileRecorderPtr->StopRecording();
858 FileRecorder::DestroyFileRecorder(_fileRecorderPtr);
859 _fileRecorderPtr = NULL;
860 return -1;
861 }
862
863 _fileRecorderPtr->RegisterModuleFileCallback(this);
864 _fileRecording = true;
865
866 return 0;
867}
868
869
870int TransmitMixer::StopRecordingMicrophone()
871{
872 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
873 "TransmitMixer::StopRecordingMicrophone()");
874
875 CriticalSectionScoped cs(&_critSect);
876
877 if (!_fileRecording)
878 {
879 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
880 "StopRecordingMicrophone() isnot recording");
881 return 0;
882 }
883
884 if (_fileRecorderPtr->StopRecording() != 0)
885 {
886 _engineStatisticsPtr->SetLastError(
887 VE_STOP_RECORDING_FAILED, kTraceError,
888 "StopRecording(), could not stop recording");
889 return -1;
890 }
891 _fileRecorderPtr->RegisterModuleFileCallback(NULL);
892 FileRecorder::DestroyFileRecorder(_fileRecorderPtr);
893 _fileRecorderPtr = NULL;
894 _fileRecording = false;
895
896 return 0;
897}
898
899int TransmitMixer::StartRecordingCall(const char* fileName,
900 const CodecInst* codecInst)
901{
902 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
903 "TransmitMixer::StartRecordingCall(fileName=%s)", fileName);
904
905 if (_fileCallRecording)
906 {
907 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
908 "StartRecordingCall() is already recording");
909 return 0;
910 }
911
912 FileFormats format;
913 const uint32_t notificationTime(0); // Not supported in VoE
914 CodecInst dummyCodec = { 100, "L16", 16000, 320, 1, 320000 };
915
916 if (codecInst != NULL && codecInst->channels != 1)
917 {
918 _engineStatisticsPtr->SetLastError(
919 VE_BAD_ARGUMENT, kTraceError,
920 "StartRecordingCall() invalid compression");
921 return (-1);
922 }
923 if (codecInst == NULL)
924 {
925 format = kFileFormatPcm16kHzFile;
926 codecInst = &dummyCodec;
927 } else if ((STR_CASE_CMP(codecInst->plname,"L16") == 0) ||
928 (STR_CASE_CMP(codecInst->plname,"PCMU") == 0) ||
929 (STR_CASE_CMP(codecInst->plname,"PCMA") == 0))
930 {
931 format = kFileFormatWavFile;
932 } else
933 {
934 format = kFileFormatCompressedFile;
935 }
936
937 CriticalSectionScoped cs(&_critSect);
938
939 // Destroy the old instance
940 if (_fileCallRecorderPtr)
941 {
942 _fileCallRecorderPtr->RegisterModuleFileCallback(NULL);
943 FileRecorder::DestroyFileRecorder(_fileCallRecorderPtr);
944 _fileCallRecorderPtr = NULL;
945 }
946
947 _fileCallRecorderPtr
948 = FileRecorder::CreateFileRecorder(_fileCallRecorderId,
949 (const FileFormats) format);
950 if (_fileCallRecorderPtr == NULL)
951 {
952 _engineStatisticsPtr->SetLastError(
953 VE_INVALID_ARGUMENT, kTraceError,
954 "StartRecordingCall() fileRecorder format isnot correct");
955 return -1;
956 }
957
958 if (_fileCallRecorderPtr->StartRecordingAudioFile(
959 fileName,
960 (const CodecInst&) *codecInst,
961 notificationTime) != 0)
962 {
963 _engineStatisticsPtr->SetLastError(
964 VE_BAD_FILE, kTraceError,
965 "StartRecordingAudioFile() failed to start file recording");
966 _fileCallRecorderPtr->StopRecording();
967 FileRecorder::DestroyFileRecorder(_fileCallRecorderPtr);
968 _fileCallRecorderPtr = NULL;
969 return -1;
970 }
971 _fileCallRecorderPtr->RegisterModuleFileCallback(this);
972 _fileCallRecording = true;
973
974 return 0;
975}
976
977int TransmitMixer::StartRecordingCall(OutStream* stream,
978 const CodecInst* codecInst)
979{
980 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
981 "TransmitMixer::StartRecordingCall()");
982
983 if (_fileCallRecording)
984 {
985 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
986 "StartRecordingCall() is already recording");
987 return 0;
988 }
989
990 FileFormats format;
991 const uint32_t notificationTime(0); // Not supported in VoE
992 CodecInst dummyCodec = { 100, "L16", 16000, 320, 1, 320000 };
993
994 if (codecInst != NULL && codecInst->channels != 1)
995 {
996 _engineStatisticsPtr->SetLastError(
997 VE_BAD_ARGUMENT, kTraceError,
998 "StartRecordingCall() invalid compression");
999 return (-1);
1000 }
1001 if (codecInst == NULL)
1002 {
1003 format = kFileFormatPcm16kHzFile;
1004 codecInst = &dummyCodec;
1005 } else if ((STR_CASE_CMP(codecInst->plname,"L16") == 0) ||
1006 (STR_CASE_CMP(codecInst->plname,"PCMU") == 0) ||
1007 (STR_CASE_CMP(codecInst->plname,"PCMA") == 0))
1008 {
1009 format = kFileFormatWavFile;
1010 } else
1011 {
1012 format = kFileFormatCompressedFile;
1013 }
1014
1015 CriticalSectionScoped cs(&_critSect);
1016
1017 // Destroy the old instance
1018 if (_fileCallRecorderPtr)
1019 {
1020 _fileCallRecorderPtr->RegisterModuleFileCallback(NULL);
1021 FileRecorder::DestroyFileRecorder(_fileCallRecorderPtr);
1022 _fileCallRecorderPtr = NULL;
1023 }
1024
1025 _fileCallRecorderPtr =
1026 FileRecorder::CreateFileRecorder(_fileCallRecorderId,
1027 (const FileFormats) format);
1028 if (_fileCallRecorderPtr == NULL)
1029 {
1030 _engineStatisticsPtr->SetLastError(
1031 VE_INVALID_ARGUMENT, kTraceError,
1032 "StartRecordingCall() fileRecorder format isnot correct");
1033 return -1;
1034 }
1035
1036 if (_fileCallRecorderPtr->StartRecordingAudioFile(*stream,
1037 *codecInst,
1038 notificationTime) != 0)
1039 {
1040 _engineStatisticsPtr->SetLastError(VE_BAD_FILE, kTraceError,
1041 "StartRecordingAudioFile() failed to start file recording");
1042 _fileCallRecorderPtr->StopRecording();
1043 FileRecorder::DestroyFileRecorder(_fileCallRecorderPtr);
1044 _fileCallRecorderPtr = NULL;
1045 return -1;
1046 }
1047
1048 _fileCallRecorderPtr->RegisterModuleFileCallback(this);
1049 _fileCallRecording = true;
1050
1051 return 0;
1052}
1053
1054int TransmitMixer::StopRecordingCall()
1055{
1056 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
1057 "TransmitMixer::StopRecordingCall()");
1058
1059 if (!_fileCallRecording)
1060 {
1061 WEBRTC_TRACE(kTraceError, kTraceVoice, VoEId(_instanceId, -1),
1062 "StopRecordingCall() file isnot recording");
1063 return -1;
1064 }
1065
1066 CriticalSectionScoped cs(&_critSect);
1067
1068 if (_fileCallRecorderPtr->StopRecording() != 0)
1069 {
1070 _engineStatisticsPtr->SetLastError(
1071 VE_STOP_RECORDING_FAILED, kTraceError,
1072 "StopRecording(), could not stop recording");
1073 return -1;
1074 }
1075
1076 _fileCallRecorderPtr->RegisterModuleFileCallback(NULL);
1077 FileRecorder::DestroyFileRecorder(_fileCallRecorderPtr);
1078 _fileCallRecorderPtr = NULL;
1079 _fileCallRecording = false;
1080
1081 return 0;
1082}
1083
1084void
1085TransmitMixer::SetMixWithMicStatus(bool mix)
1086{
1087 _mixFileWithMicrophone = mix;
1088}
1089
1090int TransmitMixer::RegisterExternalMediaProcessing(
1091 VoEMediaProcess* object,
1092 ProcessingTypes type) {
1093 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
1094 "TransmitMixer::RegisterExternalMediaProcessing()");
1095
1096 CriticalSectionScoped cs(&_callbackCritSect);
1097 if (!object) {
1098 return -1;
1099 }
1100
1101 // Store the callback object according to the processing type.
1102 if (type == kRecordingAllChannelsMixed) {
1103 external_postproc_ptr_ = object;
1104 } else if (type == kRecordingPreprocessing) {
1105 external_preproc_ptr_ = object;
1106 } else {
1107 return -1;
1108 }
1109 return 0;
1110}
1111
1112int TransmitMixer::DeRegisterExternalMediaProcessing(ProcessingTypes type) {
1113 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
1114 "TransmitMixer::DeRegisterExternalMediaProcessing()");
1115
1116 CriticalSectionScoped cs(&_callbackCritSect);
1117 if (type == kRecordingAllChannelsMixed) {
1118 external_postproc_ptr_ = NULL;
1119 } else if (type == kRecordingPreprocessing) {
1120 external_preproc_ptr_ = NULL;
1121 } else {
1122 return -1;
1123 }
1124 return 0;
1125}
1126
1127int
1128TransmitMixer::SetMute(bool enable)
1129{
1130 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
1131 "TransmitMixer::SetMute(enable=%d)", enable);
1132 _mute = enable;
1133 return 0;
1134}
1135
1136bool
1137TransmitMixer::Mute() const
1138{
1139 return _mute;
1140}
1141
1142int8_t TransmitMixer::AudioLevel() const
1143{
1144 // Speech + file level [0,9]
1145 return _audioLevel.Level();
1146}
1147
1148int16_t TransmitMixer::AudioLevelFullRange() const
1149{
1150 // Speech + file level [0,32767]
1151 return _audioLevel.LevelFullRange();
1152}
1153
1154bool TransmitMixer::IsRecordingCall()
1155{
1156 return _fileCallRecording;
1157}
1158
1159bool TransmitMixer::IsRecordingMic()
1160{
1161 CriticalSectionScoped cs(&_critSect);
1162 return _fileRecording;
1163}
1164
1165void TransmitMixer::GenerateAudioFrame(const int16_t* audio,
1166 int samples_per_channel,
1167 int num_channels,
1168 int sample_rate_hz) {
1169 int codec_rate;
1170 int num_codec_channels;
1171 GetSendCodecInfo(&codec_rate, &num_codec_channels);
1172 // TODO(ajm): This currently restricts the sample rate to 32 kHz.
1173 // See: https://code.google.com/p/webrtc/issues/detail?id=3146
1174 // When 48 kHz is supported natively by AudioProcessing, this will have
1175 // to be changed to handle 44.1 kHz.
1176 codec_rate = std::min(codec_rate, kAudioProcMaxNativeSampleRateHz);
1177 stereo_codec_ = num_codec_channels == 2;
1178
1179 if (!mono_buffer_.get()) {
1180 // Temporary space for DownConvertToCodecFormat.
1181 mono_buffer_.reset(new int16_t[kMaxMonoDataSizeSamples]);
1182 }
1183 DownConvertToCodecFormat(audio,
1184 samples_per_channel,
1185 num_channels,
1186 sample_rate_hz,
1187 num_codec_channels,
1188 codec_rate,
1189 mono_buffer_.get(),
1190 &resampler_,
1191 &_audioFrame);
1192}
1193
1194int32_t TransmitMixer::RecordAudioToFile(
1195 uint32_t mixingFrequency)
1196{
1197 CriticalSectionScoped cs(&_critSect);
1198 if (_fileRecorderPtr == NULL)
1199 {
1200 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
1201 "TransmitMixer::RecordAudioToFile() filerecorder doesnot"
1202 "exist");
1203 return -1;
1204 }
1205
1206 if (_fileRecorderPtr->RecordAudioToFile(_audioFrame) != 0)
1207 {
1208 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
1209 "TransmitMixer::RecordAudioToFile() file recording"
1210 "failed");
1211 return -1;
1212 }
1213
1214 return 0;
1215}
1216
1217int32_t TransmitMixer::MixOrReplaceAudioWithFile(
1218 int mixingFrequency)
1219{
1220 scoped_array<int16_t> fileBuffer(new int16_t[640]);
1221
1222 int fileSamples(0);
1223 {
1224 CriticalSectionScoped cs(&_critSect);
1225 if (_filePlayerPtr == NULL)
1226 {
1227 WEBRTC_TRACE(kTraceWarning, kTraceVoice,
1228 VoEId(_instanceId, -1),
1229 "TransmitMixer::MixOrReplaceAudioWithFile()"
1230 "fileplayer doesnot exist");
1231 return -1;
1232 }
1233
1234 if (_filePlayerPtr->Get10msAudioFromFile(fileBuffer.get(),
1235 fileSamples,
1236 mixingFrequency) == -1)
1237 {
1238 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
1239 "TransmitMixer::MixOrReplaceAudioWithFile() file"
1240 " mixing failed");
1241 return -1;
1242 }
1243 }
1244
1245 assert(_audioFrame.samples_per_channel_ == fileSamples);
1246
1247 if (_mixFileWithMicrophone)
1248 {
1249 // Currently file stream is always mono.
1250 // TODO(xians): Change the code when FilePlayer supports real stereo.
1251 MixWithSat(_audioFrame.data_,
1252 _audioFrame.num_channels_,
1253 fileBuffer.get(),
1254 1,
1255 fileSamples);
1256 } else
1257 {
1258 // Replace ACM audio with file.
1259 // Currently file stream is always mono.
1260 // TODO(xians): Change the code when FilePlayer supports real stereo.
1261 _audioFrame.UpdateFrame(-1,
1262 -1,
1263 fileBuffer.get(),
1264 fileSamples,
1265 mixingFrequency,
1266 AudioFrame::kNormalSpeech,
1267 AudioFrame::kVadUnknown,
1268 1);
1269 }
1270 return 0;
1271}
1272
1273void TransmitMixer::ProcessAudio(int delay_ms, int clock_drift,
1274 int current_mic_level, bool key_pressed) {
1275 if (audioproc_->set_stream_delay_ms(delay_ms) != 0) {
1276 // A redundant warning is reported in AudioDevice, which we've throttled
1277 // to avoid flooding the logs. Relegate this one to LS_VERBOSE to avoid
1278 // repeating the problem here.
1279 LOG_FERR1(LS_VERBOSE, set_stream_delay_ms, delay_ms);
1280 }
1281
1282 GainControl* agc = audioproc_->gain_control();
1283 if (agc->set_stream_analog_level(current_mic_level) != 0) {
1284 LOG_FERR1(LS_ERROR, set_stream_analog_level, current_mic_level);
1285 assert(false);
1286 }
1287
1288 EchoCancellation* aec = audioproc_->echo_cancellation();
1289 if (aec->is_drift_compensation_enabled()) {
1290 aec->set_stream_drift_samples(clock_drift);
1291 }
1292
1293 audioproc_->set_stream_key_pressed(key_pressed);
1294
1295 int err = audioproc_->ProcessStream(&_audioFrame);
1296 if (err != 0) {
1297 LOG(LS_ERROR) << "ProcessStream() error: " << err;
1298 assert(false);
1299 }
1300
1301 // Store new capture level. Only updated when analog AGC is enabled.
1302 _captureLevel = agc->stream_analog_level();
1303
1304 CriticalSectionScoped cs(&_critSect);
1305 // Triggers a callback in OnPeriodicProcess().
1306 _saturationWarning |= agc->stream_is_saturated();
1307}
1308
1309#ifdef WEBRTC_VOICE_ENGINE_TYPING_DETECTION
1310void TransmitMixer::TypingDetection(bool keyPressed)
1311{
1312 // We let the VAD determine if we're using this feature or not.
1313 if (_audioFrame.vad_activity_ == AudioFrame::kVadUnknown) {
1314 return;
1315 }
1316
1317 bool vadActive = _audioFrame.vad_activity_ == AudioFrame::kVadActive;
1318 if (_typingDetection.Process(keyPressed, vadActive)) {
1319 _typingNoiseWarningPending = true;
1320 _typingNoiseDetected = true;
1321 } else {
1322 // If there is already a warning pending, do not change the state.
1323 // Otherwise set a warning pending if last callback was for noise detected.
1324 if (!_typingNoiseWarningPending && _typingNoiseDetected) {
1325 _typingNoiseWarningPending = true;
1326 _typingNoiseDetected = false;
1327 }
1328 }
1329}
1330#endif
1331
1332int TransmitMixer::GetMixingFrequency()
1333{
1334 assert(_audioFrame.sample_rate_hz_ != 0);
1335 return _audioFrame.sample_rate_hz_;
1336}
1337
1338#ifdef WEBRTC_VOICE_ENGINE_TYPING_DETECTION
1339int TransmitMixer::TimeSinceLastTyping(int &seconds)
1340{
1341 // We check in VoEAudioProcessingImpl that this is only called when
1342 // typing detection is active.
1343 seconds = _typingDetection.TimeSinceLastDetectionInSeconds();
1344 return 0;
1345}
1346#endif
1347
1348#ifdef WEBRTC_VOICE_ENGINE_TYPING_DETECTION
1349int TransmitMixer::SetTypingDetectionParameters(int timeWindow,
1350 int costPerTyping,
1351 int reportingThreshold,
1352 int penaltyDecay,
1353 int typeEventDelay)
1354{
1355 _typingDetection.SetParameters(timeWindow,
1356 costPerTyping,
1357 reportingThreshold,
1358 penaltyDecay,
1359 typeEventDelay,
1360 0);
1361 return 0;
1362}
1363#endif
1364
1365void TransmitMixer::EnableStereoChannelSwapping(bool enable) {
1366 swap_stereo_channels_ = enable;
1367}
1368
1369bool TransmitMixer::IsStereoChannelSwappingEnabled() {
1370 return swap_stereo_channels_;
1371}
1372
1373} // namespace voe
1374} // namespace webrtc