2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
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.
11 #include "webrtc/voice_engine/transmit_mixer.h"
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"
25 #define WEBRTC_ABS(a) (((a) < 0) ? -(a) : (a))
31 // Used for downmixing before resampling.
32 // TODO(ajm): audio_device should advertise the maximum sample rate it can
34 static const int kMaxMonoDeviceDataSizeSamples = 960; // 10 ms, 96 kHz, mono.
36 // TODO(ajm): The thread safety of this is dubious...
38 TransmitMixer::OnPeriodicProcess()
40 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, -1),
41 "TransmitMixer::OnPeriodicProcess()");
43 #if defined(WEBRTC_VOICE_ENGINE_TYPING_DETECTION)
44 if (_typingNoiseWarningPending)
46 CriticalSectionScoped cs(&_callbackCritSect);
47 if (_voiceEngineObserverPtr)
49 if (_typingNoiseDetected) {
50 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
51 "TransmitMixer::OnPeriodicProcess() => "
52 "CallbackOnError(VE_TYPING_NOISE_WARNING)");
53 _voiceEngineObserverPtr->CallbackOnError(-1,
54 VE_TYPING_NOISE_WARNING);
56 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
57 "TransmitMixer::OnPeriodicProcess() => "
58 "CallbackOnError(VE_TYPING_NOISE_OFF_WARNING)");
59 _voiceEngineObserverPtr->CallbackOnError(
60 -1, VE_TYPING_NOISE_OFF_WARNING);
63 _typingNoiseWarningPending = false;
67 bool saturationWarning = false;
69 // Modify |_saturationWarning| under lock to avoid conflict with write op
70 // in ProcessAudio and also ensure that we don't hold the lock during the
72 CriticalSectionScoped cs(&_critSect);
73 saturationWarning = _saturationWarning;
74 if (_saturationWarning)
75 _saturationWarning = false;
78 if (saturationWarning)
80 CriticalSectionScoped cs(&_callbackCritSect);
81 if (_voiceEngineObserverPtr)
83 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
84 "TransmitMixer::OnPeriodicProcess() =>"
85 " CallbackOnError(VE_SATURATION_WARNING)");
86 _voiceEngineObserverPtr->CallbackOnError(-1, VE_SATURATION_WARNING);
92 void TransmitMixer::PlayNotification(int32_t id,
95 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, -1),
96 "TransmitMixer::PlayNotification(id=%d, durationMs=%d)",
102 void TransmitMixer::RecordNotification(int32_t id,
105 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
106 "TransmitMixer::RecordNotification(id=%d, durationMs=%d)",
112 void TransmitMixer::PlayFileEnded(int32_t id)
114 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, -1),
115 "TransmitMixer::PlayFileEnded(id=%d)", id);
117 assert(id == _filePlayerId);
119 CriticalSectionScoped cs(&_critSect);
121 _filePlaying = false;
122 WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId, -1),
123 "TransmitMixer::PlayFileEnded() =>"
124 "file player module is shutdown");
128 TransmitMixer::RecordFileEnded(int32_t id)
130 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, -1),
131 "TransmitMixer::RecordFileEnded(id=%d)", id);
133 if (id == _fileRecorderId)
135 CriticalSectionScoped cs(&_critSect);
136 _fileRecording = false;
137 WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId, -1),
138 "TransmitMixer::RecordFileEnded() => fileRecorder module"
140 } else if (id == _fileCallRecorderId)
142 CriticalSectionScoped cs(&_critSect);
143 _fileCallRecording = false;
144 WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId, -1),
145 "TransmitMixer::RecordFileEnded() => fileCallRecorder"
146 "module is shutdown");
151 TransmitMixer::Create(TransmitMixer*& mixer, uint32_t instanceId)
153 WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(instanceId, -1),
154 "TransmitMixer::Create(instanceId=%d)", instanceId);
155 mixer = new TransmitMixer(instanceId);
158 WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(instanceId, -1),
159 "TransmitMixer::Create() unable to allocate memory"
167 TransmitMixer::Destroy(TransmitMixer*& mixer)
176 TransmitMixer::TransmitMixer(uint32_t instanceId) :
177 _engineStatisticsPtr(NULL),
178 _channelManagerPtr(NULL),
180 _voiceEngineObserverPtr(NULL),
181 _processThreadPtr(NULL),
182 _filePlayerPtr(NULL),
183 _fileRecorderPtr(NULL),
184 _fileCallRecorderPtr(NULL),
185 // Avoid conflict with other channels by adding 1024 - 1026,
186 // won't use as much as 1024 channels.
187 _filePlayerId(instanceId + 1024),
188 _fileRecorderId(instanceId + 1025),
189 _fileCallRecorderId(instanceId + 1026),
191 _fileRecording(false),
192 _fileCallRecording(false),
194 _critSect(*CriticalSectionWrapper::CreateCriticalSection()),
195 _callbackCritSect(*CriticalSectionWrapper::CreateCriticalSection()),
196 #ifdef WEBRTC_VOICE_ENGINE_TYPING_DETECTION
198 _timeSinceLastTyping(0),
200 _typingNoiseWarningPending(false),
201 _typingNoiseDetected(false),
202 _timeWindow(10), // 10ms slots accepted to count as a hit
203 _costPerTyping(100), // Penalty added for a typing + activity coincide
204 _reportingThreshold(300), // Threshold for _penaltyCounter
205 _penaltyDecay(1), // how much we reduce _penaltyCounter every 10 ms.
206 _typeEventDelay(2), // how "old" event we check for
208 _saturationWarning(false),
209 _instanceId(instanceId),
210 _mixFileWithMicrophone(false),
212 external_postproc_ptr_(NULL),
213 external_preproc_ptr_(NULL),
215 _remainingMuteMicTimeMs(0),
216 stereo_codec_(false),
217 swap_stereo_channels_(false)
219 WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId, -1),
220 "TransmitMixer::TransmitMixer() - ctor");
223 TransmitMixer::~TransmitMixer()
225 WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId, -1),
226 "TransmitMixer::~TransmitMixer() - dtor");
227 _monitorModule.DeRegisterObserver();
228 if (_processThreadPtr)
230 _processThreadPtr->DeRegisterModule(&_monitorModule);
232 DeRegisterExternalMediaProcessing(kRecordingAllChannelsMixed);
233 DeRegisterExternalMediaProcessing(kRecordingPreprocessing);
235 CriticalSectionScoped cs(&_critSect);
236 if (_fileRecorderPtr)
238 _fileRecorderPtr->RegisterModuleFileCallback(NULL);
239 _fileRecorderPtr->StopRecording();
240 FileRecorder::DestroyFileRecorder(_fileRecorderPtr);
241 _fileRecorderPtr = NULL;
243 if (_fileCallRecorderPtr)
245 _fileCallRecorderPtr->RegisterModuleFileCallback(NULL);
246 _fileCallRecorderPtr->StopRecording();
247 FileRecorder::DestroyFileRecorder(_fileCallRecorderPtr);
248 _fileCallRecorderPtr = NULL;
252 _filePlayerPtr->RegisterModuleFileCallback(NULL);
253 _filePlayerPtr->StopPlayingFile();
254 FilePlayer::DestroyFilePlayer(_filePlayerPtr);
255 _filePlayerPtr = NULL;
259 delete &_callbackCritSect;
263 TransmitMixer::SetEngineInformation(ProcessThread& processThread,
264 Statistics& engineStatistics,
265 ChannelManager& channelManager)
267 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
268 "TransmitMixer::SetEngineInformation()");
270 _processThreadPtr = &processThread;
271 _engineStatisticsPtr = &engineStatistics;
272 _channelManagerPtr = &channelManager;
274 if (_processThreadPtr->RegisterModule(&_monitorModule) == -1)
276 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
277 "TransmitMixer::SetEngineInformation() failed to"
278 "register the monitor module");
281 _monitorModule.RegisterObserver(*this);
288 TransmitMixer::RegisterVoiceEngineObserver(VoiceEngineObserver& observer)
290 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
291 "TransmitMixer::RegisterVoiceEngineObserver()");
292 CriticalSectionScoped cs(&_callbackCritSect);
294 if (_voiceEngineObserverPtr)
296 _engineStatisticsPtr->SetLastError(
297 VE_INVALID_OPERATION, kTraceError,
298 "RegisterVoiceEngineObserver() observer already enabled");
301 _voiceEngineObserverPtr = &observer;
306 TransmitMixer::SetAudioProcessingModule(AudioProcessing* audioProcessingModule)
308 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
309 "TransmitMixer::SetAudioProcessingModule("
310 "audioProcessingModule=0x%x)",
311 audioProcessingModule);
312 audioproc_ = audioProcessingModule;
316 void TransmitMixer::GetSendCodecInfo(int* max_sample_rate, int* max_channels) {
317 *max_sample_rate = 8000;
319 for (ChannelManager::Iterator it(_channelManagerPtr); it.IsValid();
321 Channel* channel = it.GetChannel();
322 if (channel->Sending()) {
324 channel->GetSendCodec(codec);
325 // TODO(tlegrand): Remove the 32 kHz restriction once we have full 48 kHz
326 // support in Audio Coding Module.
327 *max_sample_rate = std::min(32000,
328 std::max(*max_sample_rate, codec.plfreq));
329 *max_channels = std::max(*max_channels, codec.channels);
335 TransmitMixer::PrepareDemux(const void* audioSamples,
338 uint32_t samplesPerSec,
339 uint16_t totalDelayMS,
341 uint16_t currentMicLevel,
344 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, -1),
345 "TransmitMixer::PrepareDemux(nSamples=%u, nChannels=%u,"
346 "samplesPerSec=%u, totalDelayMS=%u, clockDrift=%d,"
347 "currentMicLevel=%u)", nSamples, nChannels, samplesPerSec,
348 totalDelayMS, clockDrift, currentMicLevel);
350 // --- Resample input audio and create/store the initial audio frame
351 if (GenerateAudioFrame(static_cast<const int16_t*>(audioSamples),
354 samplesPerSec) == -1)
360 CriticalSectionScoped cs(&_callbackCritSect);
361 if (external_preproc_ptr_) {
362 external_preproc_ptr_->Process(-1, kRecordingPreprocessing,
364 _audioFrame.samples_per_channel_,
365 _audioFrame.sample_rate_hz_,
366 _audioFrame.num_channels_ == 2);
370 // --- Near-end audio processing.
371 ProcessAudio(totalDelayMS, clockDrift, currentMicLevel);
373 if (swap_stereo_channels_ && stereo_codec_)
374 // Only bother swapping if we're using a stereo codec.
375 AudioFrameOperations::SwapStereoChannels(&_audioFrame);
377 // --- Annoying typing detection (utilizes the APM/VAD decision)
378 #ifdef WEBRTC_VOICE_ENGINE_TYPING_DETECTION
379 TypingDetection(keyPressed);
382 // --- Mute during DTMF tone if direct feedback is enabled
383 if (_remainingMuteMicTimeMs > 0)
385 AudioFrameOperations::Mute(_audioFrame);
386 _remainingMuteMicTimeMs -= 10;
387 if (_remainingMuteMicTimeMs < 0)
389 _remainingMuteMicTimeMs = 0;
396 AudioFrameOperations::Mute(_audioFrame);
399 // --- Mix with file (does not affect the mixing frequency)
402 MixOrReplaceAudioWithFile(_audioFrame.sample_rate_hz_);
405 // --- Record to file
408 RecordAudioToFile(_audioFrame.sample_rate_hz_);
412 CriticalSectionScoped cs(&_callbackCritSect);
413 if (external_postproc_ptr_) {
414 external_postproc_ptr_->Process(-1, kRecordingAllChannelsMixed,
416 _audioFrame.samples_per_channel_,
417 _audioFrame.sample_rate_hz_,
418 _audioFrame.num_channels_ == 2);
422 // --- Measure audio level of speech after all processing.
423 _audioLevel.ComputeLevel(_audioFrame);
428 TransmitMixer::DemuxAndMix()
430 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, -1),
431 "TransmitMixer::DemuxAndMix()");
433 for (ChannelManager::Iterator it(_channelManagerPtr); it.IsValid();
436 Channel* channelPtr = it.GetChannel();
437 if (channelPtr->InputIsOnHold())
439 channelPtr->UpdateLocalTimeStamp();
440 } else if (channelPtr->Sending())
442 // Demultiplex makes a copy of its input.
443 channelPtr->Demultiplex(_audioFrame);
444 channelPtr->PrepareEncodeAndSend(_audioFrame.sample_rate_hz_);
450 void TransmitMixer::DemuxAndMix(const int voe_channels[],
451 int number_of_voe_channels) {
452 for (int i = 0; i < number_of_voe_channels; ++i) {
453 voe::ChannelOwner ch = _channelManagerPtr->GetChannel(voe_channels[i]);
454 voe::Channel* channel_ptr = ch.channel();
456 if (channel_ptr->InputIsOnHold()) {
457 channel_ptr->UpdateLocalTimeStamp();
458 } else if (channel_ptr->Sending()) {
459 // Demultiplex makes a copy of its input.
460 channel_ptr->Demultiplex(_audioFrame);
461 channel_ptr->PrepareEncodeAndSend(_audioFrame.sample_rate_hz_);
468 TransmitMixer::EncodeAndSend()
470 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, -1),
471 "TransmitMixer::EncodeAndSend()");
473 for (ChannelManager::Iterator it(_channelManagerPtr); it.IsValid();
476 Channel* channelPtr = it.GetChannel();
477 if (channelPtr->Sending() && !channelPtr->InputIsOnHold())
479 channelPtr->EncodeAndSend();
485 void TransmitMixer::EncodeAndSend(const int voe_channels[],
486 int number_of_voe_channels) {
487 for (int i = 0; i < number_of_voe_channels; ++i) {
488 voe::ChannelOwner ch = _channelManagerPtr->GetChannel(voe_channels[i]);
489 voe::Channel* channel_ptr = ch.channel();
490 if (channel_ptr && channel_ptr->Sending() && !channel_ptr->InputIsOnHold())
491 channel_ptr->EncodeAndSend();
495 uint32_t TransmitMixer::CaptureLevel() const
497 return _captureLevel;
501 TransmitMixer::UpdateMuteMicrophoneTime(uint32_t lengthMs)
503 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
504 "TransmitMixer::UpdateMuteMicrophoneTime(lengthMs=%d)",
506 _remainingMuteMicTimeMs = lengthMs;
510 TransmitMixer::StopSend()
512 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
513 "TransmitMixer::StopSend()");
518 int TransmitMixer::StartPlayingFileAsMicrophone(const char* fileName,
524 const CodecInst* codecInst)
526 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
527 "TransmitMixer::StartPlayingFileAsMicrophone("
528 "fileNameUTF8[]=%s,loop=%d, format=%d, volumeScaling=%5.3f,"
529 " startPosition=%d, stopPosition=%d)", fileName, loop,
530 format, volumeScaling, startPosition, stopPosition);
534 _engineStatisticsPtr->SetLastError(
535 VE_ALREADY_PLAYING, kTraceWarning,
536 "StartPlayingFileAsMicrophone() is already playing");
540 CriticalSectionScoped cs(&_critSect);
542 // Destroy the old instance
545 _filePlayerPtr->RegisterModuleFileCallback(NULL);
546 FilePlayer::DestroyFilePlayer(_filePlayerPtr);
547 _filePlayerPtr = NULL;
550 // Dynamically create the instance
552 = FilePlayer::CreateFilePlayer(_filePlayerId,
553 (const FileFormats) format);
555 if (_filePlayerPtr == NULL)
557 _engineStatisticsPtr->SetLastError(
558 VE_INVALID_ARGUMENT, kTraceError,
559 "StartPlayingFileAsMicrophone() filePlayer format isnot correct");
563 const uint32_t notificationTime(0);
565 if (_filePlayerPtr->StartPlayingFile(
572 (const CodecInst*) codecInst) != 0)
574 _engineStatisticsPtr->SetLastError(
575 VE_BAD_FILE, kTraceError,
576 "StartPlayingFile() failed to start file playout");
577 _filePlayerPtr->StopPlayingFile();
578 FilePlayer::DestroyFilePlayer(_filePlayerPtr);
579 _filePlayerPtr = NULL;
583 _filePlayerPtr->RegisterModuleFileCallback(this);
589 int TransmitMixer::StartPlayingFileAsMicrophone(InStream* stream,
594 const CodecInst* codecInst)
596 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
597 "TransmitMixer::StartPlayingFileAsMicrophone(format=%d,"
598 " volumeScaling=%5.3f, startPosition=%d, stopPosition=%d)",
599 format, volumeScaling, startPosition, stopPosition);
603 _engineStatisticsPtr->SetLastError(
604 VE_BAD_FILE, kTraceError,
605 "StartPlayingFileAsMicrophone() NULL as input stream");
611 _engineStatisticsPtr->SetLastError(
612 VE_ALREADY_PLAYING, kTraceWarning,
613 "StartPlayingFileAsMicrophone() is already playing");
617 CriticalSectionScoped cs(&_critSect);
619 // Destroy the old instance
622 _filePlayerPtr->RegisterModuleFileCallback(NULL);
623 FilePlayer::DestroyFilePlayer(_filePlayerPtr);
624 _filePlayerPtr = NULL;
627 // Dynamically create the instance
629 = FilePlayer::CreateFilePlayer(_filePlayerId,
630 (const FileFormats) format);
632 if (_filePlayerPtr == NULL)
634 _engineStatisticsPtr->SetLastError(
635 VE_INVALID_ARGUMENT, kTraceWarning,
636 "StartPlayingFileAsMicrophone() filePlayer format isnot correct");
640 const uint32_t notificationTime(0);
642 if (_filePlayerPtr->StartPlayingFile(
648 (const CodecInst*) codecInst) != 0)
650 _engineStatisticsPtr->SetLastError(
651 VE_BAD_FILE, kTraceError,
652 "StartPlayingFile() failed to start file playout");
653 _filePlayerPtr->StopPlayingFile();
654 FilePlayer::DestroyFilePlayer(_filePlayerPtr);
655 _filePlayerPtr = NULL;
658 _filePlayerPtr->RegisterModuleFileCallback(this);
664 int TransmitMixer::StopPlayingFileAsMicrophone()
666 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
667 "TransmitMixer::StopPlayingFileAsMicrophone()");
671 _engineStatisticsPtr->SetLastError(
672 VE_INVALID_OPERATION, kTraceWarning,
673 "StopPlayingFileAsMicrophone() isnot playing");
677 CriticalSectionScoped cs(&_critSect);
679 if (_filePlayerPtr->StopPlayingFile() != 0)
681 _engineStatisticsPtr->SetLastError(
682 VE_CANNOT_STOP_PLAYOUT, kTraceError,
683 "StopPlayingFile() couldnot stop playing file");
687 _filePlayerPtr->RegisterModuleFileCallback(NULL);
688 FilePlayer::DestroyFilePlayer(_filePlayerPtr);
689 _filePlayerPtr = NULL;
690 _filePlaying = false;
695 int TransmitMixer::IsPlayingFileAsMicrophone() const
697 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
698 "TransmitMixer::IsPlayingFileAsMicrophone()");
702 int TransmitMixer::ScaleFileAsMicrophonePlayout(float scale)
704 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
705 "TransmitMixer::ScaleFileAsMicrophonePlayout(scale=%5.3f)",
708 CriticalSectionScoped cs(&_critSect);
712 _engineStatisticsPtr->SetLastError(
713 VE_INVALID_OPERATION, kTraceError,
714 "ScaleFileAsMicrophonePlayout() isnot playing file");
718 if ((_filePlayerPtr == NULL) ||
719 (_filePlayerPtr->SetAudioScaling(scale) != 0))
721 _engineStatisticsPtr->SetLastError(
722 VE_BAD_ARGUMENT, kTraceError,
723 "SetAudioScaling() failed to scale playout");
730 int TransmitMixer::StartRecordingMicrophone(const char* fileName,
731 const CodecInst* codecInst)
733 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
734 "TransmitMixer::StartRecordingMicrophone(fileName=%s)",
739 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
740 "StartRecordingMicrophone() is already recording");
745 const uint32_t notificationTime(0); // Not supported in VoE
746 CodecInst dummyCodec = { 100, "L16", 16000, 320, 1, 320000 };
748 if (codecInst != NULL &&
749 (codecInst->channels < 0 || codecInst->channels > 2))
751 _engineStatisticsPtr->SetLastError(
752 VE_BAD_ARGUMENT, kTraceError,
753 "StartRecordingMicrophone() invalid compression");
756 if (codecInst == NULL)
758 format = kFileFormatPcm16kHzFile;
759 codecInst = &dummyCodec;
760 } else if ((STR_CASE_CMP(codecInst->plname,"L16") == 0) ||
761 (STR_CASE_CMP(codecInst->plname,"PCMU") == 0) ||
762 (STR_CASE_CMP(codecInst->plname,"PCMA") == 0))
764 format = kFileFormatWavFile;
767 format = kFileFormatCompressedFile;
770 CriticalSectionScoped cs(&_critSect);
772 // Destroy the old instance
773 if (_fileRecorderPtr)
775 _fileRecorderPtr->RegisterModuleFileCallback(NULL);
776 FileRecorder::DestroyFileRecorder(_fileRecorderPtr);
777 _fileRecorderPtr = NULL;
781 FileRecorder::CreateFileRecorder(_fileRecorderId,
782 (const FileFormats) format);
783 if (_fileRecorderPtr == NULL)
785 _engineStatisticsPtr->SetLastError(
786 VE_INVALID_ARGUMENT, kTraceError,
787 "StartRecordingMicrophone() fileRecorder format isnot correct");
791 if (_fileRecorderPtr->StartRecordingAudioFile(
793 (const CodecInst&) *codecInst,
794 notificationTime) != 0)
796 _engineStatisticsPtr->SetLastError(
797 VE_BAD_FILE, kTraceError,
798 "StartRecordingAudioFile() failed to start file recording");
799 _fileRecorderPtr->StopRecording();
800 FileRecorder::DestroyFileRecorder(_fileRecorderPtr);
801 _fileRecorderPtr = NULL;
804 _fileRecorderPtr->RegisterModuleFileCallback(this);
805 _fileRecording = true;
810 int TransmitMixer::StartRecordingMicrophone(OutStream* stream,
811 const CodecInst* codecInst)
813 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
814 "TransmitMixer::StartRecordingMicrophone()");
818 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
819 "StartRecordingMicrophone() is already recording");
824 const uint32_t notificationTime(0); // Not supported in VoE
825 CodecInst dummyCodec = { 100, "L16", 16000, 320, 1, 320000 };
827 if (codecInst != NULL && codecInst->channels != 1)
829 _engineStatisticsPtr->SetLastError(
830 VE_BAD_ARGUMENT, kTraceError,
831 "StartRecordingMicrophone() invalid compression");
834 if (codecInst == NULL)
836 format = kFileFormatPcm16kHzFile;
837 codecInst = &dummyCodec;
838 } else if ((STR_CASE_CMP(codecInst->plname,"L16") == 0) ||
839 (STR_CASE_CMP(codecInst->plname,"PCMU") == 0) ||
840 (STR_CASE_CMP(codecInst->plname,"PCMA") == 0))
842 format = kFileFormatWavFile;
845 format = kFileFormatCompressedFile;
848 CriticalSectionScoped cs(&_critSect);
850 // Destroy the old instance
851 if (_fileRecorderPtr)
853 _fileRecorderPtr->RegisterModuleFileCallback(NULL);
854 FileRecorder::DestroyFileRecorder(_fileRecorderPtr);
855 _fileRecorderPtr = NULL;
859 FileRecorder::CreateFileRecorder(_fileRecorderId,
860 (const FileFormats) format);
861 if (_fileRecorderPtr == NULL)
863 _engineStatisticsPtr->SetLastError(
864 VE_INVALID_ARGUMENT, kTraceError,
865 "StartRecordingMicrophone() fileRecorder format isnot correct");
869 if (_fileRecorderPtr->StartRecordingAudioFile(*stream,
871 notificationTime) != 0)
873 _engineStatisticsPtr->SetLastError(VE_BAD_FILE, kTraceError,
874 "StartRecordingAudioFile() failed to start file recording");
875 _fileRecorderPtr->StopRecording();
876 FileRecorder::DestroyFileRecorder(_fileRecorderPtr);
877 _fileRecorderPtr = NULL;
881 _fileRecorderPtr->RegisterModuleFileCallback(this);
882 _fileRecording = true;
888 int TransmitMixer::StopRecordingMicrophone()
890 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
891 "TransmitMixer::StopRecordingMicrophone()");
895 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
896 "StopRecordingMicrophone() isnot recording");
900 CriticalSectionScoped cs(&_critSect);
902 if (_fileRecorderPtr->StopRecording() != 0)
904 _engineStatisticsPtr->SetLastError(
905 VE_STOP_RECORDING_FAILED, kTraceError,
906 "StopRecording(), could not stop recording");
909 _fileRecorderPtr->RegisterModuleFileCallback(NULL);
910 FileRecorder::DestroyFileRecorder(_fileRecorderPtr);
911 _fileRecorderPtr = NULL;
912 _fileRecording = false;
917 int TransmitMixer::StartRecordingCall(const char* fileName,
918 const CodecInst* codecInst)
920 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
921 "TransmitMixer::StartRecordingCall(fileName=%s)", fileName);
923 if (_fileCallRecording)
925 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
926 "StartRecordingCall() is already recording");
931 const uint32_t notificationTime(0); // Not supported in VoE
932 CodecInst dummyCodec = { 100, "L16", 16000, 320, 1, 320000 };
934 if (codecInst != NULL && codecInst->channels != 1)
936 _engineStatisticsPtr->SetLastError(
937 VE_BAD_ARGUMENT, kTraceError,
938 "StartRecordingCall() invalid compression");
941 if (codecInst == NULL)
943 format = kFileFormatPcm16kHzFile;
944 codecInst = &dummyCodec;
945 } else if ((STR_CASE_CMP(codecInst->plname,"L16") == 0) ||
946 (STR_CASE_CMP(codecInst->plname,"PCMU") == 0) ||
947 (STR_CASE_CMP(codecInst->plname,"PCMA") == 0))
949 format = kFileFormatWavFile;
952 format = kFileFormatCompressedFile;
955 CriticalSectionScoped cs(&_critSect);
957 // Destroy the old instance
958 if (_fileCallRecorderPtr)
960 _fileCallRecorderPtr->RegisterModuleFileCallback(NULL);
961 FileRecorder::DestroyFileRecorder(_fileCallRecorderPtr);
962 _fileCallRecorderPtr = NULL;
966 = FileRecorder::CreateFileRecorder(_fileCallRecorderId,
967 (const FileFormats) format);
968 if (_fileCallRecorderPtr == NULL)
970 _engineStatisticsPtr->SetLastError(
971 VE_INVALID_ARGUMENT, kTraceError,
972 "StartRecordingCall() fileRecorder format isnot correct");
976 if (_fileCallRecorderPtr->StartRecordingAudioFile(
978 (const CodecInst&) *codecInst,
979 notificationTime) != 0)
981 _engineStatisticsPtr->SetLastError(
982 VE_BAD_FILE, kTraceError,
983 "StartRecordingAudioFile() failed to start file recording");
984 _fileCallRecorderPtr->StopRecording();
985 FileRecorder::DestroyFileRecorder(_fileCallRecorderPtr);
986 _fileCallRecorderPtr = NULL;
989 _fileCallRecorderPtr->RegisterModuleFileCallback(this);
990 _fileCallRecording = true;
995 int TransmitMixer::StartRecordingCall(OutStream* stream,
996 const CodecInst* codecInst)
998 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
999 "TransmitMixer::StartRecordingCall()");
1001 if (_fileCallRecording)
1003 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
1004 "StartRecordingCall() is already recording");
1009 const uint32_t notificationTime(0); // Not supported in VoE
1010 CodecInst dummyCodec = { 100, "L16", 16000, 320, 1, 320000 };
1012 if (codecInst != NULL && codecInst->channels != 1)
1014 _engineStatisticsPtr->SetLastError(
1015 VE_BAD_ARGUMENT, kTraceError,
1016 "StartRecordingCall() invalid compression");
1019 if (codecInst == NULL)
1021 format = kFileFormatPcm16kHzFile;
1022 codecInst = &dummyCodec;
1023 } else if ((STR_CASE_CMP(codecInst->plname,"L16") == 0) ||
1024 (STR_CASE_CMP(codecInst->plname,"PCMU") == 0) ||
1025 (STR_CASE_CMP(codecInst->plname,"PCMA") == 0))
1027 format = kFileFormatWavFile;
1030 format = kFileFormatCompressedFile;
1033 CriticalSectionScoped cs(&_critSect);
1035 // Destroy the old instance
1036 if (_fileCallRecorderPtr)
1038 _fileCallRecorderPtr->RegisterModuleFileCallback(NULL);
1039 FileRecorder::DestroyFileRecorder(_fileCallRecorderPtr);
1040 _fileCallRecorderPtr = NULL;
1043 _fileCallRecorderPtr =
1044 FileRecorder::CreateFileRecorder(_fileCallRecorderId,
1045 (const FileFormats) format);
1046 if (_fileCallRecorderPtr == NULL)
1048 _engineStatisticsPtr->SetLastError(
1049 VE_INVALID_ARGUMENT, kTraceError,
1050 "StartRecordingCall() fileRecorder format isnot correct");
1054 if (_fileCallRecorderPtr->StartRecordingAudioFile(*stream,
1056 notificationTime) != 0)
1058 _engineStatisticsPtr->SetLastError(VE_BAD_FILE, kTraceError,
1059 "StartRecordingAudioFile() failed to start file recording");
1060 _fileCallRecorderPtr->StopRecording();
1061 FileRecorder::DestroyFileRecorder(_fileCallRecorderPtr);
1062 _fileCallRecorderPtr = NULL;
1066 _fileCallRecorderPtr->RegisterModuleFileCallback(this);
1067 _fileCallRecording = true;
1072 int TransmitMixer::StopRecordingCall()
1074 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
1075 "TransmitMixer::StopRecordingCall()");
1077 if (!_fileCallRecording)
1079 WEBRTC_TRACE(kTraceError, kTraceVoice, VoEId(_instanceId, -1),
1080 "StopRecordingCall() file isnot recording");
1084 CriticalSectionScoped cs(&_critSect);
1086 if (_fileCallRecorderPtr->StopRecording() != 0)
1088 _engineStatisticsPtr->SetLastError(
1089 VE_STOP_RECORDING_FAILED, kTraceError,
1090 "StopRecording(), could not stop recording");
1094 _fileCallRecorderPtr->RegisterModuleFileCallback(NULL);
1095 FileRecorder::DestroyFileRecorder(_fileCallRecorderPtr);
1096 _fileCallRecorderPtr = NULL;
1097 _fileCallRecording = false;
1103 TransmitMixer::SetMixWithMicStatus(bool mix)
1105 _mixFileWithMicrophone = mix;
1108 int TransmitMixer::RegisterExternalMediaProcessing(
1109 VoEMediaProcess* object,
1110 ProcessingTypes type) {
1111 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
1112 "TransmitMixer::RegisterExternalMediaProcessing()");
1114 CriticalSectionScoped cs(&_callbackCritSect);
1119 // Store the callback object according to the processing type.
1120 if (type == kRecordingAllChannelsMixed) {
1121 external_postproc_ptr_ = object;
1122 } else if (type == kRecordingPreprocessing) {
1123 external_preproc_ptr_ = object;
1130 int TransmitMixer::DeRegisterExternalMediaProcessing(ProcessingTypes type) {
1131 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
1132 "TransmitMixer::DeRegisterExternalMediaProcessing()");
1134 CriticalSectionScoped cs(&_callbackCritSect);
1135 if (type == kRecordingAllChannelsMixed) {
1136 external_postproc_ptr_ = NULL;
1137 } else if (type == kRecordingPreprocessing) {
1138 external_preproc_ptr_ = NULL;
1146 TransmitMixer::SetMute(bool enable)
1148 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
1149 "TransmitMixer::SetMute(enable=%d)", enable);
1155 TransmitMixer::Mute() const
1160 int8_t TransmitMixer::AudioLevel() const
1162 // Speech + file level [0,9]
1163 return _audioLevel.Level();
1166 int16_t TransmitMixer::AudioLevelFullRange() const
1168 // Speech + file level [0,32767]
1169 return _audioLevel.LevelFullRange();
1172 bool TransmitMixer::IsRecordingCall()
1174 return _fileCallRecording;
1177 bool TransmitMixer::IsRecordingMic()
1180 return _fileRecording;
1183 // TODO(andrew): use RemixAndResample for this.
1184 int TransmitMixer::GenerateAudioFrame(const int16_t audio[],
1185 int samples_per_channel,
1187 int sample_rate_hz) {
1188 int destination_rate;
1189 int num_codec_channels;
1190 GetSendCodecInfo(&destination_rate, &num_codec_channels);
1192 // Never upsample the capture signal here. This should be done at the
1193 // end of the send chain.
1194 destination_rate = std::min(destination_rate, sample_rate_hz);
1195 stereo_codec_ = num_codec_channels == 2;
1197 const int16_t* audio_ptr = audio;
1198 int16_t mono_audio[kMaxMonoDeviceDataSizeSamples];
1199 assert(samples_per_channel <= kMaxMonoDeviceDataSizeSamples);
1200 // If no stereo codecs are in use, we downmix a stereo stream from the
1201 // device early in the chain, before resampling.
1202 if (num_channels == 2 && !stereo_codec_) {
1203 AudioFrameOperations::StereoToMono(audio, samples_per_channel,
1205 audio_ptr = mono_audio;
1209 if (resampler_.InitializeIfNeeded(sample_rate_hz,
1211 num_channels) != 0) {
1212 WEBRTC_TRACE(kTraceError, kTraceVoice, VoEId(_instanceId, -1),
1213 "TransmitMixer::GenerateAudioFrame() unable to resample");
1217 int out_length = resampler_.Resample(audio_ptr,
1218 samples_per_channel * num_channels,
1220 AudioFrame::kMaxDataSizeSamples);
1221 if (out_length == -1) {
1222 WEBRTC_TRACE(kTraceError, kTraceVoice, VoEId(_instanceId, -1),
1223 "TransmitMixer::GenerateAudioFrame() resampling failed");
1227 _audioFrame.samples_per_channel_ = out_length / num_channels;
1228 _audioFrame.id_ = _instanceId;
1229 _audioFrame.timestamp_ = -1;
1230 _audioFrame.sample_rate_hz_ = destination_rate;
1231 _audioFrame.speech_type_ = AudioFrame::kNormalSpeech;
1232 _audioFrame.vad_activity_ = AudioFrame::kVadUnknown;
1233 _audioFrame.num_channels_ = num_channels;
1238 int32_t TransmitMixer::RecordAudioToFile(
1239 uint32_t mixingFrequency)
1241 CriticalSectionScoped cs(&_critSect);
1242 if (_fileRecorderPtr == NULL)
1244 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
1245 "TransmitMixer::RecordAudioToFile() filerecorder doesnot"
1250 if (_fileRecorderPtr->RecordAudioToFile(_audioFrame) != 0)
1252 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
1253 "TransmitMixer::RecordAudioToFile() file recording"
1261 int32_t TransmitMixer::MixOrReplaceAudioWithFile(
1262 int mixingFrequency)
1264 scoped_array<int16_t> fileBuffer(new int16_t[640]);
1268 CriticalSectionScoped cs(&_critSect);
1269 if (_filePlayerPtr == NULL)
1271 WEBRTC_TRACE(kTraceWarning, kTraceVoice,
1272 VoEId(_instanceId, -1),
1273 "TransmitMixer::MixOrReplaceAudioWithFile()"
1274 "fileplayer doesnot exist");
1278 if (_filePlayerPtr->Get10msAudioFromFile(fileBuffer.get(),
1280 mixingFrequency) == -1)
1282 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
1283 "TransmitMixer::MixOrReplaceAudioWithFile() file"
1289 assert(_audioFrame.samples_per_channel_ == fileSamples);
1291 if (_mixFileWithMicrophone)
1293 // Currently file stream is always mono.
1294 // TODO(xians): Change the code when FilePlayer supports real stereo.
1295 Utility::MixWithSat(_audioFrame.data_,
1296 _audioFrame.num_channels_,
1302 // Replace ACM audio with file.
1303 // Currently file stream is always mono.
1304 // TODO(xians): Change the code when FilePlayer supports real stereo.
1305 _audioFrame.UpdateFrame(-1,
1310 AudioFrame::kNormalSpeech,
1311 AudioFrame::kVadUnknown,
1317 void TransmitMixer::ProcessAudio(int delay_ms, int clock_drift,
1318 int current_mic_level) {
1319 if (audioproc_->set_stream_delay_ms(delay_ms) != 0) {
1320 // A redundant warning is reported in AudioDevice, which we've throttled
1321 // to avoid flooding the logs. Relegate this one to LS_VERBOSE to avoid
1322 // repeating the problem here.
1323 LOG_FERR1(LS_VERBOSE, set_stream_delay_ms, delay_ms);
1326 GainControl* agc = audioproc_->gain_control();
1327 if (agc->set_stream_analog_level(current_mic_level) != 0) {
1328 LOG_FERR1(LS_ERROR, set_stream_analog_level, current_mic_level);
1332 EchoCancellation* aec = audioproc_->echo_cancellation();
1333 if (aec->is_drift_compensation_enabled()) {
1334 aec->set_stream_drift_samples(clock_drift);
1337 int err = audioproc_->ProcessStream(&_audioFrame);
1339 LOG(LS_ERROR) << "ProcessStream() error: " << err;
1343 // Store new capture level. Only updated when analog AGC is enabled.
1344 _captureLevel = agc->stream_analog_level();
1346 CriticalSectionScoped cs(&_critSect);
1347 // Triggers a callback in OnPeriodicProcess().
1348 _saturationWarning |= agc->stream_is_saturated();
1351 #ifdef WEBRTC_VOICE_ENGINE_TYPING_DETECTION
1352 int TransmitMixer::TypingDetection(bool keyPressed)
1355 // We let the VAD determine if we're using this feature or not.
1356 if (_audioFrame.vad_activity_ == AudioFrame::kVadUnknown)
1361 if (_audioFrame.vad_activity_ == AudioFrame::kVadActive)
1366 // Keep track if time since last typing event
1369 _timeSinceLastTyping = 0;
1373 ++_timeSinceLastTyping;
1376 if ((_timeSinceLastTyping < _typeEventDelay)
1377 && (_audioFrame.vad_activity_ == AudioFrame::kVadActive)
1378 && (_timeActive < _timeWindow))
1380 _penaltyCounter += _costPerTyping;
1381 if (_penaltyCounter > _reportingThreshold)
1383 // Triggers a callback in OnPeriodicProcess().
1384 _typingNoiseWarningPending = true;
1385 _typingNoiseDetected = true;
1389 // If there is already a warning pending, do not change the state.
1390 // Otherwise sets a warning pending if noise is off now but previously on.
1391 if (!_typingNoiseWarningPending && _typingNoiseDetected) {
1392 // Triggers a callback in OnPeriodicProcess().
1393 _typingNoiseWarningPending = true;
1394 _typingNoiseDetected = false;
1397 if (_penaltyCounter > 0)
1398 _penaltyCounter-=_penaltyDecay;
1404 int TransmitMixer::GetMixingFrequency()
1406 assert(_audioFrame.sample_rate_hz_ != 0);
1407 return _audioFrame.sample_rate_hz_;
1410 #ifdef WEBRTC_VOICE_ENGINE_TYPING_DETECTION
1411 int TransmitMixer::TimeSinceLastTyping(int &seconds)
1413 // We check in VoEAudioProcessingImpl that this is only called when
1414 // typing detection is active.
1416 // Round to whole seconds
1417 seconds = (_timeSinceLastTyping + 50) / 100;
1422 #ifdef WEBRTC_VOICE_ENGINE_TYPING_DETECTION
1423 int TransmitMixer::SetTypingDetectionParameters(int timeWindow,
1425 int reportingThreshold,
1430 _timeWindow = timeWindow;
1431 if(costPerTyping != 0)
1432 _costPerTyping = costPerTyping;
1433 if(reportingThreshold != 0)
1434 _reportingThreshold = reportingThreshold;
1435 if(penaltyDecay != 0)
1436 _penaltyDecay = penaltyDecay;
1437 if(typeEventDelay != 0)
1438 _typeEventDelay = typeEventDelay;
1445 void TransmitMixer::EnableStereoChannelSwapping(bool enable) {
1446 swap_stereo_channels_ = enable;
1449 bool TransmitMixer::IsStereoChannelSwappingEnabled() {
1450 return swap_stereo_channels_;
1455 } // namespace webrtc