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(
55 VE_TYPING_NOISE_WARNING);
57 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
58 "TransmitMixer::OnPeriodicProcess() => "
59 "CallbackOnError(VE_TYPING_NOISE_OFF_WARNING)");
60 _voiceEngineObserverPtr->CallbackOnError(
62 VE_TYPING_NOISE_OFF_WARNING);
65 _typingNoiseWarningPending = false;
69 bool saturationWarning = false;
71 // Modify |_saturationWarning| under lock to avoid conflict with write op
72 // in ProcessAudio and also ensure that we don't hold the lock during the
74 CriticalSectionScoped cs(&_critSect);
75 saturationWarning = _saturationWarning;
76 if (_saturationWarning)
77 _saturationWarning = false;
80 if (saturationWarning)
82 CriticalSectionScoped cs(&_callbackCritSect);
83 if (_voiceEngineObserverPtr)
85 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
86 "TransmitMixer::OnPeriodicProcess() =>"
87 " CallbackOnError(VE_SATURATION_WARNING)");
88 _voiceEngineObserverPtr->CallbackOnError(-1, VE_SATURATION_WARNING);
94 void TransmitMixer::PlayNotification(int32_t id,
97 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, -1),
98 "TransmitMixer::PlayNotification(id=%d, durationMs=%d)",
104 void TransmitMixer::RecordNotification(int32_t id,
107 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
108 "TransmitMixer::RecordNotification(id=%d, durationMs=%d)",
114 void TransmitMixer::PlayFileEnded(int32_t id)
116 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, -1),
117 "TransmitMixer::PlayFileEnded(id=%d)", id);
119 assert(id == _filePlayerId);
121 CriticalSectionScoped cs(&_critSect);
123 _filePlaying = false;
124 WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId, -1),
125 "TransmitMixer::PlayFileEnded() =>"
126 "file player module is shutdown");
130 TransmitMixer::RecordFileEnded(int32_t id)
132 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, -1),
133 "TransmitMixer::RecordFileEnded(id=%d)", id);
135 if (id == _fileRecorderId)
137 CriticalSectionScoped cs(&_critSect);
138 _fileRecording = false;
139 WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId, -1),
140 "TransmitMixer::RecordFileEnded() => fileRecorder module"
142 } else if (id == _fileCallRecorderId)
144 CriticalSectionScoped cs(&_critSect);
145 _fileCallRecording = false;
146 WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId, -1),
147 "TransmitMixer::RecordFileEnded() => fileCallRecorder"
148 "module is shutdown");
153 TransmitMixer::Create(TransmitMixer*& mixer, uint32_t instanceId)
155 WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(instanceId, -1),
156 "TransmitMixer::Create(instanceId=%d)", instanceId);
157 mixer = new TransmitMixer(instanceId);
160 WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(instanceId, -1),
161 "TransmitMixer::Create() unable to allocate memory"
169 TransmitMixer::Destroy(TransmitMixer*& mixer)
178 TransmitMixer::TransmitMixer(uint32_t instanceId) :
179 _engineStatisticsPtr(NULL),
180 _channelManagerPtr(NULL),
182 _voiceEngineObserverPtr(NULL),
183 _processThreadPtr(NULL),
184 _filePlayerPtr(NULL),
185 _fileRecorderPtr(NULL),
186 _fileCallRecorderPtr(NULL),
187 // Avoid conflict with other channels by adding 1024 - 1026,
188 // won't use as much as 1024 channels.
189 _filePlayerId(instanceId + 1024),
190 _fileRecorderId(instanceId + 1025),
191 _fileCallRecorderId(instanceId + 1026),
193 _fileRecording(false),
194 _fileCallRecording(false),
196 _critSect(*CriticalSectionWrapper::CreateCriticalSection()),
197 _callbackCritSect(*CriticalSectionWrapper::CreateCriticalSection()),
198 #ifdef WEBRTC_VOICE_ENGINE_TYPING_DETECTION
199 _typingNoiseWarningPending(false),
200 _typingNoiseDetected(false),
202 _saturationWarning(false),
203 _instanceId(instanceId),
204 _mixFileWithMicrophone(false),
206 external_postproc_ptr_(NULL),
207 external_preproc_ptr_(NULL),
209 _remainingMuteMicTimeMs(0),
210 stereo_codec_(false),
211 swap_stereo_channels_(false)
213 WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId, -1),
214 "TransmitMixer::TransmitMixer() - ctor");
217 TransmitMixer::~TransmitMixer()
219 WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId, -1),
220 "TransmitMixer::~TransmitMixer() - dtor");
221 _monitorModule.DeRegisterObserver();
222 if (_processThreadPtr)
224 _processThreadPtr->DeRegisterModule(&_monitorModule);
226 DeRegisterExternalMediaProcessing(kRecordingAllChannelsMixed);
227 DeRegisterExternalMediaProcessing(kRecordingPreprocessing);
229 CriticalSectionScoped cs(&_critSect);
230 if (_fileRecorderPtr)
232 _fileRecorderPtr->RegisterModuleFileCallback(NULL);
233 _fileRecorderPtr->StopRecording();
234 FileRecorder::DestroyFileRecorder(_fileRecorderPtr);
235 _fileRecorderPtr = NULL;
237 if (_fileCallRecorderPtr)
239 _fileCallRecorderPtr->RegisterModuleFileCallback(NULL);
240 _fileCallRecorderPtr->StopRecording();
241 FileRecorder::DestroyFileRecorder(_fileCallRecorderPtr);
242 _fileCallRecorderPtr = NULL;
246 _filePlayerPtr->RegisterModuleFileCallback(NULL);
247 _filePlayerPtr->StopPlayingFile();
248 FilePlayer::DestroyFilePlayer(_filePlayerPtr);
249 _filePlayerPtr = NULL;
253 delete &_callbackCritSect;
257 TransmitMixer::SetEngineInformation(ProcessThread& processThread,
258 Statistics& engineStatistics,
259 ChannelManager& channelManager)
261 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
262 "TransmitMixer::SetEngineInformation()");
264 _processThreadPtr = &processThread;
265 _engineStatisticsPtr = &engineStatistics;
266 _channelManagerPtr = &channelManager;
268 if (_processThreadPtr->RegisterModule(&_monitorModule) == -1)
270 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
271 "TransmitMixer::SetEngineInformation() failed to"
272 "register the monitor module");
275 _monitorModule.RegisterObserver(*this);
282 TransmitMixer::RegisterVoiceEngineObserver(VoiceEngineObserver& observer)
284 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
285 "TransmitMixer::RegisterVoiceEngineObserver()");
286 CriticalSectionScoped cs(&_callbackCritSect);
288 if (_voiceEngineObserverPtr)
290 _engineStatisticsPtr->SetLastError(
291 VE_INVALID_OPERATION, kTraceError,
292 "RegisterVoiceEngineObserver() observer already enabled");
295 _voiceEngineObserverPtr = &observer;
300 TransmitMixer::SetAudioProcessingModule(AudioProcessing* audioProcessingModule)
302 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
303 "TransmitMixer::SetAudioProcessingModule("
304 "audioProcessingModule=0x%x)",
305 audioProcessingModule);
306 audioproc_ = audioProcessingModule;
310 void TransmitMixer::GetSendCodecInfo(int* max_sample_rate, int* max_channels) {
311 *max_sample_rate = 8000;
313 for (ChannelManager::Iterator it(_channelManagerPtr); it.IsValid();
315 Channel* channel = it.GetChannel();
316 if (channel->Sending()) {
318 channel->GetSendCodec(codec);
319 // TODO(tlegrand): Remove the 32 kHz restriction once we have full 48 kHz
320 // support in Audio Coding Module.
321 *max_sample_rate = std::min(32000,
322 std::max(*max_sample_rate, codec.plfreq));
323 *max_channels = std::max(*max_channels, codec.channels);
329 TransmitMixer::PrepareDemux(const void* audioSamples,
332 uint32_t samplesPerSec,
333 uint16_t totalDelayMS,
335 uint16_t currentMicLevel,
338 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, -1),
339 "TransmitMixer::PrepareDemux(nSamples=%u, nChannels=%u,"
340 "samplesPerSec=%u, totalDelayMS=%u, clockDrift=%d,"
341 "currentMicLevel=%u)", nSamples, nChannels, samplesPerSec,
342 totalDelayMS, clockDrift, currentMicLevel);
344 // --- Resample input audio and create/store the initial audio frame
345 if (GenerateAudioFrame(static_cast<const int16_t*>(audioSamples),
348 samplesPerSec) == -1)
354 CriticalSectionScoped cs(&_callbackCritSect);
355 if (external_preproc_ptr_) {
356 external_preproc_ptr_->Process(-1, kRecordingPreprocessing,
358 _audioFrame.samples_per_channel_,
359 _audioFrame.sample_rate_hz_,
360 _audioFrame.num_channels_ == 2);
364 // --- Near-end audio processing.
365 ProcessAudio(totalDelayMS, clockDrift, currentMicLevel, keyPressed);
367 if (swap_stereo_channels_ && stereo_codec_)
368 // Only bother swapping if we're using a stereo codec.
369 AudioFrameOperations::SwapStereoChannels(&_audioFrame);
371 // --- Annoying typing detection (utilizes the APM/VAD decision)
372 #ifdef WEBRTC_VOICE_ENGINE_TYPING_DETECTION
373 TypingDetection(keyPressed);
376 // --- Mute during DTMF tone if direct feedback is enabled
377 if (_remainingMuteMicTimeMs > 0)
379 AudioFrameOperations::Mute(_audioFrame);
380 _remainingMuteMicTimeMs -= 10;
381 if (_remainingMuteMicTimeMs < 0)
383 _remainingMuteMicTimeMs = 0;
390 AudioFrameOperations::Mute(_audioFrame);
393 // --- Mix with file (does not affect the mixing frequency)
396 MixOrReplaceAudioWithFile(_audioFrame.sample_rate_hz_);
399 // --- Record to file
402 RecordAudioToFile(_audioFrame.sample_rate_hz_);
406 CriticalSectionScoped cs(&_callbackCritSect);
407 if (external_postproc_ptr_) {
408 external_postproc_ptr_->Process(-1, kRecordingAllChannelsMixed,
410 _audioFrame.samples_per_channel_,
411 _audioFrame.sample_rate_hz_,
412 _audioFrame.num_channels_ == 2);
416 // --- Measure audio level of speech after all processing.
417 _audioLevel.ComputeLevel(_audioFrame);
422 TransmitMixer::DemuxAndMix()
424 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, -1),
425 "TransmitMixer::DemuxAndMix()");
427 for (ChannelManager::Iterator it(_channelManagerPtr); it.IsValid();
430 Channel* channelPtr = it.GetChannel();
431 if (channelPtr->InputIsOnHold())
433 channelPtr->UpdateLocalTimeStamp();
434 } else if (channelPtr->Sending())
436 // Demultiplex makes a copy of its input.
437 channelPtr->Demultiplex(_audioFrame);
438 channelPtr->PrepareEncodeAndSend(_audioFrame.sample_rate_hz_);
444 void TransmitMixer::DemuxAndMix(const int voe_channels[],
445 int number_of_voe_channels) {
446 for (int i = 0; i < number_of_voe_channels; ++i) {
447 voe::ChannelOwner ch = _channelManagerPtr->GetChannel(voe_channels[i]);
448 voe::Channel* channel_ptr = ch.channel();
450 if (channel_ptr->InputIsOnHold()) {
451 channel_ptr->UpdateLocalTimeStamp();
452 } else if (channel_ptr->Sending()) {
453 // Demultiplex makes a copy of its input.
454 channel_ptr->Demultiplex(_audioFrame);
455 channel_ptr->PrepareEncodeAndSend(_audioFrame.sample_rate_hz_);
462 TransmitMixer::EncodeAndSend()
464 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, -1),
465 "TransmitMixer::EncodeAndSend()");
467 for (ChannelManager::Iterator it(_channelManagerPtr); it.IsValid();
470 Channel* channelPtr = it.GetChannel();
471 if (channelPtr->Sending() && !channelPtr->InputIsOnHold())
473 channelPtr->EncodeAndSend();
479 void TransmitMixer::EncodeAndSend(const int voe_channels[],
480 int number_of_voe_channels) {
481 for (int i = 0; i < number_of_voe_channels; ++i) {
482 voe::ChannelOwner ch = _channelManagerPtr->GetChannel(voe_channels[i]);
483 voe::Channel* channel_ptr = ch.channel();
484 if (channel_ptr && channel_ptr->Sending() && !channel_ptr->InputIsOnHold())
485 channel_ptr->EncodeAndSend();
489 uint32_t TransmitMixer::CaptureLevel() const
491 return _captureLevel;
495 TransmitMixer::UpdateMuteMicrophoneTime(uint32_t lengthMs)
497 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
498 "TransmitMixer::UpdateMuteMicrophoneTime(lengthMs=%d)",
500 _remainingMuteMicTimeMs = lengthMs;
504 TransmitMixer::StopSend()
506 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
507 "TransmitMixer::StopSend()");
512 int TransmitMixer::StartPlayingFileAsMicrophone(const char* fileName,
518 const CodecInst* codecInst)
520 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
521 "TransmitMixer::StartPlayingFileAsMicrophone("
522 "fileNameUTF8[]=%s,loop=%d, format=%d, volumeScaling=%5.3f,"
523 " startPosition=%d, stopPosition=%d)", fileName, loop,
524 format, volumeScaling, startPosition, stopPosition);
528 _engineStatisticsPtr->SetLastError(
529 VE_ALREADY_PLAYING, kTraceWarning,
530 "StartPlayingFileAsMicrophone() is already playing");
534 CriticalSectionScoped cs(&_critSect);
536 // Destroy the old instance
539 _filePlayerPtr->RegisterModuleFileCallback(NULL);
540 FilePlayer::DestroyFilePlayer(_filePlayerPtr);
541 _filePlayerPtr = NULL;
544 // Dynamically create the instance
546 = FilePlayer::CreateFilePlayer(_filePlayerId,
547 (const FileFormats) format);
549 if (_filePlayerPtr == NULL)
551 _engineStatisticsPtr->SetLastError(
552 VE_INVALID_ARGUMENT, kTraceError,
553 "StartPlayingFileAsMicrophone() filePlayer format isnot correct");
557 const uint32_t notificationTime(0);
559 if (_filePlayerPtr->StartPlayingFile(
566 (const CodecInst*) codecInst) != 0)
568 _engineStatisticsPtr->SetLastError(
569 VE_BAD_FILE, kTraceError,
570 "StartPlayingFile() failed to start file playout");
571 _filePlayerPtr->StopPlayingFile();
572 FilePlayer::DestroyFilePlayer(_filePlayerPtr);
573 _filePlayerPtr = NULL;
577 _filePlayerPtr->RegisterModuleFileCallback(this);
583 int TransmitMixer::StartPlayingFileAsMicrophone(InStream* stream,
588 const CodecInst* codecInst)
590 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
591 "TransmitMixer::StartPlayingFileAsMicrophone(format=%d,"
592 " volumeScaling=%5.3f, startPosition=%d, stopPosition=%d)",
593 format, volumeScaling, startPosition, stopPosition);
597 _engineStatisticsPtr->SetLastError(
598 VE_BAD_FILE, kTraceError,
599 "StartPlayingFileAsMicrophone() NULL as input stream");
605 _engineStatisticsPtr->SetLastError(
606 VE_ALREADY_PLAYING, kTraceWarning,
607 "StartPlayingFileAsMicrophone() is already playing");
611 CriticalSectionScoped cs(&_critSect);
613 // Destroy the old instance
616 _filePlayerPtr->RegisterModuleFileCallback(NULL);
617 FilePlayer::DestroyFilePlayer(_filePlayerPtr);
618 _filePlayerPtr = NULL;
621 // Dynamically create the instance
623 = FilePlayer::CreateFilePlayer(_filePlayerId,
624 (const FileFormats) format);
626 if (_filePlayerPtr == NULL)
628 _engineStatisticsPtr->SetLastError(
629 VE_INVALID_ARGUMENT, kTraceWarning,
630 "StartPlayingFileAsMicrophone() filePlayer format isnot correct");
634 const uint32_t notificationTime(0);
636 if (_filePlayerPtr->StartPlayingFile(
642 (const CodecInst*) codecInst) != 0)
644 _engineStatisticsPtr->SetLastError(
645 VE_BAD_FILE, kTraceError,
646 "StartPlayingFile() failed to start file playout");
647 _filePlayerPtr->StopPlayingFile();
648 FilePlayer::DestroyFilePlayer(_filePlayerPtr);
649 _filePlayerPtr = NULL;
652 _filePlayerPtr->RegisterModuleFileCallback(this);
658 int TransmitMixer::StopPlayingFileAsMicrophone()
660 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
661 "TransmitMixer::StopPlayingFileAsMicrophone()");
665 _engineStatisticsPtr->SetLastError(
666 VE_INVALID_OPERATION, kTraceWarning,
667 "StopPlayingFileAsMicrophone() isnot playing");
671 CriticalSectionScoped cs(&_critSect);
673 if (_filePlayerPtr->StopPlayingFile() != 0)
675 _engineStatisticsPtr->SetLastError(
676 VE_CANNOT_STOP_PLAYOUT, kTraceError,
677 "StopPlayingFile() couldnot stop playing file");
681 _filePlayerPtr->RegisterModuleFileCallback(NULL);
682 FilePlayer::DestroyFilePlayer(_filePlayerPtr);
683 _filePlayerPtr = NULL;
684 _filePlaying = false;
689 int TransmitMixer::IsPlayingFileAsMicrophone() const
691 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
692 "TransmitMixer::IsPlayingFileAsMicrophone()");
696 int TransmitMixer::ScaleFileAsMicrophonePlayout(float scale)
698 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
699 "TransmitMixer::ScaleFileAsMicrophonePlayout(scale=%5.3f)",
702 CriticalSectionScoped cs(&_critSect);
706 _engineStatisticsPtr->SetLastError(
707 VE_INVALID_OPERATION, kTraceError,
708 "ScaleFileAsMicrophonePlayout() isnot playing file");
712 if ((_filePlayerPtr == NULL) ||
713 (_filePlayerPtr->SetAudioScaling(scale) != 0))
715 _engineStatisticsPtr->SetLastError(
716 VE_BAD_ARGUMENT, kTraceError,
717 "SetAudioScaling() failed to scale playout");
724 int TransmitMixer::StartRecordingMicrophone(const char* fileName,
725 const CodecInst* codecInst)
727 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
728 "TransmitMixer::StartRecordingMicrophone(fileName=%s)",
733 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
734 "StartRecordingMicrophone() is already recording");
739 const uint32_t notificationTime(0); // Not supported in VoE
740 CodecInst dummyCodec = { 100, "L16", 16000, 320, 1, 320000 };
742 if (codecInst != NULL &&
743 (codecInst->channels < 0 || codecInst->channels > 2))
745 _engineStatisticsPtr->SetLastError(
746 VE_BAD_ARGUMENT, kTraceError,
747 "StartRecordingMicrophone() invalid compression");
750 if (codecInst == NULL)
752 format = kFileFormatPcm16kHzFile;
753 codecInst = &dummyCodec;
754 } else if ((STR_CASE_CMP(codecInst->plname,"L16") == 0) ||
755 (STR_CASE_CMP(codecInst->plname,"PCMU") == 0) ||
756 (STR_CASE_CMP(codecInst->plname,"PCMA") == 0))
758 format = kFileFormatWavFile;
761 format = kFileFormatCompressedFile;
764 CriticalSectionScoped cs(&_critSect);
766 // Destroy the old instance
767 if (_fileRecorderPtr)
769 _fileRecorderPtr->RegisterModuleFileCallback(NULL);
770 FileRecorder::DestroyFileRecorder(_fileRecorderPtr);
771 _fileRecorderPtr = NULL;
775 FileRecorder::CreateFileRecorder(_fileRecorderId,
776 (const FileFormats) format);
777 if (_fileRecorderPtr == NULL)
779 _engineStatisticsPtr->SetLastError(
780 VE_INVALID_ARGUMENT, kTraceError,
781 "StartRecordingMicrophone() fileRecorder format isnot correct");
785 if (_fileRecorderPtr->StartRecordingAudioFile(
787 (const CodecInst&) *codecInst,
788 notificationTime) != 0)
790 _engineStatisticsPtr->SetLastError(
791 VE_BAD_FILE, kTraceError,
792 "StartRecordingAudioFile() failed to start file recording");
793 _fileRecorderPtr->StopRecording();
794 FileRecorder::DestroyFileRecorder(_fileRecorderPtr);
795 _fileRecorderPtr = NULL;
798 _fileRecorderPtr->RegisterModuleFileCallback(this);
799 _fileRecording = true;
804 int TransmitMixer::StartRecordingMicrophone(OutStream* stream,
805 const CodecInst* codecInst)
807 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
808 "TransmitMixer::StartRecordingMicrophone()");
812 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
813 "StartRecordingMicrophone() is already recording");
818 const uint32_t notificationTime(0); // Not supported in VoE
819 CodecInst dummyCodec = { 100, "L16", 16000, 320, 1, 320000 };
821 if (codecInst != NULL && codecInst->channels != 1)
823 _engineStatisticsPtr->SetLastError(
824 VE_BAD_ARGUMENT, kTraceError,
825 "StartRecordingMicrophone() invalid compression");
828 if (codecInst == NULL)
830 format = kFileFormatPcm16kHzFile;
831 codecInst = &dummyCodec;
832 } else if ((STR_CASE_CMP(codecInst->plname,"L16") == 0) ||
833 (STR_CASE_CMP(codecInst->plname,"PCMU") == 0) ||
834 (STR_CASE_CMP(codecInst->plname,"PCMA") == 0))
836 format = kFileFormatWavFile;
839 format = kFileFormatCompressedFile;
842 CriticalSectionScoped cs(&_critSect);
844 // Destroy the old instance
845 if (_fileRecorderPtr)
847 _fileRecorderPtr->RegisterModuleFileCallback(NULL);
848 FileRecorder::DestroyFileRecorder(_fileRecorderPtr);
849 _fileRecorderPtr = NULL;
853 FileRecorder::CreateFileRecorder(_fileRecorderId,
854 (const FileFormats) format);
855 if (_fileRecorderPtr == NULL)
857 _engineStatisticsPtr->SetLastError(
858 VE_INVALID_ARGUMENT, kTraceError,
859 "StartRecordingMicrophone() fileRecorder format isnot correct");
863 if (_fileRecorderPtr->StartRecordingAudioFile(*stream,
865 notificationTime) != 0)
867 _engineStatisticsPtr->SetLastError(VE_BAD_FILE, kTraceError,
868 "StartRecordingAudioFile() failed to start file recording");
869 _fileRecorderPtr->StopRecording();
870 FileRecorder::DestroyFileRecorder(_fileRecorderPtr);
871 _fileRecorderPtr = NULL;
875 _fileRecorderPtr->RegisterModuleFileCallback(this);
876 _fileRecording = true;
882 int TransmitMixer::StopRecordingMicrophone()
884 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
885 "TransmitMixer::StopRecordingMicrophone()");
889 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
890 "StopRecordingMicrophone() isnot recording");
894 CriticalSectionScoped cs(&_critSect);
896 if (_fileRecorderPtr->StopRecording() != 0)
898 _engineStatisticsPtr->SetLastError(
899 VE_STOP_RECORDING_FAILED, kTraceError,
900 "StopRecording(), could not stop recording");
903 _fileRecorderPtr->RegisterModuleFileCallback(NULL);
904 FileRecorder::DestroyFileRecorder(_fileRecorderPtr);
905 _fileRecorderPtr = NULL;
906 _fileRecording = false;
911 int TransmitMixer::StartRecordingCall(const char* fileName,
912 const CodecInst* codecInst)
914 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
915 "TransmitMixer::StartRecordingCall(fileName=%s)", fileName);
917 if (_fileCallRecording)
919 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
920 "StartRecordingCall() is already recording");
925 const uint32_t notificationTime(0); // Not supported in VoE
926 CodecInst dummyCodec = { 100, "L16", 16000, 320, 1, 320000 };
928 if (codecInst != NULL && codecInst->channels != 1)
930 _engineStatisticsPtr->SetLastError(
931 VE_BAD_ARGUMENT, kTraceError,
932 "StartRecordingCall() invalid compression");
935 if (codecInst == NULL)
937 format = kFileFormatPcm16kHzFile;
938 codecInst = &dummyCodec;
939 } else if ((STR_CASE_CMP(codecInst->plname,"L16") == 0) ||
940 (STR_CASE_CMP(codecInst->plname,"PCMU") == 0) ||
941 (STR_CASE_CMP(codecInst->plname,"PCMA") == 0))
943 format = kFileFormatWavFile;
946 format = kFileFormatCompressedFile;
949 CriticalSectionScoped cs(&_critSect);
951 // Destroy the old instance
952 if (_fileCallRecorderPtr)
954 _fileCallRecorderPtr->RegisterModuleFileCallback(NULL);
955 FileRecorder::DestroyFileRecorder(_fileCallRecorderPtr);
956 _fileCallRecorderPtr = NULL;
960 = FileRecorder::CreateFileRecorder(_fileCallRecorderId,
961 (const FileFormats) format);
962 if (_fileCallRecorderPtr == NULL)
964 _engineStatisticsPtr->SetLastError(
965 VE_INVALID_ARGUMENT, kTraceError,
966 "StartRecordingCall() fileRecorder format isnot correct");
970 if (_fileCallRecorderPtr->StartRecordingAudioFile(
972 (const CodecInst&) *codecInst,
973 notificationTime) != 0)
975 _engineStatisticsPtr->SetLastError(
976 VE_BAD_FILE, kTraceError,
977 "StartRecordingAudioFile() failed to start file recording");
978 _fileCallRecorderPtr->StopRecording();
979 FileRecorder::DestroyFileRecorder(_fileCallRecorderPtr);
980 _fileCallRecorderPtr = NULL;
983 _fileCallRecorderPtr->RegisterModuleFileCallback(this);
984 _fileCallRecording = true;
989 int TransmitMixer::StartRecordingCall(OutStream* stream,
990 const CodecInst* codecInst)
992 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
993 "TransmitMixer::StartRecordingCall()");
995 if (_fileCallRecording)
997 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
998 "StartRecordingCall() is already recording");
1003 const uint32_t notificationTime(0); // Not supported in VoE
1004 CodecInst dummyCodec = { 100, "L16", 16000, 320, 1, 320000 };
1006 if (codecInst != NULL && codecInst->channels != 1)
1008 _engineStatisticsPtr->SetLastError(
1009 VE_BAD_ARGUMENT, kTraceError,
1010 "StartRecordingCall() invalid compression");
1013 if (codecInst == NULL)
1015 format = kFileFormatPcm16kHzFile;
1016 codecInst = &dummyCodec;
1017 } else if ((STR_CASE_CMP(codecInst->plname,"L16") == 0) ||
1018 (STR_CASE_CMP(codecInst->plname,"PCMU") == 0) ||
1019 (STR_CASE_CMP(codecInst->plname,"PCMA") == 0))
1021 format = kFileFormatWavFile;
1024 format = kFileFormatCompressedFile;
1027 CriticalSectionScoped cs(&_critSect);
1029 // Destroy the old instance
1030 if (_fileCallRecorderPtr)
1032 _fileCallRecorderPtr->RegisterModuleFileCallback(NULL);
1033 FileRecorder::DestroyFileRecorder(_fileCallRecorderPtr);
1034 _fileCallRecorderPtr = NULL;
1037 _fileCallRecorderPtr =
1038 FileRecorder::CreateFileRecorder(_fileCallRecorderId,
1039 (const FileFormats) format);
1040 if (_fileCallRecorderPtr == NULL)
1042 _engineStatisticsPtr->SetLastError(
1043 VE_INVALID_ARGUMENT, kTraceError,
1044 "StartRecordingCall() fileRecorder format isnot correct");
1048 if (_fileCallRecorderPtr->StartRecordingAudioFile(*stream,
1050 notificationTime) != 0)
1052 _engineStatisticsPtr->SetLastError(VE_BAD_FILE, kTraceError,
1053 "StartRecordingAudioFile() failed to start file recording");
1054 _fileCallRecorderPtr->StopRecording();
1055 FileRecorder::DestroyFileRecorder(_fileCallRecorderPtr);
1056 _fileCallRecorderPtr = NULL;
1060 _fileCallRecorderPtr->RegisterModuleFileCallback(this);
1061 _fileCallRecording = true;
1066 int TransmitMixer::StopRecordingCall()
1068 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
1069 "TransmitMixer::StopRecordingCall()");
1071 if (!_fileCallRecording)
1073 WEBRTC_TRACE(kTraceError, kTraceVoice, VoEId(_instanceId, -1),
1074 "StopRecordingCall() file isnot recording");
1078 CriticalSectionScoped cs(&_critSect);
1080 if (_fileCallRecorderPtr->StopRecording() != 0)
1082 _engineStatisticsPtr->SetLastError(
1083 VE_STOP_RECORDING_FAILED, kTraceError,
1084 "StopRecording(), could not stop recording");
1088 _fileCallRecorderPtr->RegisterModuleFileCallback(NULL);
1089 FileRecorder::DestroyFileRecorder(_fileCallRecorderPtr);
1090 _fileCallRecorderPtr = NULL;
1091 _fileCallRecording = false;
1097 TransmitMixer::SetMixWithMicStatus(bool mix)
1099 _mixFileWithMicrophone = mix;
1102 int TransmitMixer::RegisterExternalMediaProcessing(
1103 VoEMediaProcess* object,
1104 ProcessingTypes type) {
1105 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
1106 "TransmitMixer::RegisterExternalMediaProcessing()");
1108 CriticalSectionScoped cs(&_callbackCritSect);
1113 // Store the callback object according to the processing type.
1114 if (type == kRecordingAllChannelsMixed) {
1115 external_postproc_ptr_ = object;
1116 } else if (type == kRecordingPreprocessing) {
1117 external_preproc_ptr_ = object;
1124 int TransmitMixer::DeRegisterExternalMediaProcessing(ProcessingTypes type) {
1125 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
1126 "TransmitMixer::DeRegisterExternalMediaProcessing()");
1128 CriticalSectionScoped cs(&_callbackCritSect);
1129 if (type == kRecordingAllChannelsMixed) {
1130 external_postproc_ptr_ = NULL;
1131 } else if (type == kRecordingPreprocessing) {
1132 external_preproc_ptr_ = NULL;
1140 TransmitMixer::SetMute(bool enable)
1142 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
1143 "TransmitMixer::SetMute(enable=%d)", enable);
1149 TransmitMixer::Mute() const
1154 int8_t TransmitMixer::AudioLevel() const
1156 // Speech + file level [0,9]
1157 return _audioLevel.Level();
1160 int16_t TransmitMixer::AudioLevelFullRange() const
1162 // Speech + file level [0,32767]
1163 return _audioLevel.LevelFullRange();
1166 bool TransmitMixer::IsRecordingCall()
1168 return _fileCallRecording;
1171 bool TransmitMixer::IsRecordingMic()
1174 return _fileRecording;
1177 // TODO(andrew): use RemixAndResample for this.
1178 int TransmitMixer::GenerateAudioFrame(const int16_t audio[],
1179 int samples_per_channel,
1181 int sample_rate_hz) {
1182 int destination_rate;
1183 int num_codec_channels;
1184 GetSendCodecInfo(&destination_rate, &num_codec_channels);
1186 // Never upsample the capture signal here. This should be done at the
1187 // end of the send chain.
1188 destination_rate = std::min(destination_rate, sample_rate_hz);
1189 stereo_codec_ = num_codec_channels == 2;
1191 const int16_t* audio_ptr = audio;
1192 int16_t mono_audio[kMaxMonoDeviceDataSizeSamples];
1193 assert(samples_per_channel <= kMaxMonoDeviceDataSizeSamples);
1194 // If no stereo codecs are in use, we downmix a stereo stream from the
1195 // device early in the chain, before resampling.
1196 if (num_channels == 2 && !stereo_codec_) {
1197 AudioFrameOperations::StereoToMono(audio, samples_per_channel,
1199 audio_ptr = mono_audio;
1203 if (resampler_.InitializeIfNeeded(sample_rate_hz,
1205 num_channels) != 0) {
1206 WEBRTC_TRACE(kTraceError, kTraceVoice, VoEId(_instanceId, -1),
1207 "TransmitMixer::GenerateAudioFrame() unable to resample");
1211 int out_length = resampler_.Resample(audio_ptr,
1212 samples_per_channel * num_channels,
1214 AudioFrame::kMaxDataSizeSamples);
1215 if (out_length == -1) {
1216 WEBRTC_TRACE(kTraceError, kTraceVoice, VoEId(_instanceId, -1),
1217 "TransmitMixer::GenerateAudioFrame() resampling failed");
1221 _audioFrame.samples_per_channel_ = out_length / num_channels;
1222 _audioFrame.id_ = _instanceId;
1223 _audioFrame.timestamp_ = -1;
1224 _audioFrame.sample_rate_hz_ = destination_rate;
1225 _audioFrame.speech_type_ = AudioFrame::kNormalSpeech;
1226 _audioFrame.vad_activity_ = AudioFrame::kVadUnknown;
1227 _audioFrame.num_channels_ = num_channels;
1232 int32_t TransmitMixer::RecordAudioToFile(
1233 uint32_t mixingFrequency)
1235 CriticalSectionScoped cs(&_critSect);
1236 if (_fileRecorderPtr == NULL)
1238 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
1239 "TransmitMixer::RecordAudioToFile() filerecorder doesnot"
1244 if (_fileRecorderPtr->RecordAudioToFile(_audioFrame) != 0)
1246 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
1247 "TransmitMixer::RecordAudioToFile() file recording"
1255 int32_t TransmitMixer::MixOrReplaceAudioWithFile(
1256 int mixingFrequency)
1258 scoped_array<int16_t> fileBuffer(new int16_t[640]);
1262 CriticalSectionScoped cs(&_critSect);
1263 if (_filePlayerPtr == NULL)
1265 WEBRTC_TRACE(kTraceWarning, kTraceVoice,
1266 VoEId(_instanceId, -1),
1267 "TransmitMixer::MixOrReplaceAudioWithFile()"
1268 "fileplayer doesnot exist");
1272 if (_filePlayerPtr->Get10msAudioFromFile(fileBuffer.get(),
1274 mixingFrequency) == -1)
1276 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
1277 "TransmitMixer::MixOrReplaceAudioWithFile() file"
1283 assert(_audioFrame.samples_per_channel_ == fileSamples);
1285 if (_mixFileWithMicrophone)
1287 // Currently file stream is always mono.
1288 // TODO(xians): Change the code when FilePlayer supports real stereo.
1289 Utility::MixWithSat(_audioFrame.data_,
1290 _audioFrame.num_channels_,
1296 // Replace ACM audio with file.
1297 // Currently file stream is always mono.
1298 // TODO(xians): Change the code when FilePlayer supports real stereo.
1299 _audioFrame.UpdateFrame(-1,
1304 AudioFrame::kNormalSpeech,
1305 AudioFrame::kVadUnknown,
1311 void TransmitMixer::ProcessAudio(int delay_ms, int clock_drift,
1312 int current_mic_level, bool key_pressed) {
1313 if (audioproc_->set_stream_delay_ms(delay_ms) != 0) {
1314 // A redundant warning is reported in AudioDevice, which we've throttled
1315 // to avoid flooding the logs. Relegate this one to LS_VERBOSE to avoid
1316 // repeating the problem here.
1317 LOG_FERR1(LS_VERBOSE, set_stream_delay_ms, delay_ms);
1320 GainControl* agc = audioproc_->gain_control();
1321 if (agc->set_stream_analog_level(current_mic_level) != 0) {
1322 LOG_FERR1(LS_ERROR, set_stream_analog_level, current_mic_level);
1326 EchoCancellation* aec = audioproc_->echo_cancellation();
1327 if (aec->is_drift_compensation_enabled()) {
1328 aec->set_stream_drift_samples(clock_drift);
1331 audioproc_->set_stream_key_pressed(key_pressed);
1333 int err = audioproc_->ProcessStream(&_audioFrame);
1335 LOG(LS_ERROR) << "ProcessStream() error: " << err;
1339 // Store new capture level. Only updated when analog AGC is enabled.
1340 _captureLevel = agc->stream_analog_level();
1342 CriticalSectionScoped cs(&_critSect);
1343 // Triggers a callback in OnPeriodicProcess().
1344 _saturationWarning |= agc->stream_is_saturated();
1347 #ifdef WEBRTC_VOICE_ENGINE_TYPING_DETECTION
1348 void TransmitMixer::TypingDetection(bool keyPressed)
1350 // We let the VAD determine if we're using this feature or not.
1351 if (_audioFrame.vad_activity_ == AudioFrame::kVadUnknown) {
1355 bool vadActive = _audioFrame.vad_activity_ == AudioFrame::kVadActive;
1356 if (_typingDetection.Process(keyPressed, vadActive)) {
1357 _typingNoiseWarningPending = true;
1358 _typingNoiseDetected = true;
1360 // If there is already a warning pending, do not change the state.
1361 // Otherwise set a warning pending if last callback was for noise detected.
1362 if (!_typingNoiseWarningPending && _typingNoiseDetected) {
1363 _typingNoiseWarningPending = true;
1364 _typingNoiseDetected = false;
1370 int TransmitMixer::GetMixingFrequency()
1372 assert(_audioFrame.sample_rate_hz_ != 0);
1373 return _audioFrame.sample_rate_hz_;
1376 #ifdef WEBRTC_VOICE_ENGINE_TYPING_DETECTION
1377 int TransmitMixer::TimeSinceLastTyping(int &seconds)
1379 // We check in VoEAudioProcessingImpl that this is only called when
1380 // typing detection is active.
1381 seconds = _typingDetection.TimeSinceLastDetectionInSeconds();
1386 #ifdef WEBRTC_VOICE_ENGINE_TYPING_DETECTION
1387 int TransmitMixer::SetTypingDetectionParameters(int timeWindow,
1389 int reportingThreshold,
1393 _typingDetection.SetParameters(timeWindow,
1403 void TransmitMixer::EnableStereoChannelSwapping(bool enable) {
1404 swap_stereo_channels_ = enable;
1407 bool TransmitMixer::IsStereoChannelSwappingEnabled() {
1408 return swap_stereo_channels_;
1413 } // namespace webrtc