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))
30 // TODO(ajm): The thread safety of this is dubious...
32 TransmitMixer::OnPeriodicProcess()
34 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, -1),
35 "TransmitMixer::OnPeriodicProcess()");
37 #if defined(WEBRTC_VOICE_ENGINE_TYPING_DETECTION)
38 if (_typingNoiseWarningPending)
40 CriticalSectionScoped cs(&_callbackCritSect);
41 if (_voiceEngineObserverPtr)
43 if (_typingNoiseDetected) {
44 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
45 "TransmitMixer::OnPeriodicProcess() => "
46 "CallbackOnError(VE_TYPING_NOISE_WARNING)");
47 _voiceEngineObserverPtr->CallbackOnError(
49 VE_TYPING_NOISE_WARNING);
51 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
52 "TransmitMixer::OnPeriodicProcess() => "
53 "CallbackOnError(VE_TYPING_NOISE_OFF_WARNING)");
54 _voiceEngineObserverPtr->CallbackOnError(
56 VE_TYPING_NOISE_OFF_WARNING);
59 _typingNoiseWarningPending = false;
63 bool saturationWarning = false;
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
68 CriticalSectionScoped cs(&_critSect);
69 saturationWarning = _saturationWarning;
70 if (_saturationWarning)
71 _saturationWarning = false;
74 if (saturationWarning)
76 CriticalSectionScoped cs(&_callbackCritSect);
77 if (_voiceEngineObserverPtr)
79 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
80 "TransmitMixer::OnPeriodicProcess() =>"
81 " CallbackOnError(VE_SATURATION_WARNING)");
82 _voiceEngineObserverPtr->CallbackOnError(-1, VE_SATURATION_WARNING);
88 void TransmitMixer::PlayNotification(int32_t id,
91 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, -1),
92 "TransmitMixer::PlayNotification(id=%d, durationMs=%d)",
98 void TransmitMixer::RecordNotification(int32_t id,
101 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
102 "TransmitMixer::RecordNotification(id=%d, durationMs=%d)",
108 void TransmitMixer::PlayFileEnded(int32_t id)
110 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, -1),
111 "TransmitMixer::PlayFileEnded(id=%d)", id);
113 assert(id == _filePlayerId);
115 CriticalSectionScoped cs(&_critSect);
117 _filePlaying = false;
118 WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId, -1),
119 "TransmitMixer::PlayFileEnded() =>"
120 "file player module is shutdown");
124 TransmitMixer::RecordFileEnded(int32_t id)
126 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, -1),
127 "TransmitMixer::RecordFileEnded(id=%d)", id);
129 if (id == _fileRecorderId)
131 CriticalSectionScoped cs(&_critSect);
132 _fileRecording = false;
133 WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId, -1),
134 "TransmitMixer::RecordFileEnded() => fileRecorder module"
136 } else if (id == _fileCallRecorderId)
138 CriticalSectionScoped cs(&_critSect);
139 _fileCallRecording = false;
140 WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId, -1),
141 "TransmitMixer::RecordFileEnded() => fileCallRecorder"
142 "module is shutdown");
147 TransmitMixer::Create(TransmitMixer*& mixer, uint32_t instanceId)
149 WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(instanceId, -1),
150 "TransmitMixer::Create(instanceId=%d)", instanceId);
151 mixer = new TransmitMixer(instanceId);
154 WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(instanceId, -1),
155 "TransmitMixer::Create() unable to allocate memory"
163 TransmitMixer::Destroy(TransmitMixer*& mixer)
172 TransmitMixer::TransmitMixer(uint32_t instanceId) :
173 _engineStatisticsPtr(NULL),
174 _channelManagerPtr(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),
187 _fileRecording(false),
188 _fileCallRecording(false),
190 _critSect(*CriticalSectionWrapper::CreateCriticalSection()),
191 _callbackCritSect(*CriticalSectionWrapper::CreateCriticalSection()),
192 #ifdef WEBRTC_VOICE_ENGINE_TYPING_DETECTION
193 _typingNoiseWarningPending(false),
194 _typingNoiseDetected(false),
196 _saturationWarning(false),
197 _instanceId(instanceId),
198 _mixFileWithMicrophone(false),
200 external_postproc_ptr_(NULL),
201 external_preproc_ptr_(NULL),
203 _remainingMuteMicTimeMs(0),
204 stereo_codec_(false),
205 swap_stereo_channels_(false)
207 WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId, -1),
208 "TransmitMixer::TransmitMixer() - ctor");
211 TransmitMixer::~TransmitMixer()
213 WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId, -1),
214 "TransmitMixer::~TransmitMixer() - dtor");
215 _monitorModule.DeRegisterObserver();
216 if (_processThreadPtr)
218 _processThreadPtr->DeRegisterModule(&_monitorModule);
220 DeRegisterExternalMediaProcessing(kRecordingAllChannelsMixed);
221 DeRegisterExternalMediaProcessing(kRecordingPreprocessing);
223 CriticalSectionScoped cs(&_critSect);
224 if (_fileRecorderPtr)
226 _fileRecorderPtr->RegisterModuleFileCallback(NULL);
227 _fileRecorderPtr->StopRecording();
228 FileRecorder::DestroyFileRecorder(_fileRecorderPtr);
229 _fileRecorderPtr = NULL;
231 if (_fileCallRecorderPtr)
233 _fileCallRecorderPtr->RegisterModuleFileCallback(NULL);
234 _fileCallRecorderPtr->StopRecording();
235 FileRecorder::DestroyFileRecorder(_fileCallRecorderPtr);
236 _fileCallRecorderPtr = NULL;
240 _filePlayerPtr->RegisterModuleFileCallback(NULL);
241 _filePlayerPtr->StopPlayingFile();
242 FilePlayer::DestroyFilePlayer(_filePlayerPtr);
243 _filePlayerPtr = NULL;
247 delete &_callbackCritSect;
251 TransmitMixer::SetEngineInformation(ProcessThread& processThread,
252 Statistics& engineStatistics,
253 ChannelManager& channelManager)
255 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
256 "TransmitMixer::SetEngineInformation()");
258 _processThreadPtr = &processThread;
259 _engineStatisticsPtr = &engineStatistics;
260 _channelManagerPtr = &channelManager;
262 if (_processThreadPtr->RegisterModule(&_monitorModule) == -1)
264 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
265 "TransmitMixer::SetEngineInformation() failed to"
266 "register the monitor module");
269 _monitorModule.RegisterObserver(*this);
276 TransmitMixer::RegisterVoiceEngineObserver(VoiceEngineObserver& observer)
278 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
279 "TransmitMixer::RegisterVoiceEngineObserver()");
280 CriticalSectionScoped cs(&_callbackCritSect);
282 if (_voiceEngineObserverPtr)
284 _engineStatisticsPtr->SetLastError(
285 VE_INVALID_OPERATION, kTraceError,
286 "RegisterVoiceEngineObserver() observer already enabled");
289 _voiceEngineObserverPtr = &observer;
294 TransmitMixer::SetAudioProcessingModule(AudioProcessing* audioProcessingModule)
296 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
297 "TransmitMixer::SetAudioProcessingModule("
298 "audioProcessingModule=0x%x)",
299 audioProcessingModule);
300 audioproc_ = audioProcessingModule;
304 void TransmitMixer::GetSendCodecInfo(int* max_sample_rate, int* max_channels) {
305 *max_sample_rate = 8000;
307 for (ChannelManager::Iterator it(_channelManagerPtr); it.IsValid();
309 Channel* channel = it.GetChannel();
310 if (channel->Sending()) {
312 channel->GetSendCodec(codec);
313 *max_sample_rate = std::max(*max_sample_rate, codec.plfreq);
314 *max_channels = std::max(*max_channels, codec.channels);
320 TransmitMixer::PrepareDemux(const void* audioSamples,
323 uint32_t samplesPerSec,
324 uint16_t totalDelayMS,
326 uint16_t currentMicLevel,
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);
335 // --- Resample input audio and create/store the initial audio frame
336 GenerateAudioFrame(static_cast<const int16_t*>(audioSamples),
342 CriticalSectionScoped cs(&_callbackCritSect);
343 if (external_preproc_ptr_) {
344 external_preproc_ptr_->Process(-1, kRecordingPreprocessing,
346 _audioFrame.samples_per_channel_,
347 _audioFrame.sample_rate_hz_,
348 _audioFrame.num_channels_ == 2);
352 // --- Near-end audio processing.
353 ProcessAudio(totalDelayMS, clockDrift, currentMicLevel, keyPressed);
355 if (swap_stereo_channels_ && stereo_codec_)
356 // Only bother swapping if we're using a stereo codec.
357 AudioFrameOperations::SwapStereoChannels(&_audioFrame);
359 // --- Annoying typing detection (utilizes the APM/VAD decision)
360 #ifdef WEBRTC_VOICE_ENGINE_TYPING_DETECTION
361 TypingDetection(keyPressed);
364 // --- Mute during DTMF tone if direct feedback is enabled
365 if (_remainingMuteMicTimeMs > 0)
367 AudioFrameOperations::Mute(_audioFrame);
368 _remainingMuteMicTimeMs -= 10;
369 if (_remainingMuteMicTimeMs < 0)
371 _remainingMuteMicTimeMs = 0;
378 AudioFrameOperations::Mute(_audioFrame);
381 // --- Mix with file (does not affect the mixing frequency)
384 MixOrReplaceAudioWithFile(_audioFrame.sample_rate_hz_);
387 // --- Record to file
388 bool file_recording = false;
390 CriticalSectionScoped cs(&_critSect);
391 file_recording = _fileRecording;
395 RecordAudioToFile(_audioFrame.sample_rate_hz_);
399 CriticalSectionScoped cs(&_callbackCritSect);
400 if (external_postproc_ptr_) {
401 external_postproc_ptr_->Process(-1, kRecordingAllChannelsMixed,
403 _audioFrame.samples_per_channel_,
404 _audioFrame.sample_rate_hz_,
405 _audioFrame.num_channels_ == 2);
409 // --- Measure audio level of speech after all processing.
410 _audioLevel.ComputeLevel(_audioFrame);
415 TransmitMixer::DemuxAndMix()
417 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, -1),
418 "TransmitMixer::DemuxAndMix()");
420 for (ChannelManager::Iterator it(_channelManagerPtr); it.IsValid();
423 Channel* channelPtr = it.GetChannel();
424 if (channelPtr->Sending())
426 // Demultiplex makes a copy of its input.
427 channelPtr->Demultiplex(_audioFrame);
428 channelPtr->PrepareEncodeAndSend(_audioFrame.sample_rate_hz_);
434 void 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();
440 if (channel_ptr->Sending()) {
441 // Demultiplex makes a copy of its input.
442 channel_ptr->Demultiplex(_audioFrame);
443 channel_ptr->PrepareEncodeAndSend(_audioFrame.sample_rate_hz_);
450 TransmitMixer::EncodeAndSend()
452 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, -1),
453 "TransmitMixer::EncodeAndSend()");
455 for (ChannelManager::Iterator it(_channelManagerPtr); it.IsValid();
458 Channel* channelPtr = it.GetChannel();
459 if (channelPtr->Sending())
461 channelPtr->EncodeAndSend();
467 void 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();
472 if (channel_ptr && channel_ptr->Sending())
473 channel_ptr->EncodeAndSend();
477 uint32_t TransmitMixer::CaptureLevel() const
479 return _captureLevel;
483 TransmitMixer::UpdateMuteMicrophoneTime(uint32_t lengthMs)
485 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
486 "TransmitMixer::UpdateMuteMicrophoneTime(lengthMs=%d)",
488 _remainingMuteMicTimeMs = lengthMs;
492 TransmitMixer::StopSend()
494 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
495 "TransmitMixer::StopSend()");
500 int TransmitMixer::StartPlayingFileAsMicrophone(const char* fileName,
506 const CodecInst* codecInst)
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);
516 _engineStatisticsPtr->SetLastError(
517 VE_ALREADY_PLAYING, kTraceWarning,
518 "StartPlayingFileAsMicrophone() is already playing");
522 CriticalSectionScoped cs(&_critSect);
524 // Destroy the old instance
527 _filePlayerPtr->RegisterModuleFileCallback(NULL);
528 FilePlayer::DestroyFilePlayer(_filePlayerPtr);
529 _filePlayerPtr = NULL;
532 // Dynamically create the instance
534 = FilePlayer::CreateFilePlayer(_filePlayerId,
535 (const FileFormats) format);
537 if (_filePlayerPtr == NULL)
539 _engineStatisticsPtr->SetLastError(
540 VE_INVALID_ARGUMENT, kTraceError,
541 "StartPlayingFileAsMicrophone() filePlayer format isnot correct");
545 const uint32_t notificationTime(0);
547 if (_filePlayerPtr->StartPlayingFile(
554 (const CodecInst*) codecInst) != 0)
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;
565 _filePlayerPtr->RegisterModuleFileCallback(this);
571 int TransmitMixer::StartPlayingFileAsMicrophone(InStream* stream,
576 const CodecInst* codecInst)
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);
585 _engineStatisticsPtr->SetLastError(
586 VE_BAD_FILE, kTraceError,
587 "StartPlayingFileAsMicrophone() NULL as input stream");
593 _engineStatisticsPtr->SetLastError(
594 VE_ALREADY_PLAYING, kTraceWarning,
595 "StartPlayingFileAsMicrophone() is already playing");
599 CriticalSectionScoped cs(&_critSect);
601 // Destroy the old instance
604 _filePlayerPtr->RegisterModuleFileCallback(NULL);
605 FilePlayer::DestroyFilePlayer(_filePlayerPtr);
606 _filePlayerPtr = NULL;
609 // Dynamically create the instance
611 = FilePlayer::CreateFilePlayer(_filePlayerId,
612 (const FileFormats) format);
614 if (_filePlayerPtr == NULL)
616 _engineStatisticsPtr->SetLastError(
617 VE_INVALID_ARGUMENT, kTraceWarning,
618 "StartPlayingFileAsMicrophone() filePlayer format isnot correct");
622 const uint32_t notificationTime(0);
624 if (_filePlayerPtr->StartPlayingFile(
630 (const CodecInst*) codecInst) != 0)
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;
640 _filePlayerPtr->RegisterModuleFileCallback(this);
646 int TransmitMixer::StopPlayingFileAsMicrophone()
648 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
649 "TransmitMixer::StopPlayingFileAsMicrophone()");
653 _engineStatisticsPtr->SetLastError(
654 VE_INVALID_OPERATION, kTraceWarning,
655 "StopPlayingFileAsMicrophone() isnot playing");
659 CriticalSectionScoped cs(&_critSect);
661 if (_filePlayerPtr->StopPlayingFile() != 0)
663 _engineStatisticsPtr->SetLastError(
664 VE_CANNOT_STOP_PLAYOUT, kTraceError,
665 "StopPlayingFile() couldnot stop playing file");
669 _filePlayerPtr->RegisterModuleFileCallback(NULL);
670 FilePlayer::DestroyFilePlayer(_filePlayerPtr);
671 _filePlayerPtr = NULL;
672 _filePlaying = false;
677 int TransmitMixer::IsPlayingFileAsMicrophone() const
679 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
680 "TransmitMixer::IsPlayingFileAsMicrophone()");
684 int TransmitMixer::StartRecordingMicrophone(const char* fileName,
685 const CodecInst* codecInst)
687 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
688 "TransmitMixer::StartRecordingMicrophone(fileName=%s)",
691 CriticalSectionScoped cs(&_critSect);
695 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
696 "StartRecordingMicrophone() is already recording");
701 const uint32_t notificationTime(0); // Not supported in VoE
702 CodecInst dummyCodec = { 100, "L16", 16000, 320, 1, 320000 };
704 if (codecInst != NULL &&
705 (codecInst->channels < 0 || codecInst->channels > 2))
707 _engineStatisticsPtr->SetLastError(
708 VE_BAD_ARGUMENT, kTraceError,
709 "StartRecordingMicrophone() invalid compression");
712 if (codecInst == NULL)
714 format = kFileFormatPcm16kHzFile;
715 codecInst = &dummyCodec;
716 } else if ((STR_CASE_CMP(codecInst->plname,"L16") == 0) ||
717 (STR_CASE_CMP(codecInst->plname,"PCMU") == 0) ||
718 (STR_CASE_CMP(codecInst->plname,"PCMA") == 0))
720 format = kFileFormatWavFile;
723 format = kFileFormatCompressedFile;
726 // Destroy the old instance
727 if (_fileRecorderPtr)
729 _fileRecorderPtr->RegisterModuleFileCallback(NULL);
730 FileRecorder::DestroyFileRecorder(_fileRecorderPtr);
731 _fileRecorderPtr = NULL;
735 FileRecorder::CreateFileRecorder(_fileRecorderId,
736 (const FileFormats) format);
737 if (_fileRecorderPtr == NULL)
739 _engineStatisticsPtr->SetLastError(
740 VE_INVALID_ARGUMENT, kTraceError,
741 "StartRecordingMicrophone() fileRecorder format isnot correct");
745 if (_fileRecorderPtr->StartRecordingAudioFile(
747 (const CodecInst&) *codecInst,
748 notificationTime) != 0)
750 _engineStatisticsPtr->SetLastError(
751 VE_BAD_FILE, kTraceError,
752 "StartRecordingAudioFile() failed to start file recording");
753 _fileRecorderPtr->StopRecording();
754 FileRecorder::DestroyFileRecorder(_fileRecorderPtr);
755 _fileRecorderPtr = NULL;
758 _fileRecorderPtr->RegisterModuleFileCallback(this);
759 _fileRecording = true;
764 int TransmitMixer::StartRecordingMicrophone(OutStream* stream,
765 const CodecInst* codecInst)
767 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
768 "TransmitMixer::StartRecordingMicrophone()");
770 CriticalSectionScoped cs(&_critSect);
774 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
775 "StartRecordingMicrophone() is already recording");
780 const uint32_t notificationTime(0); // Not supported in VoE
781 CodecInst dummyCodec = { 100, "L16", 16000, 320, 1, 320000 };
783 if (codecInst != NULL && codecInst->channels != 1)
785 _engineStatisticsPtr->SetLastError(
786 VE_BAD_ARGUMENT, kTraceError,
787 "StartRecordingMicrophone() invalid compression");
790 if (codecInst == NULL)
792 format = kFileFormatPcm16kHzFile;
793 codecInst = &dummyCodec;
794 } else if ((STR_CASE_CMP(codecInst->plname,"L16") == 0) ||
795 (STR_CASE_CMP(codecInst->plname,"PCMU") == 0) ||
796 (STR_CASE_CMP(codecInst->plname,"PCMA") == 0))
798 format = kFileFormatWavFile;
801 format = kFileFormatCompressedFile;
804 // Destroy the old instance
805 if (_fileRecorderPtr)
807 _fileRecorderPtr->RegisterModuleFileCallback(NULL);
808 FileRecorder::DestroyFileRecorder(_fileRecorderPtr);
809 _fileRecorderPtr = NULL;
813 FileRecorder::CreateFileRecorder(_fileRecorderId,
814 (const FileFormats) format);
815 if (_fileRecorderPtr == NULL)
817 _engineStatisticsPtr->SetLastError(
818 VE_INVALID_ARGUMENT, kTraceError,
819 "StartRecordingMicrophone() fileRecorder format isnot correct");
823 if (_fileRecorderPtr->StartRecordingAudioFile(*stream,
825 notificationTime) != 0)
827 _engineStatisticsPtr->SetLastError(VE_BAD_FILE, kTraceError,
828 "StartRecordingAudioFile() failed to start file recording");
829 _fileRecorderPtr->StopRecording();
830 FileRecorder::DestroyFileRecorder(_fileRecorderPtr);
831 _fileRecorderPtr = NULL;
835 _fileRecorderPtr->RegisterModuleFileCallback(this);
836 _fileRecording = true;
842 int TransmitMixer::StopRecordingMicrophone()
844 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
845 "TransmitMixer::StopRecordingMicrophone()");
847 CriticalSectionScoped cs(&_critSect);
851 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
852 "StopRecordingMicrophone() isnot recording");
856 if (_fileRecorderPtr->StopRecording() != 0)
858 _engineStatisticsPtr->SetLastError(
859 VE_STOP_RECORDING_FAILED, kTraceError,
860 "StopRecording(), could not stop recording");
863 _fileRecorderPtr->RegisterModuleFileCallback(NULL);
864 FileRecorder::DestroyFileRecorder(_fileRecorderPtr);
865 _fileRecorderPtr = NULL;
866 _fileRecording = false;
871 int TransmitMixer::StartRecordingCall(const char* fileName,
872 const CodecInst* codecInst)
874 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
875 "TransmitMixer::StartRecordingCall(fileName=%s)", fileName);
877 if (_fileCallRecording)
879 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
880 "StartRecordingCall() is already recording");
885 const uint32_t notificationTime(0); // Not supported in VoE
886 CodecInst dummyCodec = { 100, "L16", 16000, 320, 1, 320000 };
888 if (codecInst != NULL && codecInst->channels != 1)
890 _engineStatisticsPtr->SetLastError(
891 VE_BAD_ARGUMENT, kTraceError,
892 "StartRecordingCall() invalid compression");
895 if (codecInst == NULL)
897 format = kFileFormatPcm16kHzFile;
898 codecInst = &dummyCodec;
899 } else if ((STR_CASE_CMP(codecInst->plname,"L16") == 0) ||
900 (STR_CASE_CMP(codecInst->plname,"PCMU") == 0) ||
901 (STR_CASE_CMP(codecInst->plname,"PCMA") == 0))
903 format = kFileFormatWavFile;
906 format = kFileFormatCompressedFile;
909 CriticalSectionScoped cs(&_critSect);
911 // Destroy the old instance
912 if (_fileCallRecorderPtr)
914 _fileCallRecorderPtr->RegisterModuleFileCallback(NULL);
915 FileRecorder::DestroyFileRecorder(_fileCallRecorderPtr);
916 _fileCallRecorderPtr = NULL;
920 = FileRecorder::CreateFileRecorder(_fileCallRecorderId,
921 (const FileFormats) format);
922 if (_fileCallRecorderPtr == NULL)
924 _engineStatisticsPtr->SetLastError(
925 VE_INVALID_ARGUMENT, kTraceError,
926 "StartRecordingCall() fileRecorder format isnot correct");
930 if (_fileCallRecorderPtr->StartRecordingAudioFile(
932 (const CodecInst&) *codecInst,
933 notificationTime) != 0)
935 _engineStatisticsPtr->SetLastError(
936 VE_BAD_FILE, kTraceError,
937 "StartRecordingAudioFile() failed to start file recording");
938 _fileCallRecorderPtr->StopRecording();
939 FileRecorder::DestroyFileRecorder(_fileCallRecorderPtr);
940 _fileCallRecorderPtr = NULL;
943 _fileCallRecorderPtr->RegisterModuleFileCallback(this);
944 _fileCallRecording = true;
949 int TransmitMixer::StartRecordingCall(OutStream* stream,
950 const CodecInst* codecInst)
952 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
953 "TransmitMixer::StartRecordingCall()");
955 if (_fileCallRecording)
957 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
958 "StartRecordingCall() is already recording");
963 const uint32_t notificationTime(0); // Not supported in VoE
964 CodecInst dummyCodec = { 100, "L16", 16000, 320, 1, 320000 };
966 if (codecInst != NULL && codecInst->channels != 1)
968 _engineStatisticsPtr->SetLastError(
969 VE_BAD_ARGUMENT, kTraceError,
970 "StartRecordingCall() invalid compression");
973 if (codecInst == NULL)
975 format = kFileFormatPcm16kHzFile;
976 codecInst = &dummyCodec;
977 } else if ((STR_CASE_CMP(codecInst->plname,"L16") == 0) ||
978 (STR_CASE_CMP(codecInst->plname,"PCMU") == 0) ||
979 (STR_CASE_CMP(codecInst->plname,"PCMA") == 0))
981 format = kFileFormatWavFile;
984 format = kFileFormatCompressedFile;
987 CriticalSectionScoped cs(&_critSect);
989 // Destroy the old instance
990 if (_fileCallRecorderPtr)
992 _fileCallRecorderPtr->RegisterModuleFileCallback(NULL);
993 FileRecorder::DestroyFileRecorder(_fileCallRecorderPtr);
994 _fileCallRecorderPtr = NULL;
997 _fileCallRecorderPtr =
998 FileRecorder::CreateFileRecorder(_fileCallRecorderId,
999 (const FileFormats) format);
1000 if (_fileCallRecorderPtr == NULL)
1002 _engineStatisticsPtr->SetLastError(
1003 VE_INVALID_ARGUMENT, kTraceError,
1004 "StartRecordingCall() fileRecorder format isnot correct");
1008 if (_fileCallRecorderPtr->StartRecordingAudioFile(*stream,
1010 notificationTime) != 0)
1012 _engineStatisticsPtr->SetLastError(VE_BAD_FILE, kTraceError,
1013 "StartRecordingAudioFile() failed to start file recording");
1014 _fileCallRecorderPtr->StopRecording();
1015 FileRecorder::DestroyFileRecorder(_fileCallRecorderPtr);
1016 _fileCallRecorderPtr = NULL;
1020 _fileCallRecorderPtr->RegisterModuleFileCallback(this);
1021 _fileCallRecording = true;
1026 int TransmitMixer::StopRecordingCall()
1028 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
1029 "TransmitMixer::StopRecordingCall()");
1031 if (!_fileCallRecording)
1033 WEBRTC_TRACE(kTraceError, kTraceVoice, VoEId(_instanceId, -1),
1034 "StopRecordingCall() file isnot recording");
1038 CriticalSectionScoped cs(&_critSect);
1040 if (_fileCallRecorderPtr->StopRecording() != 0)
1042 _engineStatisticsPtr->SetLastError(
1043 VE_STOP_RECORDING_FAILED, kTraceError,
1044 "StopRecording(), could not stop recording");
1048 _fileCallRecorderPtr->RegisterModuleFileCallback(NULL);
1049 FileRecorder::DestroyFileRecorder(_fileCallRecorderPtr);
1050 _fileCallRecorderPtr = NULL;
1051 _fileCallRecording = false;
1057 TransmitMixer::SetMixWithMicStatus(bool mix)
1059 _mixFileWithMicrophone = mix;
1062 int TransmitMixer::RegisterExternalMediaProcessing(
1063 VoEMediaProcess* object,
1064 ProcessingTypes type) {
1065 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
1066 "TransmitMixer::RegisterExternalMediaProcessing()");
1068 CriticalSectionScoped cs(&_callbackCritSect);
1073 // Store the callback object according to the processing type.
1074 if (type == kRecordingAllChannelsMixed) {
1075 external_postproc_ptr_ = object;
1076 } else if (type == kRecordingPreprocessing) {
1077 external_preproc_ptr_ = object;
1084 int TransmitMixer::DeRegisterExternalMediaProcessing(ProcessingTypes type) {
1085 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
1086 "TransmitMixer::DeRegisterExternalMediaProcessing()");
1088 CriticalSectionScoped cs(&_callbackCritSect);
1089 if (type == kRecordingAllChannelsMixed) {
1090 external_postproc_ptr_ = NULL;
1091 } else if (type == kRecordingPreprocessing) {
1092 external_preproc_ptr_ = NULL;
1100 TransmitMixer::SetMute(bool enable)
1102 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
1103 "TransmitMixer::SetMute(enable=%d)", enable);
1109 TransmitMixer::Mute() const
1114 int8_t TransmitMixer::AudioLevel() const
1116 // Speech + file level [0,9]
1117 return _audioLevel.Level();
1120 int16_t TransmitMixer::AudioLevelFullRange() const
1122 // Speech + file level [0,32767]
1123 return _audioLevel.LevelFullRange();
1126 bool TransmitMixer::IsRecordingCall()
1128 return _fileCallRecording;
1131 bool TransmitMixer::IsRecordingMic()
1133 CriticalSectionScoped cs(&_critSect);
1134 return _fileRecording;
1137 void TransmitMixer::GenerateAudioFrame(const int16_t* audio,
1138 int samples_per_channel,
1140 int sample_rate_hz) {
1142 int num_codec_channels;
1143 GetSendCodecInfo(&codec_rate, &num_codec_channels);
1144 // TODO(ajm): This currently restricts the sample rate to 32 kHz.
1145 // See: https://code.google.com/p/webrtc/issues/detail?id=3146
1146 // When 48 kHz is supported natively by AudioProcessing, this will have
1147 // to be changed to handle 44.1 kHz.
1148 int max_sample_rate_hz = kAudioProcMaxNativeSampleRateHz;
1149 if (audioproc_->echo_control_mobile()->is_enabled()) {
1150 // AECM only supports 8 and 16 kHz.
1151 max_sample_rate_hz = 16000;
1153 codec_rate = std::min(codec_rate, max_sample_rate_hz);
1154 stereo_codec_ = num_codec_channels == 2;
1156 if (!mono_buffer_.get()) {
1157 // Temporary space for DownConvertToCodecFormat.
1158 mono_buffer_.reset(new int16_t[kMaxMonoDataSizeSamples]);
1160 DownConvertToCodecFormat(audio,
1161 samples_per_channel,
1171 int32_t TransmitMixer::RecordAudioToFile(
1172 uint32_t mixingFrequency)
1174 CriticalSectionScoped cs(&_critSect);
1175 if (_fileRecorderPtr == NULL)
1177 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
1178 "TransmitMixer::RecordAudioToFile() filerecorder doesnot"
1183 if (_fileRecorderPtr->RecordAudioToFile(_audioFrame) != 0)
1185 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
1186 "TransmitMixer::RecordAudioToFile() file recording"
1194 int32_t TransmitMixer::MixOrReplaceAudioWithFile(
1195 int mixingFrequency)
1197 scoped_ptr<int16_t[]> fileBuffer(new int16_t[640]);
1201 CriticalSectionScoped cs(&_critSect);
1202 if (_filePlayerPtr == NULL)
1204 WEBRTC_TRACE(kTraceWarning, kTraceVoice,
1205 VoEId(_instanceId, -1),
1206 "TransmitMixer::MixOrReplaceAudioWithFile()"
1207 "fileplayer doesnot exist");
1211 if (_filePlayerPtr->Get10msAudioFromFile(fileBuffer.get(),
1213 mixingFrequency) == -1)
1215 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
1216 "TransmitMixer::MixOrReplaceAudioWithFile() file"
1222 assert(_audioFrame.samples_per_channel_ == fileSamples);
1224 if (_mixFileWithMicrophone)
1226 // Currently file stream is always mono.
1227 // TODO(xians): Change the code when FilePlayer supports real stereo.
1228 MixWithSat(_audioFrame.data_,
1229 _audioFrame.num_channels_,
1235 // Replace ACM audio with file.
1236 // Currently file stream is always mono.
1237 // TODO(xians): Change the code when FilePlayer supports real stereo.
1238 _audioFrame.UpdateFrame(-1,
1243 AudioFrame::kNormalSpeech,
1244 AudioFrame::kVadUnknown,
1250 void TransmitMixer::ProcessAudio(int delay_ms, int clock_drift,
1251 int current_mic_level, bool key_pressed) {
1252 if (audioproc_->set_stream_delay_ms(delay_ms) != 0) {
1253 // A redundant warning is reported in AudioDevice, which we've throttled
1254 // to avoid flooding the logs. Relegate this one to LS_VERBOSE to avoid
1255 // repeating the problem here.
1256 LOG_FERR1(LS_VERBOSE, set_stream_delay_ms, delay_ms);
1259 GainControl* agc = audioproc_->gain_control();
1260 if (agc->set_stream_analog_level(current_mic_level) != 0) {
1261 LOG_FERR1(LS_ERROR, set_stream_analog_level, current_mic_level);
1265 EchoCancellation* aec = audioproc_->echo_cancellation();
1266 if (aec->is_drift_compensation_enabled()) {
1267 aec->set_stream_drift_samples(clock_drift);
1270 audioproc_->set_stream_key_pressed(key_pressed);
1272 int err = audioproc_->ProcessStream(&_audioFrame);
1274 LOG(LS_ERROR) << "ProcessStream() error: " << err;
1278 // Store new capture level. Only updated when analog AGC is enabled.
1279 _captureLevel = agc->stream_analog_level();
1281 CriticalSectionScoped cs(&_critSect);
1282 // Triggers a callback in OnPeriodicProcess().
1283 _saturationWarning |= agc->stream_is_saturated();
1286 #ifdef WEBRTC_VOICE_ENGINE_TYPING_DETECTION
1287 void TransmitMixer::TypingDetection(bool keyPressed)
1289 // We let the VAD determine if we're using this feature or not.
1290 if (_audioFrame.vad_activity_ == AudioFrame::kVadUnknown) {
1294 bool vadActive = _audioFrame.vad_activity_ == AudioFrame::kVadActive;
1295 if (_typingDetection.Process(keyPressed, vadActive)) {
1296 _typingNoiseWarningPending = true;
1297 _typingNoiseDetected = true;
1299 // If there is already a warning pending, do not change the state.
1300 // Otherwise set a warning pending if last callback was for noise detected.
1301 if (!_typingNoiseWarningPending && _typingNoiseDetected) {
1302 _typingNoiseWarningPending = true;
1303 _typingNoiseDetected = false;
1309 int TransmitMixer::GetMixingFrequency()
1311 assert(_audioFrame.sample_rate_hz_ != 0);
1312 return _audioFrame.sample_rate_hz_;
1315 #ifdef WEBRTC_VOICE_ENGINE_TYPING_DETECTION
1316 int TransmitMixer::TimeSinceLastTyping(int &seconds)
1318 // We check in VoEAudioProcessingImpl that this is only called when
1319 // typing detection is active.
1320 seconds = _typingDetection.TimeSinceLastDetectionInSeconds();
1325 #ifdef WEBRTC_VOICE_ENGINE_TYPING_DETECTION
1326 int TransmitMixer::SetTypingDetectionParameters(int timeWindow,
1328 int reportingThreshold,
1332 _typingDetection.SetParameters(timeWindow,
1342 void TransmitMixer::EnableStereoChannelSwapping(bool enable) {
1343 swap_stereo_channels_ = enable;
1346 bool TransmitMixer::IsStereoChannelSwappingEnabled() {
1347 return swap_stereo_channels_;
1351 } // namespace webrtc