blob: a8e4177932352ec11acc5abea63fa99facebda3b [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"
19#include "webrtc/voice_engine/output_mixer_internal.h"
20#include "webrtc/voice_engine/statistics.h"
niklase@google.com470e71d2011-07-07 08:21:25 +000021
22namespace webrtc {
23
24namespace voe {
25
26void
pbos@webrtc.org6141e132013-04-09 10:09:10 +000027OutputMixer::NewMixedAudio(const int32_t id,
niklase@google.com470e71d2011-07-07 08:21:25 +000028 const AudioFrame& generalAudioFrame,
29 const AudioFrame** uniqueAudioFrames,
pbos@webrtc.org6141e132013-04-09 10:09:10 +000030 const 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
39void OutputMixer::MixedParticipants(
pbos@webrtc.org6141e132013-04-09 10:09:10 +000040 const int32_t id,
niklase@google.com470e71d2011-07-07 08:21:25 +000041 const ParticipantStatistics* participantStatistics,
pbos@webrtc.org6141e132013-04-09 10:09:10 +000042 const uint32_t size)
niklase@google.com470e71d2011-07-07 08:21:25 +000043{
44 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
45 "OutputMixer::MixedParticipants(id=%d, size=%u)", id, size);
46}
47
pbos@webrtc.org6141e132013-04-09 10:09:10 +000048void OutputMixer::VADPositiveParticipants(const int32_t id,
49 const ParticipantStatistics* participantStatistics, const uint32_t size)
niklase@google.com470e71d2011-07-07 08:21:25 +000050{
51 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
52 "OutputMixer::VADPositiveParticipants(id=%d, size=%u)",
53 id, size);
54}
55
pbos@webrtc.org6141e132013-04-09 10:09:10 +000056void OutputMixer::MixedAudioLevel(const int32_t id, const uint32_t level)
niklase@google.com470e71d2011-07-07 08:21:25 +000057{
58 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
59 "OutputMixer::MixedAudioLevel(id=%d, level=%u)", id, level);
60}
61
pbos@webrtc.org6141e132013-04-09 10:09:10 +000062void OutputMixer::PlayNotification(const int32_t id, const uint32_t durationMs)
niklase@google.com470e71d2011-07-07 08:21:25 +000063{
64 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
65 "OutputMixer::PlayNotification(id=%d, durationMs=%d)",
66 id, durationMs);
67 // Not implement yet
68}
69
pbos@webrtc.org6141e132013-04-09 10:09:10 +000070void OutputMixer::RecordNotification(const int32_t id,
71 const uint32_t durationMs)
niklase@google.com470e71d2011-07-07 08:21:25 +000072{
73 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
74 "OutputMixer::RecordNotification(id=%d, durationMs=%d)",
75 id, durationMs);
76
77 // Not implement yet
78}
79
pbos@webrtc.org6141e132013-04-09 10:09:10 +000080void OutputMixer::PlayFileEnded(const int32_t id)
niklase@google.com470e71d2011-07-07 08:21:25 +000081{
82 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
83 "OutputMixer::PlayFileEnded(id=%d)", id);
84
85 // not needed
86}
87
pbos@webrtc.org6141e132013-04-09 10:09:10 +000088void OutputMixer::RecordFileEnded(const int32_t id)
niklase@google.com470e71d2011-07-07 08:21:25 +000089{
90 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
91 "OutputMixer::RecordFileEnded(id=%d)", id);
92 assert(id == _instanceId);
93
mflodman@webrtc.org9a065d12012-03-07 08:12:21 +000094 CriticalSectionScoped cs(&_fileCritSect);
niklase@google.com470e71d2011-07-07 08:21:25 +000095 _outputFileRecording = false;
96 WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId,-1),
97 "OutputMixer::RecordFileEnded() =>"
98 "output file recorder module is shutdown");
99}
100
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000101int32_t
102OutputMixer::Create(OutputMixer*& mixer, const uint32_t instanceId)
niklase@google.com470e71d2011-07-07 08:21:25 +0000103{
104 WEBRTC_TRACE(kTraceMemory, kTraceVoice, instanceId,
105 "OutputMixer::Create(instanceId=%d)", instanceId);
106 mixer = new OutputMixer(instanceId);
107 if (mixer == NULL)
108 {
109 WEBRTC_TRACE(kTraceMemory, kTraceVoice, instanceId,
110 "OutputMixer::Create() unable to allocate memory for"
111 "mixer");
112 return -1;
113 }
114 return 0;
115}
116
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000117OutputMixer::OutputMixer(const uint32_t instanceId) :
niklase@google.com470e71d2011-07-07 08:21:25 +0000118 _callbackCritSect(*CriticalSectionWrapper::CreateCriticalSection()),
119 _fileCritSect(*CriticalSectionWrapper::CreateCriticalSection()),
andrew@webrtc.orgc4f129f2011-11-10 03:41:22 +0000120 _mixerModule(*AudioConferenceMixer::Create(instanceId)),
niklase@google.com470e71d2011-07-07 08:21:25 +0000121 _audioLevel(),
xians@google.com22963ab2011-08-03 12:40:23 +0000122 _dtmfGenerator(instanceId),
123 _instanceId(instanceId),
124 _externalMediaCallbackPtr(NULL),
niklase@google.com470e71d2011-07-07 08:21:25 +0000125 _externalMedia(false),
126 _panLeft(1.0f),
127 _panRight(1.0f),
xians@google.com22963ab2011-08-03 12:40:23 +0000128 _mixingFrequencyHz(8000),
129 _outputFileRecorderPtr(NULL),
130 _outputFileRecording(false)
niklase@google.com470e71d2011-07-07 08:21:25 +0000131{
132 WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId,-1),
133 "OutputMixer::OutputMixer() - ctor");
andrew@webrtc.orgae1a58b2013-01-22 04:44:30 +0000134
niklase@google.com470e71d2011-07-07 08:21:25 +0000135 if ((_mixerModule.RegisterMixedStreamCallback(*this) == -1) ||
136 (_mixerModule.RegisterMixerStatusCallback(*this, 100) == -1))
137 {
138 WEBRTC_TRACE(kTraceError, kTraceVoice, VoEId(_instanceId,-1),
139 "OutputMixer::OutputMixer() failed to register mixer"
140 "callbacks");
141 }
andrew@webrtc.orgae1a58b2013-01-22 04:44:30 +0000142
niklase@google.com470e71d2011-07-07 08:21:25 +0000143 _dtmfGenerator.Init();
144}
145
146void
147OutputMixer::Destroy(OutputMixer*& mixer)
148{
149 if (mixer)
150 {
151 delete mixer;
152 mixer = NULL;
153 }
154}
andrew@webrtc.orgae1a58b2013-01-22 04:44:30 +0000155
niklase@google.com470e71d2011-07-07 08:21:25 +0000156OutputMixer::~OutputMixer()
157{
158 WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId,-1),
159 "OutputMixer::~OutputMixer() - dtor");
160 if (_externalMedia)
161 {
162 DeRegisterExternalMediaProcessing();
163 }
164 {
mflodman@webrtc.org9a065d12012-03-07 08:12:21 +0000165 CriticalSectionScoped cs(&_fileCritSect);
niklase@google.com470e71d2011-07-07 08:21:25 +0000166 if (_outputFileRecorderPtr)
167 {
168 _outputFileRecorderPtr->RegisterModuleFileCallback(NULL);
169 _outputFileRecorderPtr->StopRecording();
170 FileRecorder::DestroyFileRecorder(_outputFileRecorderPtr);
171 _outputFileRecorderPtr = NULL;
172 }
173 }
174 _mixerModule.UnRegisterMixerStatusCallback();
175 _mixerModule.UnRegisterMixedStreamCallback();
176 delete &_mixerModule;
177 delete &_callbackCritSect;
178 delete &_fileCritSect;
179}
180
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000181int32_t
niklase@google.com470e71d2011-07-07 08:21:25 +0000182OutputMixer::SetEngineInformation(voe::Statistics& engineStatistics)
183{
184 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
185 "OutputMixer::SetEngineInformation()");
186 _engineStatisticsPtr = &engineStatistics;
187 return 0;
188}
189
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000190int32_t
191OutputMixer::SetAudioProcessingModule(AudioProcessing* audioProcessingModule)
niklase@google.com470e71d2011-07-07 08:21:25 +0000192{
193 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
194 "OutputMixer::SetAudioProcessingModule("
195 "audioProcessingModule=0x%x)", audioProcessingModule);
196 _audioProcessingModulePtr = audioProcessingModule;
197 return 0;
198}
199
200int OutputMixer::RegisterExternalMediaProcessing(
201 VoEMediaProcess& proccess_object)
202{
203 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
204 "OutputMixer::RegisterExternalMediaProcessing()");
205
mflodman@webrtc.org9a065d12012-03-07 08:12:21 +0000206 CriticalSectionScoped cs(&_callbackCritSect);
niklase@google.com470e71d2011-07-07 08:21:25 +0000207 _externalMediaCallbackPtr = &proccess_object;
208 _externalMedia = true;
209
210 return 0;
211}
212
213int OutputMixer::DeRegisterExternalMediaProcessing()
214{
215 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
216 "OutputMixer::DeRegisterExternalMediaProcessing()");
217
mflodman@webrtc.org9a065d12012-03-07 08:12:21 +0000218 CriticalSectionScoped cs(&_callbackCritSect);
niklase@google.com470e71d2011-07-07 08:21:25 +0000219 _externalMedia = false;
220 _externalMediaCallbackPtr = NULL;
221
222 return 0;
223}
224
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000225int OutputMixer::PlayDtmfTone(uint8_t eventCode, int lengthMs,
niklase@google.com470e71d2011-07-07 08:21:25 +0000226 int attenuationDb)
227{
228 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
229 "OutputMixer::PlayDtmfTone()");
230 if (_dtmfGenerator.AddTone(eventCode, lengthMs, attenuationDb) != 0)
231 {
232 _engineStatisticsPtr->SetLastError(VE_STILL_PLAYING_PREV_DTMF,
233 kTraceError,
234 "OutputMixer::PlayDtmfTone()");
235 return -1;
236 }
237 return 0;
238}
239
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000240int OutputMixer::StartPlayingDtmfTone(uint8_t eventCode,
niklase@google.com470e71d2011-07-07 08:21:25 +0000241 int attenuationDb)
242{
243 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
244 "OutputMixer::StartPlayingDtmfTone()");
245 if (_dtmfGenerator.StartTone(eventCode, attenuationDb) != 0)
246 {
247 _engineStatisticsPtr->SetLastError(
248 VE_STILL_PLAYING_PREV_DTMF,
249 kTraceError,
250 "OutputMixer::StartPlayingDtmfTone())");
251 return -1;
252 }
253 return 0;
254}
255
256int OutputMixer::StopPlayingDtmfTone()
257{
258 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
259 "OutputMixer::StopPlayingDtmfTone()");
260 return (_dtmfGenerator.StopTone());
261}
262
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000263int32_t
niklase@google.com470e71d2011-07-07 08:21:25 +0000264OutputMixer::SetMixabilityStatus(MixerParticipant& participant,
265 const bool mixable)
266{
267 return _mixerModule.SetMixabilityStatus(participant, mixable);
268}
269
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000270int32_t
henrike@webrtc.org066f9e52011-10-28 23:15:47 +0000271OutputMixer::SetAnonymousMixabilityStatus(MixerParticipant& participant,
272 const bool mixable)
273{
274 return _mixerModule.SetAnonymousMixabilityStatus(participant,mixable);
275}
276
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000277int32_t
niklase@google.com470e71d2011-07-07 08:21:25 +0000278OutputMixer::MixActiveChannels()
279{
280 return _mixerModule.Process();
281}
282
283int
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000284OutputMixer::GetSpeechOutputLevel(uint32_t& level)
niklase@google.com470e71d2011-07-07 08:21:25 +0000285{
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000286 int8_t currentLevel = _audioLevel.Level();
287 level = static_cast<uint32_t> (currentLevel);
niklase@google.com470e71d2011-07-07 08:21:25 +0000288 WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId,-1),
289 "GetSpeechOutputLevel() => level=%u", level);
290 return 0;
291}
292
293int
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000294OutputMixer::GetSpeechOutputLevelFullRange(uint32_t& level)
niklase@google.com470e71d2011-07-07 08:21:25 +0000295{
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000296 int16_t currentLevel = _audioLevel.LevelFullRange();
297 level = static_cast<uint32_t> (currentLevel);
niklase@google.com470e71d2011-07-07 08:21:25 +0000298 WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId,-1),
299 "GetSpeechOutputLevelFullRange() => level=%u", level);
300 return 0;
301}
302
303int
304OutputMixer::SetOutputVolumePan(float left, float right)
305{
306 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
307 "OutputMixer::SetOutputVolumePan()");
308 _panLeft = left;
309 _panRight = right;
310 return 0;
311}
312
313int
314OutputMixer::GetOutputVolumePan(float& left, float& right)
315{
316 left = _panLeft;
317 right = _panRight;
318 WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId,-1),
319 "GetOutputVolumePan() => left=%2.1f, right=%2.1f",
320 left, right);
321 return 0;
322}
323
324int OutputMixer::StartRecordingPlayout(const char* fileName,
325 const CodecInst* codecInst)
326{
327 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
328 "OutputMixer::StartRecordingPlayout(fileName=%s)", fileName);
329
330 if (_outputFileRecording)
331 {
332 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId,-1),
333 "StartRecordingPlayout() is already recording");
334 return 0;
335 }
336
337 FileFormats format;
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000338 const uint32_t notificationTime(0);
niklase@google.com470e71d2011-07-07 08:21:25 +0000339 CodecInst dummyCodec={100,"L16",16000,320,1,320000};
340
niklas.enbom@webrtc.org40197d72012-03-26 08:45:47 +0000341 if ((codecInst != NULL) &&
342 ((codecInst->channels < 1) || (codecInst->channels > 2)))
niklase@google.com470e71d2011-07-07 08:21:25 +0000343 {
344 _engineStatisticsPtr->SetLastError(
345 VE_BAD_ARGUMENT, kTraceError,
346 "StartRecordingPlayout() invalid compression");
347 return(-1);
348 }
349 if(codecInst == NULL)
350 {
351 format = kFileFormatPcm16kHzFile;
352 codecInst=&dummyCodec;
353 }
354 else if((STR_CASE_CMP(codecInst->plname,"L16") == 0) ||
355 (STR_CASE_CMP(codecInst->plname,"PCMU") == 0) ||
356 (STR_CASE_CMP(codecInst->plname,"PCMA") == 0))
357 {
358 format = kFileFormatWavFile;
359 }
360 else
361 {
362 format = kFileFormatCompressedFile;
363 }
364
mflodman@webrtc.org9a065d12012-03-07 08:12:21 +0000365 CriticalSectionScoped cs(&_fileCritSect);
andrew@webrtc.orgae1a58b2013-01-22 04:44:30 +0000366
niklase@google.com470e71d2011-07-07 08:21:25 +0000367 // Destroy the old instance
368 if (_outputFileRecorderPtr)
369 {
370 _outputFileRecorderPtr->RegisterModuleFileCallback(NULL);
371 FileRecorder::DestroyFileRecorder(_outputFileRecorderPtr);
372 _outputFileRecorderPtr = NULL;
373 }
374
375 _outputFileRecorderPtr = FileRecorder::CreateFileRecorder(
376 _instanceId,
377 (const FileFormats)format);
378 if (_outputFileRecorderPtr == NULL)
379 {
380 _engineStatisticsPtr->SetLastError(
381 VE_INVALID_ARGUMENT, kTraceError,
382 "StartRecordingPlayout() fileRecorder format isnot correct");
383 return -1;
384 }
385
386 if (_outputFileRecorderPtr->StartRecordingAudioFile(
387 fileName,
388 (const CodecInst&)*codecInst,
389 notificationTime) != 0)
390 {
391 _engineStatisticsPtr->SetLastError(
392 VE_BAD_FILE, kTraceError,
393 "StartRecordingAudioFile() failed to start file recording");
394 _outputFileRecorderPtr->StopRecording();
395 FileRecorder::DestroyFileRecorder(_outputFileRecorderPtr);
396 _outputFileRecorderPtr = NULL;
397 return -1;
398 }
399 _outputFileRecorderPtr->RegisterModuleFileCallback(this);
400 _outputFileRecording = true;
401
402 return 0;
403}
404
405int OutputMixer::StartRecordingPlayout(OutStream* stream,
406 const CodecInst* codecInst)
407{
408 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
409 "OutputMixer::StartRecordingPlayout()");
410
411 if (_outputFileRecording)
412 {
413 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId,-1),
414 "StartRecordingPlayout() is already recording");
415 return 0;
416 }
417
418 FileFormats format;
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000419 const uint32_t notificationTime(0);
niklase@google.com470e71d2011-07-07 08:21:25 +0000420 CodecInst dummyCodec={100,"L16",16000,320,1,320000};
421
422 if (codecInst != NULL && codecInst->channels != 1)
423 {
424 _engineStatisticsPtr->SetLastError(
425 VE_BAD_ARGUMENT, kTraceError,
426 "StartRecordingPlayout() invalid compression");
427 return(-1);
428 }
429 if(codecInst == NULL)
430 {
431 format = kFileFormatPcm16kHzFile;
432 codecInst=&dummyCodec;
433 }
434 else if((STR_CASE_CMP(codecInst->plname,"L16") == 0) ||
435 (STR_CASE_CMP(codecInst->plname,"PCMU") == 0) ||
436 (STR_CASE_CMP(codecInst->plname,"PCMA") == 0))
437 {
438 format = kFileFormatWavFile;
439 }
440 else
441 {
442 format = kFileFormatCompressedFile;
443 }
444
mflodman@webrtc.org9a065d12012-03-07 08:12:21 +0000445 CriticalSectionScoped cs(&_fileCritSect);
niklase@google.com470e71d2011-07-07 08:21:25 +0000446
447 // Destroy the old instance
448 if (_outputFileRecorderPtr)
449 {
450 _outputFileRecorderPtr->RegisterModuleFileCallback(NULL);
451 FileRecorder::DestroyFileRecorder(_outputFileRecorderPtr);
452 _outputFileRecorderPtr = NULL;
453 }
454
455 _outputFileRecorderPtr = FileRecorder::CreateFileRecorder(
456 _instanceId,
457 (const FileFormats)format);
458 if (_outputFileRecorderPtr == NULL)
459 {
460 _engineStatisticsPtr->SetLastError(
461 VE_INVALID_ARGUMENT, kTraceError,
462 "StartRecordingPlayout() fileRecorder format isnot correct");
463 return -1;
464 }
465
466 if (_outputFileRecorderPtr->StartRecordingAudioFile(*stream,
467 *codecInst,
468 notificationTime) != 0)
469 {
470 _engineStatisticsPtr->SetLastError(VE_BAD_FILE, kTraceError,
andrew@webrtc.org4ecea3e2012-06-27 03:25:31 +0000471 "StartRecordingAudioFile() failed to start file recording");
niklase@google.com470e71d2011-07-07 08:21:25 +0000472 _outputFileRecorderPtr->StopRecording();
473 FileRecorder::DestroyFileRecorder(_outputFileRecorderPtr);
474 _outputFileRecorderPtr = NULL;
475 return -1;
476 }
andrew@webrtc.org4ecea3e2012-06-27 03:25:31 +0000477
niklase@google.com470e71d2011-07-07 08:21:25 +0000478 _outputFileRecorderPtr->RegisterModuleFileCallback(this);
479 _outputFileRecording = true;
480
481 return 0;
482}
483
484int OutputMixer::StopRecordingPlayout()
485{
486 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
487 "OutputMixer::StopRecordingPlayout()");
488
489 if (!_outputFileRecording)
490 {
491 WEBRTC_TRACE(kTraceError, kTraceVoice, VoEId(_instanceId,-1),
492 "StopRecordingPlayout() file isnot recording");
493 return -1;
494 }
495
mflodman@webrtc.org9a065d12012-03-07 08:12:21 +0000496 CriticalSectionScoped cs(&_fileCritSect);
niklase@google.com470e71d2011-07-07 08:21:25 +0000497
498 if (_outputFileRecorderPtr->StopRecording() != 0)
499 {
500 _engineStatisticsPtr->SetLastError(
501 VE_STOP_RECORDING_FAILED, kTraceError,
502 "StopRecording(), could not stop recording");
503 return -1;
504 }
505 _outputFileRecorderPtr->RegisterModuleFileCallback(NULL);
506 FileRecorder::DestroyFileRecorder(_outputFileRecorderPtr);
507 _outputFileRecorderPtr = NULL;
508 _outputFileRecording = false;
509
510 return 0;
511}
512
andrew@webrtc.org4ecea3e2012-06-27 03:25:31 +0000513int OutputMixer::GetMixedAudio(int sample_rate_hz,
514 int num_channels,
515 AudioFrame* frame) {
516 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
517 "OutputMixer::GetMixedAudio(sample_rate_hz=%d, num_channels=%d)",
518 sample_rate_hz, num_channels);
niklase@google.com470e71d2011-07-07 08:21:25 +0000519
andrew@webrtc.org4ecea3e2012-06-27 03:25:31 +0000520 // --- Record playout if enabled
521 {
522 CriticalSectionScoped cs(&_fileCritSect);
523 if (_outputFileRecording && _outputFileRecorderPtr)
524 _outputFileRecorderPtr->RecordAudioToFile(_audioFrame);
525 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000526
andrew@webrtc.org4ecea3e2012-06-27 03:25:31 +0000527 frame->num_channels_ = num_channels;
528 frame->sample_rate_hz_ = sample_rate_hz;
529 // TODO(andrew): Ideally the downmixing would occur much earlier, in
530 // AudioCodingModule.
andrew@webrtc.org50b2efe2013-04-29 17:27:29 +0000531 return RemixAndResample(_audioFrame, &resampler_, frame);
niklase@google.com470e71d2011-07-07 08:21:25 +0000532}
533
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000534int32_t
niklase@google.com470e71d2011-07-07 08:21:25 +0000535OutputMixer::DoOperationsOnCombinedSignal()
536{
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)
568
569 APMAnalyzeReverseStream();
570
571 // --- External media processing
572
573 if (_externalMedia)
574 {
mflodman@webrtc.org9a065d12012-03-07 08:12:21 +0000575 CriticalSectionScoped cs(&_callbackCritSect);
andrew@webrtc.org63a50982012-05-02 23:56:37 +0000576 const bool isStereo = (_audioFrame.num_channels_ == 2);
niklase@google.com470e71d2011-07-07 08:21:25 +0000577 if (_externalMediaCallbackPtr)
578 {
579 _externalMediaCallbackPtr->Process(
580 -1,
andrew@webrtc.org4ecea3e2012-06-27 03:25:31 +0000581 kPlaybackAllChannelsMixed,
pbos@webrtc.org6141e132013-04-09 10:09:10 +0000582 (int16_t*)_audioFrame.data_,
andrew@webrtc.org63a50982012-05-02 23:56:37 +0000583 _audioFrame.samples_per_channel_,
584 _audioFrame.sample_rate_hz_,
niklase@google.com470e71d2011-07-07 08:21:25 +0000585 isStereo);
586 }
587 }
588
589 // --- Measure audio level (0-9) for the combined signal
590 _audioLevel.ComputeLevel(_audioFrame);
591
592 return 0;
593}
594
595// ----------------------------------------------------------------------------
andrew@webrtc.org4ecea3e2012-06-27 03:25:31 +0000596// Private methods
niklase@google.com470e71d2011-07-07 08:21:25 +0000597// ----------------------------------------------------------------------------
598
andrew@webrtc.org4ecea3e2012-06-27 03:25:31 +0000599void OutputMixer::APMAnalyzeReverseStream() {
600 // Convert from mixing to AudioProcessing sample rate, determined by the send
601 // side. Downmix to mono.
602 AudioFrame frame;
603 frame.num_channels_ = 1;
604 frame.sample_rate_hz_ = _audioProcessingModulePtr->sample_rate_hz();
andrew@webrtc.org50b2efe2013-04-29 17:27:29 +0000605 if (RemixAndResample(_audioFrame, &audioproc_resampler_, &frame) == -1)
andrew@webrtc.org4ecea3e2012-06-27 03:25:31 +0000606 return;
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
658} // namespace voe
659
660} // namespace webrtc