blob: 0dacf35eafabb3744f48f7762f4c6095ff7565d1 [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/critical_section_wrapper.h"
17#include "webrtc/system_wrappers/include/file_wrapper.h"
18#include "webrtc/system_wrappers/include/trace.h"
andrew@webrtc.org50b2efe2013-04-29 17:27:29 +000019#include "webrtc/voice_engine/include/voe_external_media.h"
andrew@webrtc.org50b2efe2013-04-29 17:27:29 +000020#include "webrtc/voice_engine/statistics.h"
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +000021#include "webrtc/voice_engine/utility.h"
niklase@google.com470e71d2011-07-07 08:21:25 +000022
23namespace webrtc {
niklase@google.com470e71d2011-07-07 08:21:25 +000024namespace voe {
25
26void
pbos@webrtc.org92135212013-05-14 08:31:39 +000027OutputMixer::NewMixedAudio(int32_t id,
niklase@google.com470e71d2011-07-07 08:21:25 +000028 const AudioFrame& generalAudioFrame,
29 const AudioFrame** uniqueAudioFrames,
pbos@webrtc.org92135212013-05-14 08:31:39 +000030 uint32_t size)
niklase@google.com470e71d2011-07-07 08:21:25 +000031{
32 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
33 "OutputMixer::NewMixedAudio(id=%d, size=%u)", id, size);
34
andrew@webrtc.orgae1a58b2013-01-22 04:44:30 +000035 _audioFrame.CopyFrom(generalAudioFrame);
andrew@webrtc.org63a50982012-05-02 23:56:37 +000036 _audioFrame.id_ = id;
niklase@google.com470e71d2011-07-07 08:21:25 +000037}
38
pbos@webrtc.org92135212013-05-14 08:31:39 +000039void OutputMixer::PlayNotification(int32_t id, uint32_t durationMs)
niklase@google.com470e71d2011-07-07 08:21:25 +000040{
41 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
42 "OutputMixer::PlayNotification(id=%d, durationMs=%d)",
43 id, durationMs);
44 // Not implement yet
45}
46
pbos@webrtc.org92135212013-05-14 08:31:39 +000047void OutputMixer::RecordNotification(int32_t id,
48 uint32_t durationMs)
niklase@google.com470e71d2011-07-07 08:21:25 +000049{
50 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
51 "OutputMixer::RecordNotification(id=%d, durationMs=%d)",
52 id, durationMs);
53
54 // Not implement yet
55}
56
pbos@webrtc.org92135212013-05-14 08:31:39 +000057void OutputMixer::PlayFileEnded(int32_t id)
niklase@google.com470e71d2011-07-07 08:21:25 +000058{
59 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
60 "OutputMixer::PlayFileEnded(id=%d)", id);
61
62 // not needed
63}
64
pbos@webrtc.org92135212013-05-14 08:31:39 +000065void OutputMixer::RecordFileEnded(int32_t id)
niklase@google.com470e71d2011-07-07 08:21:25 +000066{
67 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
68 "OutputMixer::RecordFileEnded(id=%d)", id);
69 assert(id == _instanceId);
70
mflodman@webrtc.org9a065d12012-03-07 08:12:21 +000071 CriticalSectionScoped cs(&_fileCritSect);
niklase@google.com470e71d2011-07-07 08:21:25 +000072 _outputFileRecording = false;
73 WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId,-1),
74 "OutputMixer::RecordFileEnded() =>"
75 "output file recorder module is shutdown");
76}
77
pbos@webrtc.org6141e132013-04-09 10:09:10 +000078int32_t
pbos@webrtc.org92135212013-05-14 08:31:39 +000079OutputMixer::Create(OutputMixer*& mixer, uint32_t instanceId)
niklase@google.com470e71d2011-07-07 08:21:25 +000080{
81 WEBRTC_TRACE(kTraceMemory, kTraceVoice, instanceId,
82 "OutputMixer::Create(instanceId=%d)", instanceId);
83 mixer = new OutputMixer(instanceId);
84 if (mixer == NULL)
85 {
86 WEBRTC_TRACE(kTraceMemory, kTraceVoice, instanceId,
87 "OutputMixer::Create() unable to allocate memory for"
88 "mixer");
89 return -1;
90 }
91 return 0;
92}
93
pbos@webrtc.org92135212013-05-14 08:31:39 +000094OutputMixer::OutputMixer(uint32_t instanceId) :
niklase@google.com470e71d2011-07-07 08:21:25 +000095 _callbackCritSect(*CriticalSectionWrapper::CreateCriticalSection()),
96 _fileCritSect(*CriticalSectionWrapper::CreateCriticalSection()),
andrew@webrtc.orgc4f129f2011-11-10 03:41:22 +000097 _mixerModule(*AudioConferenceMixer::Create(instanceId)),
niklase@google.com470e71d2011-07-07 08:21:25 +000098 _audioLevel(),
xians@google.com22963ab2011-08-03 12:40:23 +000099 _dtmfGenerator(instanceId),
100 _instanceId(instanceId),
101 _externalMediaCallbackPtr(NULL),
niklase@google.com470e71d2011-07-07 08:21:25 +0000102 _externalMedia(false),
103 _panLeft(1.0f),
104 _panRight(1.0f),
xians@google.com22963ab2011-08-03 12:40:23 +0000105 _mixingFrequencyHz(8000),
106 _outputFileRecorderPtr(NULL),
107 _outputFileRecording(false)
niklase@google.com470e71d2011-07-07 08:21:25 +0000108{
109 WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId,-1),
110 "OutputMixer::OutputMixer() - ctor");
andrew@webrtc.orgae1a58b2013-01-22 04:44:30 +0000111
minyuel0f4b3732015-08-31 16:04:32 +0200112 if (_mixerModule.RegisterMixedStreamCallback(this) == -1)
niklase@google.com470e71d2011-07-07 08:21:25 +0000113 {
114 WEBRTC_TRACE(kTraceError, kTraceVoice, VoEId(_instanceId,-1),
115 "OutputMixer::OutputMixer() failed to register mixer"
116 "callbacks");
117 }
andrew@webrtc.orgae1a58b2013-01-22 04:44:30 +0000118
niklase@google.com470e71d2011-07-07 08:21:25 +0000119 _dtmfGenerator.Init();
120}
121
122void
123OutputMixer::Destroy(OutputMixer*& mixer)
124{
125 if (mixer)
126 {
127 delete mixer;
128 mixer = NULL;
129 }
130}
andrew@webrtc.orgae1a58b2013-01-22 04:44:30 +0000131
niklase@google.com470e71d2011-07-07 08:21:25 +0000132OutputMixer::~OutputMixer()
133{
134 WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId,-1),
135 "OutputMixer::~OutputMixer() - dtor");
136 if (_externalMedia)
137 {
138 DeRegisterExternalMediaProcessing();
139 }
140 {
mflodman@webrtc.org9a065d12012-03-07 08:12:21 +0000141 CriticalSectionScoped cs(&_fileCritSect);
niklase@google.com470e71d2011-07-07 08:21:25 +0000142 if (_outputFileRecorderPtr)
143 {
144 _outputFileRecorderPtr->RegisterModuleFileCallback(NULL);
145 _outputFileRecorderPtr->StopRecording();
146 FileRecorder::DestroyFileRecorder(_outputFileRecorderPtr);
147 _outputFileRecorderPtr = NULL;
148 }
149 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000150 _mixerModule.UnRegisterMixedStreamCallback();
151 delete &_mixerModule;
152 delete &_callbackCritSect;
153 delete &_fileCritSect;
154}
155
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000156int32_t
niklase@google.com470e71d2011-07-07 08:21:25 +0000157OutputMixer::SetEngineInformation(voe::Statistics& engineStatistics)
158{
159 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
160 "OutputMixer::SetEngineInformation()");
161 _engineStatisticsPtr = &engineStatistics;
162 return 0;
163}
164
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000165int32_t
166OutputMixer::SetAudioProcessingModule(AudioProcessing* audioProcessingModule)
niklase@google.com470e71d2011-07-07 08:21:25 +0000167{
168 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
169 "OutputMixer::SetAudioProcessingModule("
170 "audioProcessingModule=0x%x)", audioProcessingModule);
171 _audioProcessingModulePtr = audioProcessingModule;
172 return 0;
173}
174
175int OutputMixer::RegisterExternalMediaProcessing(
176 VoEMediaProcess& proccess_object)
177{
178 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
179 "OutputMixer::RegisterExternalMediaProcessing()");
180
mflodman@webrtc.org9a065d12012-03-07 08:12:21 +0000181 CriticalSectionScoped cs(&_callbackCritSect);
niklase@google.com470e71d2011-07-07 08:21:25 +0000182 _externalMediaCallbackPtr = &proccess_object;
183 _externalMedia = true;
184
185 return 0;
186}
187
188int OutputMixer::DeRegisterExternalMediaProcessing()
189{
190 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
191 "OutputMixer::DeRegisterExternalMediaProcessing()");
192
mflodman@webrtc.org9a065d12012-03-07 08:12:21 +0000193 CriticalSectionScoped cs(&_callbackCritSect);
niklase@google.com470e71d2011-07-07 08:21:25 +0000194 _externalMedia = false;
195 _externalMediaCallbackPtr = NULL;
196
197 return 0;
198}
199
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000200int OutputMixer::PlayDtmfTone(uint8_t eventCode, int lengthMs,
niklase@google.com470e71d2011-07-07 08:21:25 +0000201 int attenuationDb)
202{
203 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
204 "OutputMixer::PlayDtmfTone()");
205 if (_dtmfGenerator.AddTone(eventCode, lengthMs, attenuationDb) != 0)
206 {
207 _engineStatisticsPtr->SetLastError(VE_STILL_PLAYING_PREV_DTMF,
208 kTraceError,
209 "OutputMixer::PlayDtmfTone()");
210 return -1;
211 }
212 return 0;
213}
214
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000215int32_t
niklase@google.com470e71d2011-07-07 08:21:25 +0000216OutputMixer::SetMixabilityStatus(MixerParticipant& participant,
pbos@webrtc.org92135212013-05-14 08:31:39 +0000217 bool mixable)
niklase@google.com470e71d2011-07-07 08:21:25 +0000218{
minyuel0f4b3732015-08-31 16:04:32 +0200219 return _mixerModule.SetMixabilityStatus(&participant, mixable);
niklase@google.com470e71d2011-07-07 08:21:25 +0000220}
221
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000222int32_t
henrike@webrtc.org066f9e52011-10-28 23:15:47 +0000223OutputMixer::SetAnonymousMixabilityStatus(MixerParticipant& participant,
pbos@webrtc.org92135212013-05-14 08:31:39 +0000224 bool mixable)
henrike@webrtc.org066f9e52011-10-28 23:15:47 +0000225{
minyuel0f4b3732015-08-31 16:04:32 +0200226 return _mixerModule.SetAnonymousMixabilityStatus(&participant, mixable);
henrike@webrtc.org066f9e52011-10-28 23:15:47 +0000227}
228
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000229int32_t
niklase@google.com470e71d2011-07-07 08:21:25 +0000230OutputMixer::MixActiveChannels()
231{
232 return _mixerModule.Process();
233}
234
235int
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000236OutputMixer::GetSpeechOutputLevel(uint32_t& level)
niklase@google.com470e71d2011-07-07 08:21:25 +0000237{
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000238 int8_t currentLevel = _audioLevel.Level();
239 level = static_cast<uint32_t> (currentLevel);
niklase@google.com470e71d2011-07-07 08:21:25 +0000240 WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId,-1),
241 "GetSpeechOutputLevel() => level=%u", level);
242 return 0;
243}
244
245int
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000246OutputMixer::GetSpeechOutputLevelFullRange(uint32_t& level)
niklase@google.com470e71d2011-07-07 08:21:25 +0000247{
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000248 int16_t currentLevel = _audioLevel.LevelFullRange();
249 level = static_cast<uint32_t> (currentLevel);
niklase@google.com470e71d2011-07-07 08:21:25 +0000250 WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId,-1),
251 "GetSpeechOutputLevelFullRange() => level=%u", level);
252 return 0;
253}
254
255int
256OutputMixer::SetOutputVolumePan(float left, float right)
257{
258 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
259 "OutputMixer::SetOutputVolumePan()");
260 _panLeft = left;
261 _panRight = right;
262 return 0;
263}
264
265int
266OutputMixer::GetOutputVolumePan(float& left, float& right)
267{
268 left = _panLeft;
269 right = _panRight;
270 WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId,-1),
271 "GetOutputVolumePan() => left=%2.1f, right=%2.1f",
272 left, right);
273 return 0;
274}
275
276int OutputMixer::StartRecordingPlayout(const char* fileName,
277 const CodecInst* codecInst)
278{
279 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
280 "OutputMixer::StartRecordingPlayout(fileName=%s)", fileName);
281
282 if (_outputFileRecording)
283 {
284 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId,-1),
285 "StartRecordingPlayout() is already recording");
286 return 0;
287 }
288
289 FileFormats format;
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000290 const uint32_t notificationTime(0);
niklase@google.com470e71d2011-07-07 08:21:25 +0000291 CodecInst dummyCodec={100,"L16",16000,320,1,320000};
292
niklas.enbom@webrtc.org40197d72012-03-26 08:45:47 +0000293 if ((codecInst != NULL) &&
294 ((codecInst->channels < 1) || (codecInst->channels > 2)))
niklase@google.com470e71d2011-07-07 08:21:25 +0000295 {
296 _engineStatisticsPtr->SetLastError(
297 VE_BAD_ARGUMENT, kTraceError,
298 "StartRecordingPlayout() invalid compression");
299 return(-1);
300 }
301 if(codecInst == NULL)
302 {
303 format = kFileFormatPcm16kHzFile;
304 codecInst=&dummyCodec;
305 }
306 else if((STR_CASE_CMP(codecInst->plname,"L16") == 0) ||
307 (STR_CASE_CMP(codecInst->plname,"PCMU") == 0) ||
308 (STR_CASE_CMP(codecInst->plname,"PCMA") == 0))
309 {
310 format = kFileFormatWavFile;
311 }
312 else
313 {
314 format = kFileFormatCompressedFile;
315 }
316
mflodman@webrtc.org9a065d12012-03-07 08:12:21 +0000317 CriticalSectionScoped cs(&_fileCritSect);
andrew@webrtc.orgae1a58b2013-01-22 04:44:30 +0000318
niklase@google.com470e71d2011-07-07 08:21:25 +0000319 // Destroy the old instance
320 if (_outputFileRecorderPtr)
321 {
322 _outputFileRecorderPtr->RegisterModuleFileCallback(NULL);
323 FileRecorder::DestroyFileRecorder(_outputFileRecorderPtr);
324 _outputFileRecorderPtr = NULL;
325 }
326
327 _outputFileRecorderPtr = FileRecorder::CreateFileRecorder(
328 _instanceId,
329 (const FileFormats)format);
330 if (_outputFileRecorderPtr == NULL)
331 {
332 _engineStatisticsPtr->SetLastError(
333 VE_INVALID_ARGUMENT, kTraceError,
334 "StartRecordingPlayout() fileRecorder format isnot correct");
335 return -1;
336 }
337
338 if (_outputFileRecorderPtr->StartRecordingAudioFile(
339 fileName,
340 (const CodecInst&)*codecInst,
341 notificationTime) != 0)
342 {
343 _engineStatisticsPtr->SetLastError(
344 VE_BAD_FILE, kTraceError,
345 "StartRecordingAudioFile() failed to start file recording");
346 _outputFileRecorderPtr->StopRecording();
347 FileRecorder::DestroyFileRecorder(_outputFileRecorderPtr);
348 _outputFileRecorderPtr = NULL;
349 return -1;
350 }
351 _outputFileRecorderPtr->RegisterModuleFileCallback(this);
352 _outputFileRecording = true;
353
354 return 0;
355}
356
357int OutputMixer::StartRecordingPlayout(OutStream* stream,
358 const CodecInst* codecInst)
359{
360 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
361 "OutputMixer::StartRecordingPlayout()");
362
363 if (_outputFileRecording)
364 {
365 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId,-1),
366 "StartRecordingPlayout() is already recording");
367 return 0;
368 }
369
370 FileFormats format;
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000371 const uint32_t notificationTime(0);
niklase@google.com470e71d2011-07-07 08:21:25 +0000372 CodecInst dummyCodec={100,"L16",16000,320,1,320000};
373
374 if (codecInst != NULL && codecInst->channels != 1)
375 {
376 _engineStatisticsPtr->SetLastError(
377 VE_BAD_ARGUMENT, kTraceError,
378 "StartRecordingPlayout() invalid compression");
379 return(-1);
380 }
381 if(codecInst == NULL)
382 {
383 format = kFileFormatPcm16kHzFile;
384 codecInst=&dummyCodec;
385 }
386 else if((STR_CASE_CMP(codecInst->plname,"L16") == 0) ||
387 (STR_CASE_CMP(codecInst->plname,"PCMU") == 0) ||
388 (STR_CASE_CMP(codecInst->plname,"PCMA") == 0))
389 {
390 format = kFileFormatWavFile;
391 }
392 else
393 {
394 format = kFileFormatCompressedFile;
395 }
396
mflodman@webrtc.org9a065d12012-03-07 08:12:21 +0000397 CriticalSectionScoped cs(&_fileCritSect);
niklase@google.com470e71d2011-07-07 08:21:25 +0000398
399 // Destroy the old instance
400 if (_outputFileRecorderPtr)
401 {
402 _outputFileRecorderPtr->RegisterModuleFileCallback(NULL);
403 FileRecorder::DestroyFileRecorder(_outputFileRecorderPtr);
404 _outputFileRecorderPtr = NULL;
405 }
406
407 _outputFileRecorderPtr = FileRecorder::CreateFileRecorder(
408 _instanceId,
409 (const FileFormats)format);
410 if (_outputFileRecorderPtr == NULL)
411 {
412 _engineStatisticsPtr->SetLastError(
413 VE_INVALID_ARGUMENT, kTraceError,
414 "StartRecordingPlayout() fileRecorder format isnot correct");
415 return -1;
416 }
417
418 if (_outputFileRecorderPtr->StartRecordingAudioFile(*stream,
419 *codecInst,
420 notificationTime) != 0)
421 {
422 _engineStatisticsPtr->SetLastError(VE_BAD_FILE, kTraceError,
andrew@webrtc.org4ecea3e2012-06-27 03:25:31 +0000423 "StartRecordingAudioFile() failed to start file recording");
niklase@google.com470e71d2011-07-07 08:21:25 +0000424 _outputFileRecorderPtr->StopRecording();
425 FileRecorder::DestroyFileRecorder(_outputFileRecorderPtr);
426 _outputFileRecorderPtr = NULL;
427 return -1;
428 }
andrew@webrtc.org4ecea3e2012-06-27 03:25:31 +0000429
niklase@google.com470e71d2011-07-07 08:21:25 +0000430 _outputFileRecorderPtr->RegisterModuleFileCallback(this);
431 _outputFileRecording = true;
432
433 return 0;
434}
435
436int OutputMixer::StopRecordingPlayout()
437{
438 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
439 "OutputMixer::StopRecordingPlayout()");
440
441 if (!_outputFileRecording)
442 {
443 WEBRTC_TRACE(kTraceError, kTraceVoice, VoEId(_instanceId,-1),
444 "StopRecordingPlayout() file isnot recording");
445 return -1;
446 }
447
mflodman@webrtc.org9a065d12012-03-07 08:12:21 +0000448 CriticalSectionScoped cs(&_fileCritSect);
niklase@google.com470e71d2011-07-07 08:21:25 +0000449
450 if (_outputFileRecorderPtr->StopRecording() != 0)
451 {
452 _engineStatisticsPtr->SetLastError(
453 VE_STOP_RECORDING_FAILED, kTraceError,
454 "StopRecording(), could not stop recording");
455 return -1;
456 }
457 _outputFileRecorderPtr->RegisterModuleFileCallback(NULL);
458 FileRecorder::DestroyFileRecorder(_outputFileRecorderPtr);
459 _outputFileRecorderPtr = NULL;
460 _outputFileRecording = false;
461
462 return 0;
463}
464
andrew@webrtc.org4ecea3e2012-06-27 03:25:31 +0000465int OutputMixer::GetMixedAudio(int sample_rate_hz,
Peter Kasting69558702016-01-12 16:26:35 -0800466 size_t num_channels,
andrew@webrtc.org4ecea3e2012-06-27 03:25:31 +0000467 AudioFrame* frame) {
Peter Kasting69558702016-01-12 16:26:35 -0800468 WEBRTC_TRACE(
469 kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
470 "OutputMixer::GetMixedAudio(sample_rate_hz=%d, num_channels=%" PRIuS ")",
471 sample_rate_hz, num_channels);
niklase@google.com470e71d2011-07-07 08:21:25 +0000472
andrew@webrtc.org4ecea3e2012-06-27 03:25:31 +0000473 // --- Record playout if enabled
474 {
475 CriticalSectionScoped cs(&_fileCritSect);
476 if (_outputFileRecording && _outputFileRecorderPtr)
477 _outputFileRecorderPtr->RecordAudioToFile(_audioFrame);
478 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000479
andrew@webrtc.org4ecea3e2012-06-27 03:25:31 +0000480 frame->num_channels_ = num_channels;
481 frame->sample_rate_hz_ = sample_rate_hz;
482 // TODO(andrew): Ideally the downmixing would occur much earlier, in
483 // AudioCodingModule.
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000484 RemixAndResample(_audioFrame, &resampler_, frame);
485 return 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000486}
487
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000488int32_t
xians@webrtc.org56925312014-04-14 10:50:37 +0000489OutputMixer::DoOperationsOnCombinedSignal(bool feed_data_to_apm)
niklase@google.com470e71d2011-07-07 08:21:25 +0000490{
andrew@webrtc.org63a50982012-05-02 23:56:37 +0000491 if (_audioFrame.sample_rate_hz_ != _mixingFrequencyHz)
niklase@google.com470e71d2011-07-07 08:21:25 +0000492 {
493 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
494 "OutputMixer::DoOperationsOnCombinedSignal() => "
andrew@webrtc.org63a50982012-05-02 23:56:37 +0000495 "mixing frequency = %d", _audioFrame.sample_rate_hz_);
496 _mixingFrequencyHz = _audioFrame.sample_rate_hz_;
niklase@google.com470e71d2011-07-07 08:21:25 +0000497 }
498
499 // --- Insert inband Dtmf tone
500 if (_dtmfGenerator.IsAddingTone())
501 {
502 InsertInbandDtmfTone();
503 }
504
505 // Scale left and/or right channel(s) if balance is active
506 if (_panLeft != 1.0 || _panRight != 1.0)
507 {
andrew@webrtc.org63a50982012-05-02 23:56:37 +0000508 if (_audioFrame.num_channels_ == 1)
niklase@google.com470e71d2011-07-07 08:21:25 +0000509 {
andrew@webrtc.org4ecea3e2012-06-27 03:25:31 +0000510 AudioFrameOperations::MonoToStereo(&_audioFrame);
niklase@google.com470e71d2011-07-07 08:21:25 +0000511 }
512 else
513 {
514 // Pure stereo mode (we are receiving a stereo signal).
515 }
516
andrew@webrtc.org63a50982012-05-02 23:56:37 +0000517 assert(_audioFrame.num_channels_ == 2);
niklase@google.com470e71d2011-07-07 08:21:25 +0000518 AudioFrameOperations::Scale(_panLeft, _panRight, _audioFrame);
519 }
520
521 // --- Far-end Voice Quality Enhancement (AudioProcessing Module)
peah66085be2015-12-16 02:02:20 -0800522 if (feed_data_to_apm) {
523 // Convert from mixing to AudioProcessing sample rate, similarly to how it
524 // is done on the send side. Downmix to mono.
525 AudioFrame frame;
526 frame.num_channels_ = 1;
527 frame.sample_rate_hz_ = _audioProcessingModulePtr->input_sample_rate_hz();
528 RemixAndResample(_audioFrame, &audioproc_resampler_, &frame);
529
530 if (_audioProcessingModulePtr->AnalyzeReverseStream(&frame) != 0) {
531 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
532 "AudioProcessingModule::AnalyzeReverseStream() => error");
533 RTC_DCHECK(false);
534 }
535 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000536
537 // --- External media processing
niklase@google.com470e71d2011-07-07 08:21:25 +0000538 {
mflodman@webrtc.org9a065d12012-03-07 08:12:21 +0000539 CriticalSectionScoped cs(&_callbackCritSect);
henrika@webrtc.org944cbeb2014-03-18 10:32:33 +0000540 if (_externalMedia)
niklase@google.com470e71d2011-07-07 08:21:25 +0000541 {
henrika@webrtc.org944cbeb2014-03-18 10:32:33 +0000542 const bool is_stereo = (_audioFrame.num_channels_ == 2);
543 if (_externalMediaCallbackPtr)
544 {
545 _externalMediaCallbackPtr->Process(
546 -1,
547 kPlaybackAllChannelsMixed,
548 (int16_t*)_audioFrame.data_,
549 _audioFrame.samples_per_channel_,
550 _audioFrame.sample_rate_hz_,
551 is_stereo);
552 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000553 }
554 }
555
556 // --- Measure audio level (0-9) for the combined signal
557 _audioLevel.ComputeLevel(_audioFrame);
558
559 return 0;
560}
561
562// ----------------------------------------------------------------------------
andrew@webrtc.org4ecea3e2012-06-27 03:25:31 +0000563// Private methods
niklase@google.com470e71d2011-07-07 08:21:25 +0000564// ----------------------------------------------------------------------------
565
niklase@google.com470e71d2011-07-07 08:21:25 +0000566int
567OutputMixer::InsertInbandDtmfTone()
568{
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000569 uint16_t sampleRate(0);
niklase@google.com470e71d2011-07-07 08:21:25 +0000570 _dtmfGenerator.GetSampleRate(sampleRate);
andrew@webrtc.org63a50982012-05-02 23:56:37 +0000571 if (sampleRate != _audioFrame.sample_rate_hz_)
niklase@google.com470e71d2011-07-07 08:21:25 +0000572 {
573 // Update sample rate of Dtmf tone since the mixing frequency changed.
574 _dtmfGenerator.SetSampleRate(
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000575 (uint16_t)(_audioFrame.sample_rate_hz_));
niklase@google.com470e71d2011-07-07 08:21:25 +0000576 // Reset the tone to be added taking the new sample rate into account.
577 _dtmfGenerator.ResetTone();
578 }
579
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000580 int16_t toneBuffer[320];
581 uint16_t toneSamples(0);
niklase@google.com470e71d2011-07-07 08:21:25 +0000582 if (_dtmfGenerator.Get10msTone(toneBuffer, toneSamples) == -1)
583 {
584 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
585 "OutputMixer::InsertInbandDtmfTone() inserting Dtmf"
586 "tone failed");
587 return -1;
588 }
589
590 // replace mixed audio with Dtmf tone
andrew@webrtc.org63a50982012-05-02 23:56:37 +0000591 if (_audioFrame.num_channels_ == 1)
niklase@google.com470e71d2011-07-07 08:21:25 +0000592 {
593 // mono
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000594 memcpy(_audioFrame.data_, toneBuffer, sizeof(int16_t)
niklase@google.com470e71d2011-07-07 08:21:25 +0000595 * toneSamples);
596 } else
597 {
598 // stereo
Peter Kastingdce40cf2015-08-24 14:52:23 -0700599 for (size_t i = 0; i < _audioFrame.samples_per_channel_; i++)
niklase@google.com470e71d2011-07-07 08:21:25 +0000600 {
andrew@webrtc.org63a50982012-05-02 23:56:37 +0000601 _audioFrame.data_[2 * i] = toneBuffer[i];
602 _audioFrame.data_[2 * i + 1] = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000603 }
604 }
andrew@webrtc.org63a50982012-05-02 23:56:37 +0000605 assert(_audioFrame.samples_per_channel_ == toneSamples);
niklase@google.com470e71d2011-07-07 08:21:25 +0000606
607 return 0;
608}
609
pbos@webrtc.orgd900e8b2013-07-03 15:12:26 +0000610} // namespace voe
pbos@webrtc.orgd900e8b2013-07-03 15:12:26 +0000611} // namespace webrtc