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