blob: f5bf51e353bf8c1d8c9cdac1fb5c6b8b3e8c37d1 [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
andrew@webrtc.org50b2efe2013-04-29 17:27:29 +000013#include "webrtc/modules/audio_processing/include/audio_processing.h"
14#include "webrtc/modules/utility/interface/audio_frame_operations.h"
15#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
16#include "webrtc/system_wrappers/interface/file_wrapper.h"
17#include "webrtc/system_wrappers/interface/trace.h"
18#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
38void OutputMixer::MixedParticipants(
pbos@webrtc.org92135212013-05-14 08:31:39 +000039 int32_t id,
niklase@google.com470e71d2011-07-07 08:21:25 +000040 const ParticipantStatistics* participantStatistics,
pbos@webrtc.org92135212013-05-14 08:31:39 +000041 uint32_t size)
niklase@google.com470e71d2011-07-07 08:21:25 +000042{
43 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
44 "OutputMixer::MixedParticipants(id=%d, size=%u)", id, size);
45}
46
pbos@webrtc.org92135212013-05-14 08:31:39 +000047void OutputMixer::VADPositiveParticipants(int32_t id,
48 const ParticipantStatistics* participantStatistics, uint32_t size)
niklase@google.com470e71d2011-07-07 08:21:25 +000049{
50 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
51 "OutputMixer::VADPositiveParticipants(id=%d, size=%u)",
52 id, size);
53}
54
pbos@webrtc.org92135212013-05-14 08:31:39 +000055void OutputMixer::MixedAudioLevel(int32_t id, uint32_t level)
niklase@google.com470e71d2011-07-07 08:21:25 +000056{
57 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
58 "OutputMixer::MixedAudioLevel(id=%d, level=%u)", id, level);
59}
60
pbos@webrtc.org92135212013-05-14 08:31:39 +000061void OutputMixer::PlayNotification(int32_t id, uint32_t durationMs)
niklase@google.com470e71d2011-07-07 08:21:25 +000062{
63 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
64 "OutputMixer::PlayNotification(id=%d, durationMs=%d)",
65 id, durationMs);
66 // Not implement yet
67}
68
pbos@webrtc.org92135212013-05-14 08:31:39 +000069void OutputMixer::RecordNotification(int32_t id,
70 uint32_t durationMs)
niklase@google.com470e71d2011-07-07 08:21:25 +000071{
72 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
73 "OutputMixer::RecordNotification(id=%d, durationMs=%d)",
74 id, durationMs);
75
76 // Not implement yet
77}
78
pbos@webrtc.org92135212013-05-14 08:31:39 +000079void OutputMixer::PlayFileEnded(int32_t id)
niklase@google.com470e71d2011-07-07 08:21:25 +000080{
81 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
82 "OutputMixer::PlayFileEnded(id=%d)", id);
83
84 // not needed
85}
86
pbos@webrtc.org92135212013-05-14 08:31:39 +000087void OutputMixer::RecordFileEnded(int32_t id)
niklase@google.com470e71d2011-07-07 08:21:25 +000088{
89 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
90 "OutputMixer::RecordFileEnded(id=%d)", id);
91 assert(id == _instanceId);
92
mflodman@webrtc.org9a065d12012-03-07 08:12:21 +000093 CriticalSectionScoped cs(&_fileCritSect);
niklase@google.com470e71d2011-07-07 08:21:25 +000094 _outputFileRecording = false;
95 WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId,-1),
96 "OutputMixer::RecordFileEnded() =>"
97 "output file recorder module is shutdown");
98}
99
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000100int32_t
pbos@webrtc.org92135212013-05-14 08:31:39 +0000101OutputMixer::Create(OutputMixer*& mixer, uint32_t instanceId)
niklase@google.com470e71d2011-07-07 08:21:25 +0000102{
103 WEBRTC_TRACE(kTraceMemory, kTraceVoice, instanceId,
104 "OutputMixer::Create(instanceId=%d)", instanceId);
105 mixer = new OutputMixer(instanceId);
106 if (mixer == NULL)
107 {
108 WEBRTC_TRACE(kTraceMemory, kTraceVoice, instanceId,
109 "OutputMixer::Create() unable to allocate memory for"
110 "mixer");
111 return -1;
112 }
113 return 0;
114}
115
pbos@webrtc.org92135212013-05-14 08:31:39 +0000116OutputMixer::OutputMixer(uint32_t instanceId) :
niklase@google.com470e71d2011-07-07 08:21:25 +0000117 _callbackCritSect(*CriticalSectionWrapper::CreateCriticalSection()),
118 _fileCritSect(*CriticalSectionWrapper::CreateCriticalSection()),
andrew@webrtc.orgc4f129f2011-11-10 03:41:22 +0000119 _mixerModule(*AudioConferenceMixer::Create(instanceId)),
niklase@google.com470e71d2011-07-07 08:21:25 +0000120 _audioLevel(),
xians@google.com22963ab2011-08-03 12:40:23 +0000121 _dtmfGenerator(instanceId),
122 _instanceId(instanceId),
123 _externalMediaCallbackPtr(NULL),
niklase@google.com470e71d2011-07-07 08:21:25 +0000124 _externalMedia(false),
125 _panLeft(1.0f),
126 _panRight(1.0f),
xians@google.com22963ab2011-08-03 12:40:23 +0000127 _mixingFrequencyHz(8000),
128 _outputFileRecorderPtr(NULL),
129 _outputFileRecording(false)
niklase@google.com470e71d2011-07-07 08:21:25 +0000130{
131 WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId,-1),
132 "OutputMixer::OutputMixer() - ctor");
andrew@webrtc.orgae1a58b2013-01-22 04:44:30 +0000133
niklase@google.com470e71d2011-07-07 08:21:25 +0000134 if ((_mixerModule.RegisterMixedStreamCallback(*this) == -1) ||
135 (_mixerModule.RegisterMixerStatusCallback(*this, 100) == -1))
136 {
137 WEBRTC_TRACE(kTraceError, kTraceVoice, VoEId(_instanceId,-1),
138 "OutputMixer::OutputMixer() failed to register mixer"
139 "callbacks");
140 }
andrew@webrtc.orgae1a58b2013-01-22 04:44:30 +0000141
niklase@google.com470e71d2011-07-07 08:21:25 +0000142 _dtmfGenerator.Init();
143}
144
145void
146OutputMixer::Destroy(OutputMixer*& mixer)
147{
148 if (mixer)
149 {
150 delete mixer;
151 mixer = NULL;
152 }
153}
andrew@webrtc.orgae1a58b2013-01-22 04:44:30 +0000154
niklase@google.com470e71d2011-07-07 08:21:25 +0000155OutputMixer::~OutputMixer()
156{
157 WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId,-1),
158 "OutputMixer::~OutputMixer() - dtor");
159 if (_externalMedia)
160 {
161 DeRegisterExternalMediaProcessing();
162 }
163 {
mflodman@webrtc.org9a065d12012-03-07 08:12:21 +0000164 CriticalSectionScoped cs(&_fileCritSect);
niklase@google.com470e71d2011-07-07 08:21:25 +0000165 if (_outputFileRecorderPtr)
166 {
167 _outputFileRecorderPtr->RegisterModuleFileCallback(NULL);
168 _outputFileRecorderPtr->StopRecording();
169 FileRecorder::DestroyFileRecorder(_outputFileRecorderPtr);
170 _outputFileRecorderPtr = NULL;
171 }
172 }
173 _mixerModule.UnRegisterMixerStatusCallback();
174 _mixerModule.UnRegisterMixedStreamCallback();
175 delete &_mixerModule;
176 delete &_callbackCritSect;
177 delete &_fileCritSect;
178}
179
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000180int32_t
niklase@google.com470e71d2011-07-07 08:21:25 +0000181OutputMixer::SetEngineInformation(voe::Statistics& engineStatistics)
182{
183 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
184 "OutputMixer::SetEngineInformation()");
185 _engineStatisticsPtr = &engineStatistics;
186 return 0;
187}
188
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000189int32_t
190OutputMixer::SetAudioProcessingModule(AudioProcessing* audioProcessingModule)
niklase@google.com470e71d2011-07-07 08:21:25 +0000191{
192 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
193 "OutputMixer::SetAudioProcessingModule("
194 "audioProcessingModule=0x%x)", audioProcessingModule);
195 _audioProcessingModulePtr = audioProcessingModule;
196 return 0;
197}
198
199int OutputMixer::RegisterExternalMediaProcessing(
200 VoEMediaProcess& proccess_object)
201{
202 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
203 "OutputMixer::RegisterExternalMediaProcessing()");
204
mflodman@webrtc.org9a065d12012-03-07 08:12:21 +0000205 CriticalSectionScoped cs(&_callbackCritSect);
niklase@google.com470e71d2011-07-07 08:21:25 +0000206 _externalMediaCallbackPtr = &proccess_object;
207 _externalMedia = true;
208
209 return 0;
210}
211
212int OutputMixer::DeRegisterExternalMediaProcessing()
213{
214 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
215 "OutputMixer::DeRegisterExternalMediaProcessing()");
216
mflodman@webrtc.org9a065d12012-03-07 08:12:21 +0000217 CriticalSectionScoped cs(&_callbackCritSect);
niklase@google.com470e71d2011-07-07 08:21:25 +0000218 _externalMedia = false;
219 _externalMediaCallbackPtr = NULL;
220
221 return 0;
222}
223
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000224int OutputMixer::PlayDtmfTone(uint8_t eventCode, int lengthMs,
niklase@google.com470e71d2011-07-07 08:21:25 +0000225 int attenuationDb)
226{
227 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
228 "OutputMixer::PlayDtmfTone()");
229 if (_dtmfGenerator.AddTone(eventCode, lengthMs, attenuationDb) != 0)
230 {
231 _engineStatisticsPtr->SetLastError(VE_STILL_PLAYING_PREV_DTMF,
232 kTraceError,
233 "OutputMixer::PlayDtmfTone()");
234 return -1;
235 }
236 return 0;
237}
238
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000239int OutputMixer::StartPlayingDtmfTone(uint8_t eventCode,
niklase@google.com470e71d2011-07-07 08:21:25 +0000240 int attenuationDb)
241{
242 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
243 "OutputMixer::StartPlayingDtmfTone()");
244 if (_dtmfGenerator.StartTone(eventCode, attenuationDb) != 0)
245 {
246 _engineStatisticsPtr->SetLastError(
247 VE_STILL_PLAYING_PREV_DTMF,
248 kTraceError,
249 "OutputMixer::StartPlayingDtmfTone())");
250 return -1;
251 }
252 return 0;
253}
254
255int OutputMixer::StopPlayingDtmfTone()
256{
257 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
258 "OutputMixer::StopPlayingDtmfTone()");
259 return (_dtmfGenerator.StopTone());
260}
261
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000262int32_t
niklase@google.com470e71d2011-07-07 08:21:25 +0000263OutputMixer::SetMixabilityStatus(MixerParticipant& participant,
pbos@webrtc.org92135212013-05-14 08:31:39 +0000264 bool mixable)
niklase@google.com470e71d2011-07-07 08:21:25 +0000265{
266 return _mixerModule.SetMixabilityStatus(participant, mixable);
267}
268
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000269int32_t
henrike@webrtc.org066f9e52011-10-28 23:15:47 +0000270OutputMixer::SetAnonymousMixabilityStatus(MixerParticipant& participant,
pbos@webrtc.org92135212013-05-14 08:31:39 +0000271 bool mixable)
henrike@webrtc.org066f9e52011-10-28 23:15:47 +0000272{
273 return _mixerModule.SetAnonymousMixabilityStatus(participant,mixable);
274}
275
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000276int32_t
niklase@google.com470e71d2011-07-07 08:21:25 +0000277OutputMixer::MixActiveChannels()
278{
279 return _mixerModule.Process();
280}
281
282int
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000283OutputMixer::GetSpeechOutputLevel(uint32_t& level)
niklase@google.com470e71d2011-07-07 08:21:25 +0000284{
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000285 int8_t currentLevel = _audioLevel.Level();
286 level = static_cast<uint32_t> (currentLevel);
niklase@google.com470e71d2011-07-07 08:21:25 +0000287 WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId,-1),
288 "GetSpeechOutputLevel() => level=%u", level);
289 return 0;
290}
291
292int
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000293OutputMixer::GetSpeechOutputLevelFullRange(uint32_t& level)
niklase@google.com470e71d2011-07-07 08:21:25 +0000294{
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000295 int16_t currentLevel = _audioLevel.LevelFullRange();
296 level = static_cast<uint32_t> (currentLevel);
niklase@google.com470e71d2011-07-07 08:21:25 +0000297 WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId,-1),
298 "GetSpeechOutputLevelFullRange() => level=%u", level);
299 return 0;
300}
301
302int
303OutputMixer::SetOutputVolumePan(float left, float right)
304{
305 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
306 "OutputMixer::SetOutputVolumePan()");
307 _panLeft = left;
308 _panRight = right;
309 return 0;
310}
311
312int
313OutputMixer::GetOutputVolumePan(float& left, float& right)
314{
315 left = _panLeft;
316 right = _panRight;
317 WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId,-1),
318 "GetOutputVolumePan() => left=%2.1f, right=%2.1f",
319 left, right);
320 return 0;
321}
322
323int OutputMixer::StartRecordingPlayout(const char* fileName,
324 const CodecInst* codecInst)
325{
326 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
327 "OutputMixer::StartRecordingPlayout(fileName=%s)", fileName);
328
329 if (_outputFileRecording)
330 {
331 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId,-1),
332 "StartRecordingPlayout() is already recording");
333 return 0;
334 }
335
336 FileFormats format;
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000337 const uint32_t notificationTime(0);
niklase@google.com470e71d2011-07-07 08:21:25 +0000338 CodecInst dummyCodec={100,"L16",16000,320,1,320000};
339
niklas.enbom@webrtc.org40197d72012-03-26 08:45:47 +0000340 if ((codecInst != NULL) &&
341 ((codecInst->channels < 1) || (codecInst->channels > 2)))
niklase@google.com470e71d2011-07-07 08:21:25 +0000342 {
343 _engineStatisticsPtr->SetLastError(
344 VE_BAD_ARGUMENT, kTraceError,
345 "StartRecordingPlayout() invalid compression");
346 return(-1);
347 }
348 if(codecInst == NULL)
349 {
350 format = kFileFormatPcm16kHzFile;
351 codecInst=&dummyCodec;
352 }
353 else if((STR_CASE_CMP(codecInst->plname,"L16") == 0) ||
354 (STR_CASE_CMP(codecInst->plname,"PCMU") == 0) ||
355 (STR_CASE_CMP(codecInst->plname,"PCMA") == 0))
356 {
357 format = kFileFormatWavFile;
358 }
359 else
360 {
361 format = kFileFormatCompressedFile;
362 }
363
mflodman@webrtc.org9a065d12012-03-07 08:12:21 +0000364 CriticalSectionScoped cs(&_fileCritSect);
andrew@webrtc.orgae1a58b2013-01-22 04:44:30 +0000365
niklase@google.com470e71d2011-07-07 08:21:25 +0000366 // Destroy the old instance
367 if (_outputFileRecorderPtr)
368 {
369 _outputFileRecorderPtr->RegisterModuleFileCallback(NULL);
370 FileRecorder::DestroyFileRecorder(_outputFileRecorderPtr);
371 _outputFileRecorderPtr = NULL;
372 }
373
374 _outputFileRecorderPtr = FileRecorder::CreateFileRecorder(
375 _instanceId,
376 (const FileFormats)format);
377 if (_outputFileRecorderPtr == NULL)
378 {
379 _engineStatisticsPtr->SetLastError(
380 VE_INVALID_ARGUMENT, kTraceError,
381 "StartRecordingPlayout() fileRecorder format isnot correct");
382 return -1;
383 }
384
385 if (_outputFileRecorderPtr->StartRecordingAudioFile(
386 fileName,
387 (const CodecInst&)*codecInst,
388 notificationTime) != 0)
389 {
390 _engineStatisticsPtr->SetLastError(
391 VE_BAD_FILE, kTraceError,
392 "StartRecordingAudioFile() failed to start file recording");
393 _outputFileRecorderPtr->StopRecording();
394 FileRecorder::DestroyFileRecorder(_outputFileRecorderPtr);
395 _outputFileRecorderPtr = NULL;
396 return -1;
397 }
398 _outputFileRecorderPtr->RegisterModuleFileCallback(this);
399 _outputFileRecording = true;
400
401 return 0;
402}
403
404int OutputMixer::StartRecordingPlayout(OutStream* stream,
405 const CodecInst* codecInst)
406{
407 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
408 "OutputMixer::StartRecordingPlayout()");
409
410 if (_outputFileRecording)
411 {
412 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId,-1),
413 "StartRecordingPlayout() is already recording");
414 return 0;
415 }
416
417 FileFormats format;
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000418 const uint32_t notificationTime(0);
niklase@google.com470e71d2011-07-07 08:21:25 +0000419 CodecInst dummyCodec={100,"L16",16000,320,1,320000};
420
421 if (codecInst != NULL && codecInst->channels != 1)
422 {
423 _engineStatisticsPtr->SetLastError(
424 VE_BAD_ARGUMENT, kTraceError,
425 "StartRecordingPlayout() invalid compression");
426 return(-1);
427 }
428 if(codecInst == NULL)
429 {
430 format = kFileFormatPcm16kHzFile;
431 codecInst=&dummyCodec;
432 }
433 else if((STR_CASE_CMP(codecInst->plname,"L16") == 0) ||
434 (STR_CASE_CMP(codecInst->plname,"PCMU") == 0) ||
435 (STR_CASE_CMP(codecInst->plname,"PCMA") == 0))
436 {
437 format = kFileFormatWavFile;
438 }
439 else
440 {
441 format = kFileFormatCompressedFile;
442 }
443
mflodman@webrtc.org9a065d12012-03-07 08:12:21 +0000444 CriticalSectionScoped cs(&_fileCritSect);
niklase@google.com470e71d2011-07-07 08:21:25 +0000445
446 // Destroy the old instance
447 if (_outputFileRecorderPtr)
448 {
449 _outputFileRecorderPtr->RegisterModuleFileCallback(NULL);
450 FileRecorder::DestroyFileRecorder(_outputFileRecorderPtr);
451 _outputFileRecorderPtr = NULL;
452 }
453
454 _outputFileRecorderPtr = FileRecorder::CreateFileRecorder(
455 _instanceId,
456 (const FileFormats)format);
457 if (_outputFileRecorderPtr == NULL)
458 {
459 _engineStatisticsPtr->SetLastError(
460 VE_INVALID_ARGUMENT, kTraceError,
461 "StartRecordingPlayout() fileRecorder format isnot correct");
462 return -1;
463 }
464
465 if (_outputFileRecorderPtr->StartRecordingAudioFile(*stream,
466 *codecInst,
467 notificationTime) != 0)
468 {
469 _engineStatisticsPtr->SetLastError(VE_BAD_FILE, kTraceError,
andrew@webrtc.org4ecea3e2012-06-27 03:25:31 +0000470 "StartRecordingAudioFile() failed to start file recording");
niklase@google.com470e71d2011-07-07 08:21:25 +0000471 _outputFileRecorderPtr->StopRecording();
472 FileRecorder::DestroyFileRecorder(_outputFileRecorderPtr);
473 _outputFileRecorderPtr = NULL;
474 return -1;
475 }
andrew@webrtc.org4ecea3e2012-06-27 03:25:31 +0000476
niklase@google.com470e71d2011-07-07 08:21:25 +0000477 _outputFileRecorderPtr->RegisterModuleFileCallback(this);
478 _outputFileRecording = true;
479
480 return 0;
481}
482
483int OutputMixer::StopRecordingPlayout()
484{
485 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
486 "OutputMixer::StopRecordingPlayout()");
487
488 if (!_outputFileRecording)
489 {
490 WEBRTC_TRACE(kTraceError, kTraceVoice, VoEId(_instanceId,-1),
491 "StopRecordingPlayout() file isnot recording");
492 return -1;
493 }
494
mflodman@webrtc.org9a065d12012-03-07 08:12:21 +0000495 CriticalSectionScoped cs(&_fileCritSect);
niklase@google.com470e71d2011-07-07 08:21:25 +0000496
497 if (_outputFileRecorderPtr->StopRecording() != 0)
498 {
499 _engineStatisticsPtr->SetLastError(
500 VE_STOP_RECORDING_FAILED, kTraceError,
501 "StopRecording(), could not stop recording");
502 return -1;
503 }
504 _outputFileRecorderPtr->RegisterModuleFileCallback(NULL);
505 FileRecorder::DestroyFileRecorder(_outputFileRecorderPtr);
506 _outputFileRecorderPtr = NULL;
507 _outputFileRecording = false;
508
509 return 0;
510}
511
andrew@webrtc.org4ecea3e2012-06-27 03:25:31 +0000512int OutputMixer::GetMixedAudio(int sample_rate_hz,
513 int num_channels,
514 AudioFrame* frame) {
515 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
516 "OutputMixer::GetMixedAudio(sample_rate_hz=%d, num_channels=%d)",
517 sample_rate_hz, num_channels);
niklase@google.com470e71d2011-07-07 08:21:25 +0000518
andrew@webrtc.org4ecea3e2012-06-27 03:25:31 +0000519 // --- Record playout if enabled
520 {
521 CriticalSectionScoped cs(&_fileCritSect);
522 if (_outputFileRecording && _outputFileRecorderPtr)
523 _outputFileRecorderPtr->RecordAudioToFile(_audioFrame);
524 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000525
andrew@webrtc.org4ecea3e2012-06-27 03:25:31 +0000526 frame->num_channels_ = num_channels;
527 frame->sample_rate_hz_ = sample_rate_hz;
528 // TODO(andrew): Ideally the downmixing would occur much earlier, in
529 // AudioCodingModule.
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000530 RemixAndResample(_audioFrame, &resampler_, frame);
531 return 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000532}
533
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000534int32_t
xians@webrtc.org56925312014-04-14 10:50:37 +0000535OutputMixer::DoOperationsOnCombinedSignal(bool feed_data_to_apm)
niklase@google.com470e71d2011-07-07 08:21:25 +0000536{
andrew@webrtc.org63a50982012-05-02 23:56:37 +0000537 if (_audioFrame.sample_rate_hz_ != _mixingFrequencyHz)
niklase@google.com470e71d2011-07-07 08:21:25 +0000538 {
539 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
540 "OutputMixer::DoOperationsOnCombinedSignal() => "
andrew@webrtc.org63a50982012-05-02 23:56:37 +0000541 "mixing frequency = %d", _audioFrame.sample_rate_hz_);
542 _mixingFrequencyHz = _audioFrame.sample_rate_hz_;
niklase@google.com470e71d2011-07-07 08:21:25 +0000543 }
544
545 // --- Insert inband Dtmf tone
546 if (_dtmfGenerator.IsAddingTone())
547 {
548 InsertInbandDtmfTone();
549 }
550
551 // Scale left and/or right channel(s) if balance is active
552 if (_panLeft != 1.0 || _panRight != 1.0)
553 {
andrew@webrtc.org63a50982012-05-02 23:56:37 +0000554 if (_audioFrame.num_channels_ == 1)
niklase@google.com470e71d2011-07-07 08:21:25 +0000555 {
andrew@webrtc.org4ecea3e2012-06-27 03:25:31 +0000556 AudioFrameOperations::MonoToStereo(&_audioFrame);
niklase@google.com470e71d2011-07-07 08:21:25 +0000557 }
558 else
559 {
560 // Pure stereo mode (we are receiving a stereo signal).
561 }
562
andrew@webrtc.org63a50982012-05-02 23:56:37 +0000563 assert(_audioFrame.num_channels_ == 2);
niklase@google.com470e71d2011-07-07 08:21:25 +0000564 AudioFrameOperations::Scale(_panLeft, _panRight, _audioFrame);
565 }
566
567 // --- Far-end Voice Quality Enhancement (AudioProcessing Module)
xians@webrtc.org56925312014-04-14 10:50:37 +0000568 if (feed_data_to_apm)
569 APMAnalyzeReverseStream();
niklase@google.com470e71d2011-07-07 08:21:25 +0000570
571 // --- External media processing
niklase@google.com470e71d2011-07-07 08:21:25 +0000572 {
mflodman@webrtc.org9a065d12012-03-07 08:12:21 +0000573 CriticalSectionScoped cs(&_callbackCritSect);
henrika@webrtc.org944cbeb2014-03-18 10:32:33 +0000574 if (_externalMedia)
niklase@google.com470e71d2011-07-07 08:21:25 +0000575 {
henrika@webrtc.org944cbeb2014-03-18 10:32:33 +0000576 const bool is_stereo = (_audioFrame.num_channels_ == 2);
577 if (_externalMediaCallbackPtr)
578 {
579 _externalMediaCallbackPtr->Process(
580 -1,
581 kPlaybackAllChannelsMixed,
582 (int16_t*)_audioFrame.data_,
583 _audioFrame.samples_per_channel_,
584 _audioFrame.sample_rate_hz_,
585 is_stereo);
586 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000587 }
588 }
589
590 // --- Measure audio level (0-9) for the combined signal
591 _audioLevel.ComputeLevel(_audioFrame);
592
593 return 0;
594}
595
596// ----------------------------------------------------------------------------
andrew@webrtc.org4ecea3e2012-06-27 03:25:31 +0000597// Private methods
niklase@google.com470e71d2011-07-07 08:21:25 +0000598// ----------------------------------------------------------------------------
599
andrew@webrtc.org4ecea3e2012-06-27 03:25:31 +0000600void OutputMixer::APMAnalyzeReverseStream() {
601 // Convert from mixing to AudioProcessing sample rate, determined by the send
602 // side. Downmix to mono.
603 AudioFrame frame;
604 frame.num_channels_ = 1;
605 frame.sample_rate_hz_ = _audioProcessingModulePtr->sample_rate_hz();
andrew@webrtc.org40ee3d02014-04-03 21:56:01 +0000606 RemixAndResample(_audioFrame, &audioproc_resampler_, &frame);
niklase@google.com470e71d2011-07-07 08:21:25 +0000607
andrew@webrtc.org4ecea3e2012-06-27 03:25:31 +0000608 if (_audioProcessingModulePtr->AnalyzeReverseStream(&frame) == -1) {
609 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId,-1),
610 "AudioProcessingModule::AnalyzeReverseStream() => error");
611 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000612}
613
614int
615OutputMixer::InsertInbandDtmfTone()
616{
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000617 uint16_t sampleRate(0);
niklase@google.com470e71d2011-07-07 08:21:25 +0000618 _dtmfGenerator.GetSampleRate(sampleRate);
andrew@webrtc.org63a50982012-05-02 23:56:37 +0000619 if (sampleRate != _audioFrame.sample_rate_hz_)
niklase@google.com470e71d2011-07-07 08:21:25 +0000620 {
621 // Update sample rate of Dtmf tone since the mixing frequency changed.
622 _dtmfGenerator.SetSampleRate(
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000623 (uint16_t)(_audioFrame.sample_rate_hz_));
niklase@google.com470e71d2011-07-07 08:21:25 +0000624 // Reset the tone to be added taking the new sample rate into account.
625 _dtmfGenerator.ResetTone();
626 }
627
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000628 int16_t toneBuffer[320];
629 uint16_t toneSamples(0);
niklase@google.com470e71d2011-07-07 08:21:25 +0000630 if (_dtmfGenerator.Get10msTone(toneBuffer, toneSamples) == -1)
631 {
632 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
633 "OutputMixer::InsertInbandDtmfTone() inserting Dtmf"
634 "tone failed");
635 return -1;
636 }
637
638 // replace mixed audio with Dtmf tone
andrew@webrtc.org63a50982012-05-02 23:56:37 +0000639 if (_audioFrame.num_channels_ == 1)
niklase@google.com470e71d2011-07-07 08:21:25 +0000640 {
641 // mono
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000642 memcpy(_audioFrame.data_, toneBuffer, sizeof(int16_t)
niklase@google.com470e71d2011-07-07 08:21:25 +0000643 * toneSamples);
644 } else
645 {
646 // stereo
andrew@webrtc.org63a50982012-05-02 23:56:37 +0000647 for (int i = 0; i < _audioFrame.samples_per_channel_; i++)
niklase@google.com470e71d2011-07-07 08:21:25 +0000648 {
andrew@webrtc.org63a50982012-05-02 23:56:37 +0000649 _audioFrame.data_[2 * i] = toneBuffer[i];
650 _audioFrame.data_[2 * i + 1] = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000651 }
652 }
andrew@webrtc.org63a50982012-05-02 23:56:37 +0000653 assert(_audioFrame.samples_per_channel_ == toneSamples);
niklase@google.com470e71d2011-07-07 08:21:25 +0000654
655 return 0;
656}
657
pbos@webrtc.orgd900e8b2013-07-03 15:12:26 +0000658} // namespace voe
pbos@webrtc.orgd900e8b2013-07-03 15:12:26 +0000659} // namespace webrtc