blob: aa50e0bf2be0ad06fb7c3c68bb7d922fde81d040 [file] [log] [blame]
niklase@google.com470e71d2011-07-07 08:21:25 +00001/*
mflodman@webrtc.org9a065d12012-03-07 08:12:21 +00002 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
niklase@google.com470e71d2011-07-07 08:21:25 +00003 *
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
andrew@webrtc.org50b2efe2013-04-29 17:27:29 +000011#include "webrtc/voice_engine/output_mixer.h"
niklase@google.com470e71d2011-07-07 08:21:25 +000012
Peter Kasting69558702016-01-12 16:26:35 -080013#include "webrtc/base/format_macros.h"
andrew@webrtc.org50b2efe2013-04-29 17:27:29 +000014#include "webrtc/modules/audio_processing/include/audio_processing.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/file_wrapper.h"
17#include "webrtc/system_wrappers/include/trace.h"
andrew@webrtc.org50b2efe2013-04-29 17:27:29 +000018#include "webrtc/voice_engine/include/voe_external_media.h"
andrew@webrtc.org50b2efe2013-04-29 17:27:29 +000019#include "webrtc/voice_engine/statistics.h"
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +000020#include "webrtc/voice_engine/utility.h"
niklase@google.com470e71d2011-07-07 08:21:25 +000021
22namespace webrtc {
niklase@google.com470e71d2011-07-07 08:21:25 +000023namespace voe {
24
25void
pbos@webrtc.org92135212013-05-14 08:31:39 +000026OutputMixer::NewMixedAudio(int32_t id,
niklase@google.com470e71d2011-07-07 08:21:25 +000027 const AudioFrame& generalAudioFrame,
28 const AudioFrame** uniqueAudioFrames,
pbos@webrtc.org92135212013-05-14 08:31:39 +000029 uint32_t size)
niklase@google.com470e71d2011-07-07 08:21:25 +000030{
31 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
32 "OutputMixer::NewMixedAudio(id=%d, size=%u)", id, size);
33
andrew@webrtc.orgae1a58b2013-01-22 04:44:30 +000034 _audioFrame.CopyFrom(generalAudioFrame);
andrew@webrtc.org63a50982012-05-02 23:56:37 +000035 _audioFrame.id_ = id;
niklase@google.com470e71d2011-07-07 08:21:25 +000036}
37
pbos@webrtc.org92135212013-05-14 08:31:39 +000038void OutputMixer::PlayNotification(int32_t id, uint32_t durationMs)
niklase@google.com470e71d2011-07-07 08:21:25 +000039{
40 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
41 "OutputMixer::PlayNotification(id=%d, durationMs=%d)",
42 id, durationMs);
43 // Not implement yet
44}
45
pbos@webrtc.org92135212013-05-14 08:31:39 +000046void OutputMixer::RecordNotification(int32_t id,
47 uint32_t durationMs)
niklase@google.com470e71d2011-07-07 08:21:25 +000048{
49 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
50 "OutputMixer::RecordNotification(id=%d, durationMs=%d)",
51 id, durationMs);
52
53 // Not implement yet
54}
55
pbos@webrtc.org92135212013-05-14 08:31:39 +000056void OutputMixer::PlayFileEnded(int32_t id)
niklase@google.com470e71d2011-07-07 08:21:25 +000057{
58 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
59 "OutputMixer::PlayFileEnded(id=%d)", id);
60
61 // not needed
62}
63
pbos@webrtc.org92135212013-05-14 08:31:39 +000064void OutputMixer::RecordFileEnded(int32_t id)
niklase@google.com470e71d2011-07-07 08:21:25 +000065{
66 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
67 "OutputMixer::RecordFileEnded(id=%d)", id);
68 assert(id == _instanceId);
69
tommi31fc21f2016-01-21 10:37:37 -080070 rtc::CritScope cs(&_fileCritSect);
niklase@google.com470e71d2011-07-07 08:21:25 +000071 _outputFileRecording = false;
72 WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId,-1),
73 "OutputMixer::RecordFileEnded() =>"
74 "output file recorder module is shutdown");
75}
76
pbos@webrtc.org6141e132013-04-09 10:09:10 +000077int32_t
pbos@webrtc.org92135212013-05-14 08:31:39 +000078OutputMixer::Create(OutputMixer*& mixer, uint32_t instanceId)
niklase@google.com470e71d2011-07-07 08:21:25 +000079{
80 WEBRTC_TRACE(kTraceMemory, kTraceVoice, instanceId,
81 "OutputMixer::Create(instanceId=%d)", instanceId);
82 mixer = new OutputMixer(instanceId);
83 if (mixer == NULL)
84 {
85 WEBRTC_TRACE(kTraceMemory, kTraceVoice, instanceId,
86 "OutputMixer::Create() unable to allocate memory for"
87 "mixer");
88 return -1;
89 }
90 return 0;
91}
92
pbos@webrtc.org92135212013-05-14 08:31:39 +000093OutputMixer::OutputMixer(uint32_t instanceId) :
andrew@webrtc.orgc4f129f2011-11-10 03:41:22 +000094 _mixerModule(*AudioConferenceMixer::Create(instanceId)),
niklase@google.com470e71d2011-07-07 08:21:25 +000095 _audioLevel(),
xians@google.com22963ab2011-08-03 12:40:23 +000096 _dtmfGenerator(instanceId),
97 _instanceId(instanceId),
98 _externalMediaCallbackPtr(NULL),
niklase@google.com470e71d2011-07-07 08:21:25 +000099 _externalMedia(false),
100 _panLeft(1.0f),
101 _panRight(1.0f),
xians@google.com22963ab2011-08-03 12:40:23 +0000102 _mixingFrequencyHz(8000),
103 _outputFileRecorderPtr(NULL),
104 _outputFileRecording(false)
niklase@google.com470e71d2011-07-07 08:21:25 +0000105{
106 WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId,-1),
107 "OutputMixer::OutputMixer() - ctor");
andrew@webrtc.orgae1a58b2013-01-22 04:44:30 +0000108
minyuel0f4b3732015-08-31 16:04:32 +0200109 if (_mixerModule.RegisterMixedStreamCallback(this) == -1)
niklase@google.com470e71d2011-07-07 08:21:25 +0000110 {
111 WEBRTC_TRACE(kTraceError, kTraceVoice, VoEId(_instanceId,-1),
112 "OutputMixer::OutputMixer() failed to register mixer"
113 "callbacks");
114 }
andrew@webrtc.orgae1a58b2013-01-22 04:44:30 +0000115
niklase@google.com470e71d2011-07-07 08:21:25 +0000116 _dtmfGenerator.Init();
117}
118
119void
120OutputMixer::Destroy(OutputMixer*& mixer)
121{
122 if (mixer)
123 {
124 delete mixer;
125 mixer = NULL;
126 }
127}
andrew@webrtc.orgae1a58b2013-01-22 04:44:30 +0000128
niklase@google.com470e71d2011-07-07 08:21:25 +0000129OutputMixer::~OutputMixer()
130{
131 WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId,-1),
132 "OutputMixer::~OutputMixer() - dtor");
133 if (_externalMedia)
134 {
135 DeRegisterExternalMediaProcessing();
136 }
137 {
tommi31fc21f2016-01-21 10:37:37 -0800138 rtc::CritScope cs(&_fileCritSect);
niklase@google.com470e71d2011-07-07 08:21:25 +0000139 if (_outputFileRecorderPtr)
140 {
141 _outputFileRecorderPtr->RegisterModuleFileCallback(NULL);
142 _outputFileRecorderPtr->StopRecording();
143 FileRecorder::DestroyFileRecorder(_outputFileRecorderPtr);
144 _outputFileRecorderPtr = NULL;
145 }
146 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000147 _mixerModule.UnRegisterMixedStreamCallback();
148 delete &_mixerModule;
niklase@google.com470e71d2011-07-07 08:21:25 +0000149}
150
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000151int32_t
niklase@google.com470e71d2011-07-07 08:21:25 +0000152OutputMixer::SetEngineInformation(voe::Statistics& engineStatistics)
153{
154 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
155 "OutputMixer::SetEngineInformation()");
156 _engineStatisticsPtr = &engineStatistics;
157 return 0;
158}
159
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000160int32_t
161OutputMixer::SetAudioProcessingModule(AudioProcessing* audioProcessingModule)
niklase@google.com470e71d2011-07-07 08:21:25 +0000162{
163 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
164 "OutputMixer::SetAudioProcessingModule("
165 "audioProcessingModule=0x%x)", audioProcessingModule);
166 _audioProcessingModulePtr = audioProcessingModule;
167 return 0;
168}
169
170int OutputMixer::RegisterExternalMediaProcessing(
171 VoEMediaProcess& proccess_object)
172{
173 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
174 "OutputMixer::RegisterExternalMediaProcessing()");
175
tommi31fc21f2016-01-21 10:37:37 -0800176 rtc::CritScope cs(&_callbackCritSect);
niklase@google.com470e71d2011-07-07 08:21:25 +0000177 _externalMediaCallbackPtr = &proccess_object;
178 _externalMedia = true;
179
180 return 0;
181}
182
183int OutputMixer::DeRegisterExternalMediaProcessing()
184{
185 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
186 "OutputMixer::DeRegisterExternalMediaProcessing()");
187
tommi31fc21f2016-01-21 10:37:37 -0800188 rtc::CritScope cs(&_callbackCritSect);
niklase@google.com470e71d2011-07-07 08:21:25 +0000189 _externalMedia = false;
190 _externalMediaCallbackPtr = NULL;
191
192 return 0;
193}
194
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000195int OutputMixer::PlayDtmfTone(uint8_t eventCode, int lengthMs,
niklase@google.com470e71d2011-07-07 08:21:25 +0000196 int attenuationDb)
197{
198 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
199 "OutputMixer::PlayDtmfTone()");
200 if (_dtmfGenerator.AddTone(eventCode, lengthMs, attenuationDb) != 0)
201 {
202 _engineStatisticsPtr->SetLastError(VE_STILL_PLAYING_PREV_DTMF,
203 kTraceError,
204 "OutputMixer::PlayDtmfTone()");
205 return -1;
206 }
207 return 0;
208}
209
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000210int32_t
niklase@google.com470e71d2011-07-07 08:21:25 +0000211OutputMixer::SetMixabilityStatus(MixerParticipant& participant,
pbos@webrtc.org92135212013-05-14 08:31:39 +0000212 bool mixable)
niklase@google.com470e71d2011-07-07 08:21:25 +0000213{
minyuel0f4b3732015-08-31 16:04:32 +0200214 return _mixerModule.SetMixabilityStatus(&participant, mixable);
niklase@google.com470e71d2011-07-07 08:21:25 +0000215}
216
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000217int32_t
henrike@webrtc.org066f9e52011-10-28 23:15:47 +0000218OutputMixer::SetAnonymousMixabilityStatus(MixerParticipant& participant,
pbos@webrtc.org92135212013-05-14 08:31:39 +0000219 bool mixable)
henrike@webrtc.org066f9e52011-10-28 23:15:47 +0000220{
minyuel0f4b3732015-08-31 16:04:32 +0200221 return _mixerModule.SetAnonymousMixabilityStatus(&participant, mixable);
henrike@webrtc.org066f9e52011-10-28 23:15:47 +0000222}
223
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000224int32_t
niklase@google.com470e71d2011-07-07 08:21:25 +0000225OutputMixer::MixActiveChannels()
226{
227 return _mixerModule.Process();
228}
229
230int
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000231OutputMixer::GetSpeechOutputLevel(uint32_t& level)
niklase@google.com470e71d2011-07-07 08:21:25 +0000232{
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000233 int8_t currentLevel = _audioLevel.Level();
234 level = static_cast<uint32_t> (currentLevel);
niklase@google.com470e71d2011-07-07 08:21:25 +0000235 WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId,-1),
236 "GetSpeechOutputLevel() => level=%u", level);
237 return 0;
238}
239
240int
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000241OutputMixer::GetSpeechOutputLevelFullRange(uint32_t& level)
niklase@google.com470e71d2011-07-07 08:21:25 +0000242{
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000243 int16_t currentLevel = _audioLevel.LevelFullRange();
244 level = static_cast<uint32_t> (currentLevel);
niklase@google.com470e71d2011-07-07 08:21:25 +0000245 WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId,-1),
246 "GetSpeechOutputLevelFullRange() => level=%u", level);
247 return 0;
248}
249
250int
251OutputMixer::SetOutputVolumePan(float left, float right)
252{
253 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
254 "OutputMixer::SetOutputVolumePan()");
255 _panLeft = left;
256 _panRight = right;
257 return 0;
258}
259
260int
261OutputMixer::GetOutputVolumePan(float& left, float& right)
262{
263 left = _panLeft;
264 right = _panRight;
265 WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId,-1),
266 "GetOutputVolumePan() => left=%2.1f, right=%2.1f",
267 left, right);
268 return 0;
269}
270
271int OutputMixer::StartRecordingPlayout(const char* fileName,
272 const CodecInst* codecInst)
273{
274 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
275 "OutputMixer::StartRecordingPlayout(fileName=%s)", fileName);
276
277 if (_outputFileRecording)
278 {
279 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId,-1),
280 "StartRecordingPlayout() is already recording");
281 return 0;
282 }
283
284 FileFormats format;
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000285 const uint32_t notificationTime(0);
niklase@google.com470e71d2011-07-07 08:21:25 +0000286 CodecInst dummyCodec={100,"L16",16000,320,1,320000};
287
niklas.enbom@webrtc.org40197d72012-03-26 08:45:47 +0000288 if ((codecInst != NULL) &&
289 ((codecInst->channels < 1) || (codecInst->channels > 2)))
niklase@google.com470e71d2011-07-07 08:21:25 +0000290 {
291 _engineStatisticsPtr->SetLastError(
292 VE_BAD_ARGUMENT, kTraceError,
293 "StartRecordingPlayout() invalid compression");
294 return(-1);
295 }
296 if(codecInst == NULL)
297 {
298 format = kFileFormatPcm16kHzFile;
299 codecInst=&dummyCodec;
300 }
301 else if((STR_CASE_CMP(codecInst->plname,"L16") == 0) ||
302 (STR_CASE_CMP(codecInst->plname,"PCMU") == 0) ||
303 (STR_CASE_CMP(codecInst->plname,"PCMA") == 0))
304 {
305 format = kFileFormatWavFile;
306 }
307 else
308 {
309 format = kFileFormatCompressedFile;
310 }
311
tommi31fc21f2016-01-21 10:37:37 -0800312 rtc::CritScope cs(&_fileCritSect);
andrew@webrtc.orgae1a58b2013-01-22 04:44:30 +0000313
niklase@google.com470e71d2011-07-07 08:21:25 +0000314 // Destroy the old instance
315 if (_outputFileRecorderPtr)
316 {
317 _outputFileRecorderPtr->RegisterModuleFileCallback(NULL);
318 FileRecorder::DestroyFileRecorder(_outputFileRecorderPtr);
319 _outputFileRecorderPtr = NULL;
320 }
321
322 _outputFileRecorderPtr = FileRecorder::CreateFileRecorder(
323 _instanceId,
324 (const FileFormats)format);
325 if (_outputFileRecorderPtr == NULL)
326 {
327 _engineStatisticsPtr->SetLastError(
328 VE_INVALID_ARGUMENT, kTraceError,
329 "StartRecordingPlayout() fileRecorder format isnot correct");
330 return -1;
331 }
332
333 if (_outputFileRecorderPtr->StartRecordingAudioFile(
334 fileName,
335 (const CodecInst&)*codecInst,
336 notificationTime) != 0)
337 {
338 _engineStatisticsPtr->SetLastError(
339 VE_BAD_FILE, kTraceError,
340 "StartRecordingAudioFile() failed to start file recording");
341 _outputFileRecorderPtr->StopRecording();
342 FileRecorder::DestroyFileRecorder(_outputFileRecorderPtr);
343 _outputFileRecorderPtr = NULL;
344 return -1;
345 }
346 _outputFileRecorderPtr->RegisterModuleFileCallback(this);
347 _outputFileRecording = true;
348
349 return 0;
350}
351
352int OutputMixer::StartRecordingPlayout(OutStream* stream,
353 const CodecInst* codecInst)
354{
355 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
356 "OutputMixer::StartRecordingPlayout()");
357
358 if (_outputFileRecording)
359 {
360 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId,-1),
361 "StartRecordingPlayout() is already recording");
362 return 0;
363 }
364
365 FileFormats format;
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000366 const uint32_t notificationTime(0);
niklase@google.com470e71d2011-07-07 08:21:25 +0000367 CodecInst dummyCodec={100,"L16",16000,320,1,320000};
368
369 if (codecInst != NULL && codecInst->channels != 1)
370 {
371 _engineStatisticsPtr->SetLastError(
372 VE_BAD_ARGUMENT, kTraceError,
373 "StartRecordingPlayout() invalid compression");
374 return(-1);
375 }
376 if(codecInst == NULL)
377 {
378 format = kFileFormatPcm16kHzFile;
379 codecInst=&dummyCodec;
380 }
381 else if((STR_CASE_CMP(codecInst->plname,"L16") == 0) ||
382 (STR_CASE_CMP(codecInst->plname,"PCMU") == 0) ||
383 (STR_CASE_CMP(codecInst->plname,"PCMA") == 0))
384 {
385 format = kFileFormatWavFile;
386 }
387 else
388 {
389 format = kFileFormatCompressedFile;
390 }
391
tommi31fc21f2016-01-21 10:37:37 -0800392 rtc::CritScope cs(&_fileCritSect);
niklase@google.com470e71d2011-07-07 08:21:25 +0000393
394 // Destroy the old instance
395 if (_outputFileRecorderPtr)
396 {
397 _outputFileRecorderPtr->RegisterModuleFileCallback(NULL);
398 FileRecorder::DestroyFileRecorder(_outputFileRecorderPtr);
399 _outputFileRecorderPtr = NULL;
400 }
401
402 _outputFileRecorderPtr = FileRecorder::CreateFileRecorder(
403 _instanceId,
404 (const FileFormats)format);
405 if (_outputFileRecorderPtr == NULL)
406 {
407 _engineStatisticsPtr->SetLastError(
408 VE_INVALID_ARGUMENT, kTraceError,
409 "StartRecordingPlayout() fileRecorder format isnot correct");
410 return -1;
411 }
412
413 if (_outputFileRecorderPtr->StartRecordingAudioFile(*stream,
414 *codecInst,
415 notificationTime) != 0)
416 {
417 _engineStatisticsPtr->SetLastError(VE_BAD_FILE, kTraceError,
andrew@webrtc.org4ecea3e2012-06-27 03:25:31 +0000418 "StartRecordingAudioFile() failed to start file recording");
niklase@google.com470e71d2011-07-07 08:21:25 +0000419 _outputFileRecorderPtr->StopRecording();
420 FileRecorder::DestroyFileRecorder(_outputFileRecorderPtr);
421 _outputFileRecorderPtr = NULL;
422 return -1;
423 }
andrew@webrtc.org4ecea3e2012-06-27 03:25:31 +0000424
niklase@google.com470e71d2011-07-07 08:21:25 +0000425 _outputFileRecorderPtr->RegisterModuleFileCallback(this);
426 _outputFileRecording = true;
427
428 return 0;
429}
430
431int OutputMixer::StopRecordingPlayout()
432{
433 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
434 "OutputMixer::StopRecordingPlayout()");
435
436 if (!_outputFileRecording)
437 {
438 WEBRTC_TRACE(kTraceError, kTraceVoice, VoEId(_instanceId,-1),
439 "StopRecordingPlayout() file isnot recording");
440 return -1;
441 }
442
tommi31fc21f2016-01-21 10:37:37 -0800443 rtc::CritScope cs(&_fileCritSect);
niklase@google.com470e71d2011-07-07 08:21:25 +0000444
445 if (_outputFileRecorderPtr->StopRecording() != 0)
446 {
447 _engineStatisticsPtr->SetLastError(
448 VE_STOP_RECORDING_FAILED, kTraceError,
449 "StopRecording(), could not stop recording");
450 return -1;
451 }
452 _outputFileRecorderPtr->RegisterModuleFileCallback(NULL);
453 FileRecorder::DestroyFileRecorder(_outputFileRecorderPtr);
454 _outputFileRecorderPtr = NULL;
455 _outputFileRecording = false;
456
457 return 0;
458}
459
andrew@webrtc.org4ecea3e2012-06-27 03:25:31 +0000460int OutputMixer::GetMixedAudio(int sample_rate_hz,
Peter Kasting69558702016-01-12 16:26:35 -0800461 size_t num_channels,
andrew@webrtc.org4ecea3e2012-06-27 03:25:31 +0000462 AudioFrame* frame) {
Peter Kasting69558702016-01-12 16:26:35 -0800463 WEBRTC_TRACE(
464 kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
465 "OutputMixer::GetMixedAudio(sample_rate_hz=%d, num_channels=%" PRIuS ")",
466 sample_rate_hz, num_channels);
niklase@google.com470e71d2011-07-07 08:21:25 +0000467
andrew@webrtc.org4ecea3e2012-06-27 03:25:31 +0000468 // --- Record playout if enabled
469 {
tommi31fc21f2016-01-21 10:37:37 -0800470 rtc::CritScope cs(&_fileCritSect);
andrew@webrtc.org4ecea3e2012-06-27 03:25:31 +0000471 if (_outputFileRecording && _outputFileRecorderPtr)
472 _outputFileRecorderPtr->RecordAudioToFile(_audioFrame);
473 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000474
andrew@webrtc.org4ecea3e2012-06-27 03:25:31 +0000475 frame->num_channels_ = num_channels;
476 frame->sample_rate_hz_ = sample_rate_hz;
477 // TODO(andrew): Ideally the downmixing would occur much earlier, in
478 // AudioCodingModule.
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000479 RemixAndResample(_audioFrame, &resampler_, frame);
480 return 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000481}
482
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000483int32_t
xians@webrtc.org56925312014-04-14 10:50:37 +0000484OutputMixer::DoOperationsOnCombinedSignal(bool feed_data_to_apm)
niklase@google.com470e71d2011-07-07 08:21:25 +0000485{
andrew@webrtc.org63a50982012-05-02 23:56:37 +0000486 if (_audioFrame.sample_rate_hz_ != _mixingFrequencyHz)
niklase@google.com470e71d2011-07-07 08:21:25 +0000487 {
488 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
489 "OutputMixer::DoOperationsOnCombinedSignal() => "
andrew@webrtc.org63a50982012-05-02 23:56:37 +0000490 "mixing frequency = %d", _audioFrame.sample_rate_hz_);
491 _mixingFrequencyHz = _audioFrame.sample_rate_hz_;
niklase@google.com470e71d2011-07-07 08:21:25 +0000492 }
493
494 // --- Insert inband Dtmf tone
495 if (_dtmfGenerator.IsAddingTone())
496 {
497 InsertInbandDtmfTone();
498 }
499
500 // Scale left and/or right channel(s) if balance is active
501 if (_panLeft != 1.0 || _panRight != 1.0)
502 {
andrew@webrtc.org63a50982012-05-02 23:56:37 +0000503 if (_audioFrame.num_channels_ == 1)
niklase@google.com470e71d2011-07-07 08:21:25 +0000504 {
andrew@webrtc.org4ecea3e2012-06-27 03:25:31 +0000505 AudioFrameOperations::MonoToStereo(&_audioFrame);
niklase@google.com470e71d2011-07-07 08:21:25 +0000506 }
507 else
508 {
509 // Pure stereo mode (we are receiving a stereo signal).
510 }
511
andrew@webrtc.org63a50982012-05-02 23:56:37 +0000512 assert(_audioFrame.num_channels_ == 2);
niklase@google.com470e71d2011-07-07 08:21:25 +0000513 AudioFrameOperations::Scale(_panLeft, _panRight, _audioFrame);
514 }
515
516 // --- Far-end Voice Quality Enhancement (AudioProcessing Module)
peah66085be2015-12-16 02:02:20 -0800517 if (feed_data_to_apm) {
518 // Convert from mixing to AudioProcessing sample rate, similarly to how it
519 // is done on the send side. Downmix to mono.
520 AudioFrame frame;
521 frame.num_channels_ = 1;
522 frame.sample_rate_hz_ = _audioProcessingModulePtr->input_sample_rate_hz();
523 RemixAndResample(_audioFrame, &audioproc_resampler_, &frame);
524
525 if (_audioProcessingModulePtr->AnalyzeReverseStream(&frame) != 0) {
526 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
527 "AudioProcessingModule::AnalyzeReverseStream() => error");
528 RTC_DCHECK(false);
529 }
530 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000531
532 // --- External media processing
niklase@google.com470e71d2011-07-07 08:21:25 +0000533 {
tommi31fc21f2016-01-21 10:37:37 -0800534 rtc::CritScope cs(&_callbackCritSect);
henrika@webrtc.org944cbeb2014-03-18 10:32:33 +0000535 if (_externalMedia)
niklase@google.com470e71d2011-07-07 08:21:25 +0000536 {
henrika@webrtc.org944cbeb2014-03-18 10:32:33 +0000537 const bool is_stereo = (_audioFrame.num_channels_ == 2);
538 if (_externalMediaCallbackPtr)
539 {
540 _externalMediaCallbackPtr->Process(
541 -1,
542 kPlaybackAllChannelsMixed,
543 (int16_t*)_audioFrame.data_,
544 _audioFrame.samples_per_channel_,
545 _audioFrame.sample_rate_hz_,
546 is_stereo);
547 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000548 }
549 }
550
551 // --- Measure audio level (0-9) for the combined signal
552 _audioLevel.ComputeLevel(_audioFrame);
553
554 return 0;
555}
556
557// ----------------------------------------------------------------------------
andrew@webrtc.org4ecea3e2012-06-27 03:25:31 +0000558// Private methods
niklase@google.com470e71d2011-07-07 08:21:25 +0000559// ----------------------------------------------------------------------------
560
niklase@google.com470e71d2011-07-07 08:21:25 +0000561int
562OutputMixer::InsertInbandDtmfTone()
563{
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000564 uint16_t sampleRate(0);
niklase@google.com470e71d2011-07-07 08:21:25 +0000565 _dtmfGenerator.GetSampleRate(sampleRate);
andrew@webrtc.org63a50982012-05-02 23:56:37 +0000566 if (sampleRate != _audioFrame.sample_rate_hz_)
niklase@google.com470e71d2011-07-07 08:21:25 +0000567 {
568 // Update sample rate of Dtmf tone since the mixing frequency changed.
569 _dtmfGenerator.SetSampleRate(
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000570 (uint16_t)(_audioFrame.sample_rate_hz_));
niklase@google.com470e71d2011-07-07 08:21:25 +0000571 // Reset the tone to be added taking the new sample rate into account.
572 _dtmfGenerator.ResetTone();
573 }
574
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000575 int16_t toneBuffer[320];
576 uint16_t toneSamples(0);
niklase@google.com470e71d2011-07-07 08:21:25 +0000577 if (_dtmfGenerator.Get10msTone(toneBuffer, toneSamples) == -1)
578 {
579 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
580 "OutputMixer::InsertInbandDtmfTone() inserting Dtmf"
581 "tone failed");
582 return -1;
583 }
584
585 // replace mixed audio with Dtmf tone
andrew@webrtc.org63a50982012-05-02 23:56:37 +0000586 if (_audioFrame.num_channels_ == 1)
niklase@google.com470e71d2011-07-07 08:21:25 +0000587 {
588 // mono
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000589 memcpy(_audioFrame.data_, toneBuffer, sizeof(int16_t)
niklase@google.com470e71d2011-07-07 08:21:25 +0000590 * toneSamples);
591 } else
592 {
593 // stereo
Peter Kastingdce40cf2015-08-24 14:52:23 -0700594 for (size_t i = 0; i < _audioFrame.samples_per_channel_; i++)
niklase@google.com470e71d2011-07-07 08:21:25 +0000595 {
andrew@webrtc.org63a50982012-05-02 23:56:37 +0000596 _audioFrame.data_[2 * i] = toneBuffer[i];
597 _audioFrame.data_[2 * i + 1] = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000598 }
599 }
andrew@webrtc.org63a50982012-05-02 23:56:37 +0000600 assert(_audioFrame.samples_per_channel_ == toneSamples);
niklase@google.com470e71d2011-07-07 08:21:25 +0000601
602 return 0;
603}
604
pbos@webrtc.orgd900e8b2013-07-03 15:12:26 +0000605} // namespace voe
pbos@webrtc.orgd900e8b2013-07-03 15:12:26 +0000606} // namespace webrtc