Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / third_party / webrtc / voice_engine / output_mixer.cc
1 /*
2  *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10
11 #include "webrtc/voice_engine/output_mixer.h"
12
13 #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/statistics.h"
20 #include "webrtc/voice_engine/utility.h"
21
22 namespace webrtc {
23 namespace voe {
24
25 void
26 OutputMixer::NewMixedAudio(int32_t id,
27                            const AudioFrame& generalAudioFrame,
28                            const AudioFrame** uniqueAudioFrames,
29                            uint32_t size)
30 {
31     WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
32                  "OutputMixer::NewMixedAudio(id=%d, size=%u)", id, size);
33
34     _audioFrame.CopyFrom(generalAudioFrame);
35     _audioFrame.id_ = id;
36 }
37
38 void OutputMixer::MixedParticipants(
39     int32_t id,
40     const ParticipantStatistics* participantStatistics,
41     uint32_t size)
42 {
43     WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
44                  "OutputMixer::MixedParticipants(id=%d, size=%u)", id, size);
45 }
46
47 void OutputMixer::VADPositiveParticipants(int32_t id,
48     const ParticipantStatistics* participantStatistics, uint32_t size)
49 {
50     WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
51                  "OutputMixer::VADPositiveParticipants(id=%d, size=%u)",
52                  id, size);
53 }
54
55 void OutputMixer::MixedAudioLevel(int32_t id, uint32_t level)
56 {
57     WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
58                  "OutputMixer::MixedAudioLevel(id=%d, level=%u)", id, level);
59 }
60
61 void OutputMixer::PlayNotification(int32_t id, uint32_t durationMs)
62 {
63     WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
64                  "OutputMixer::PlayNotification(id=%d, durationMs=%d)",
65                  id, durationMs);
66     // Not implement yet
67 }
68
69 void OutputMixer::RecordNotification(int32_t id,
70                                      uint32_t durationMs)
71 {
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
79 void OutputMixer::PlayFileEnded(int32_t id)
80 {
81     WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
82                  "OutputMixer::PlayFileEnded(id=%d)", id);
83
84     // not needed
85 }
86
87 void OutputMixer::RecordFileEnded(int32_t id)
88 {
89     WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
90                  "OutputMixer::RecordFileEnded(id=%d)", id);
91     assert(id == _instanceId);
92
93     CriticalSectionScoped cs(&_fileCritSect);
94     _outputFileRecording = false;
95     WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId,-1),
96                  "OutputMixer::RecordFileEnded() =>"
97                  "output file recorder module is shutdown");
98 }
99
100 int32_t
101 OutputMixer::Create(OutputMixer*& mixer, uint32_t instanceId)
102 {
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
116 OutputMixer::OutputMixer(uint32_t instanceId) :
117     _callbackCritSect(*CriticalSectionWrapper::CreateCriticalSection()),
118     _fileCritSect(*CriticalSectionWrapper::CreateCriticalSection()),
119     _mixerModule(*AudioConferenceMixer::Create(instanceId)),
120     _audioLevel(),
121     _dtmfGenerator(instanceId),
122     _instanceId(instanceId),
123     _externalMediaCallbackPtr(NULL),
124     _externalMedia(false),
125     _panLeft(1.0f),
126     _panRight(1.0f),
127     _mixingFrequencyHz(8000),
128     _outputFileRecorderPtr(NULL),
129     _outputFileRecording(false)
130 {
131     WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId,-1),
132                  "OutputMixer::OutputMixer() - ctor");
133
134     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     }
141
142     _dtmfGenerator.Init();
143 }
144
145 void
146 OutputMixer::Destroy(OutputMixer*& mixer)
147 {
148     if (mixer)
149     {
150         delete mixer;
151         mixer = NULL;
152     }
153 }
154
155 OutputMixer::~OutputMixer()
156 {
157     WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId,-1),
158                  "OutputMixer::~OutputMixer() - dtor");
159     if (_externalMedia)
160     {
161         DeRegisterExternalMediaProcessing();
162     }
163     {
164         CriticalSectionScoped cs(&_fileCritSect);
165         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
180 int32_t
181 OutputMixer::SetEngineInformation(voe::Statistics& engineStatistics)
182 {
183     WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
184                  "OutputMixer::SetEngineInformation()");
185     _engineStatisticsPtr = &engineStatistics;
186     return 0;
187 }
188
189 int32_t
190 OutputMixer::SetAudioProcessingModule(AudioProcessing* audioProcessingModule)
191 {
192     WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
193                  "OutputMixer::SetAudioProcessingModule("
194                  "audioProcessingModule=0x%x)", audioProcessingModule);
195     _audioProcessingModulePtr = audioProcessingModule;
196     return 0;
197 }
198
199 int OutputMixer::RegisterExternalMediaProcessing(
200     VoEMediaProcess& proccess_object)
201 {
202     WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
203                "OutputMixer::RegisterExternalMediaProcessing()");
204
205     CriticalSectionScoped cs(&_callbackCritSect);
206     _externalMediaCallbackPtr = &proccess_object;
207     _externalMedia = true;
208
209     return 0;
210 }
211
212 int OutputMixer::DeRegisterExternalMediaProcessing()
213 {
214     WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
215                  "OutputMixer::DeRegisterExternalMediaProcessing()");
216
217     CriticalSectionScoped cs(&_callbackCritSect);
218     _externalMedia = false;
219     _externalMediaCallbackPtr = NULL;
220
221     return 0;
222 }
223
224 int OutputMixer::PlayDtmfTone(uint8_t eventCode, int lengthMs,
225                               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
239 int OutputMixer::StartPlayingDtmfTone(uint8_t eventCode,
240                                       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
255 int OutputMixer::StopPlayingDtmfTone()
256 {
257     WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
258                  "OutputMixer::StopPlayingDtmfTone()");
259     return (_dtmfGenerator.StopTone());
260 }
261
262 int32_t
263 OutputMixer::SetMixabilityStatus(MixerParticipant& participant,
264                                  bool mixable)
265 {
266     return _mixerModule.SetMixabilityStatus(participant, mixable);
267 }
268
269 int32_t
270 OutputMixer::SetAnonymousMixabilityStatus(MixerParticipant& participant,
271                                           bool mixable)
272 {
273     return _mixerModule.SetAnonymousMixabilityStatus(participant,mixable);
274 }
275
276 int32_t
277 OutputMixer::MixActiveChannels()
278 {
279     return _mixerModule.Process();
280 }
281
282 int
283 OutputMixer::GetSpeechOutputLevel(uint32_t& level)
284 {
285     int8_t currentLevel = _audioLevel.Level();
286     level = static_cast<uint32_t> (currentLevel);
287     WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId,-1),
288                  "GetSpeechOutputLevel() => level=%u", level);
289     return 0;
290 }
291
292 int
293 OutputMixer::GetSpeechOutputLevelFullRange(uint32_t& level)
294 {
295     int16_t currentLevel = _audioLevel.LevelFullRange();
296     level = static_cast<uint32_t> (currentLevel);
297     WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId,-1),
298                  "GetSpeechOutputLevelFullRange() => level=%u", level);
299     return 0;
300 }
301
302 int
303 OutputMixer::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
312 int
313 OutputMixer::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
323 int 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;
337     const uint32_t notificationTime(0);
338     CodecInst dummyCodec={100,"L16",16000,320,1,320000};
339
340     if ((codecInst != NULL) &&
341       ((codecInst->channels < 1) || (codecInst->channels > 2)))
342     {
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
364     CriticalSectionScoped cs(&_fileCritSect);
365
366     // 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
404 int 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;
418     const uint32_t notificationTime(0);
419     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
444     CriticalSectionScoped cs(&_fileCritSect);
445
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,
470            "StartRecordingAudioFile() failed to start file recording");
471         _outputFileRecorderPtr->StopRecording();
472         FileRecorder::DestroyFileRecorder(_outputFileRecorderPtr);
473         _outputFileRecorderPtr = NULL;
474         return -1;
475     }
476
477     _outputFileRecorderPtr->RegisterModuleFileCallback(this);
478     _outputFileRecording = true;
479
480     return 0;
481 }
482
483 int 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
495     CriticalSectionScoped cs(&_fileCritSect);
496
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
512 int 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);
518
519   // --- Record playout if enabled
520   {
521     CriticalSectionScoped cs(&_fileCritSect);
522     if (_outputFileRecording && _outputFileRecorderPtr)
523       _outputFileRecorderPtr->RecordAudioToFile(_audioFrame);
524   }
525
526   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.
530   RemixAndResample(_audioFrame, &resampler_, frame);
531   return 0;
532 }
533
534 int32_t
535 OutputMixer::DoOperationsOnCombinedSignal(bool feed_data_to_apm)
536 {
537     if (_audioFrame.sample_rate_hz_ != _mixingFrequencyHz)
538     {
539         WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
540                      "OutputMixer::DoOperationsOnCombinedSignal() => "
541                      "mixing frequency = %d", _audioFrame.sample_rate_hz_);
542         _mixingFrequencyHz = _audioFrame.sample_rate_hz_;
543     }
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     {
554         if (_audioFrame.num_channels_ == 1)
555         {
556             AudioFrameOperations::MonoToStereo(&_audioFrame);
557         }
558         else
559         {
560             // Pure stereo mode (we are receiving a stereo signal).
561         }
562
563         assert(_audioFrame.num_channels_ == 2);
564         AudioFrameOperations::Scale(_panLeft, _panRight, _audioFrame);
565     }
566
567     // --- Far-end Voice Quality Enhancement (AudioProcessing Module)
568     if (feed_data_to_apm)
569       APMAnalyzeReverseStream();
570
571     // --- External media processing
572     {
573         CriticalSectionScoped cs(&_callbackCritSect);
574         if (_externalMedia)
575         {
576             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             }
587         }
588     }
589
590     // --- Measure audio level (0-9) for the combined signal
591     _audioLevel.ComputeLevel(_audioFrame);
592
593     return 0;
594 }
595
596 // ----------------------------------------------------------------------------
597 //                             Private methods
598 // ----------------------------------------------------------------------------
599
600 void 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->input_sample_rate_hz();
606   RemixAndResample(_audioFrame, &audioproc_resampler_, &frame);
607
608   if (_audioProcessingModulePtr->AnalyzeReverseStream(&frame) == -1) {
609     WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId,-1),
610                  "AudioProcessingModule::AnalyzeReverseStream() => error");
611   }
612 }
613
614 int
615 OutputMixer::InsertInbandDtmfTone()
616 {
617     uint16_t sampleRate(0);
618     _dtmfGenerator.GetSampleRate(sampleRate);
619     if (sampleRate != _audioFrame.sample_rate_hz_)
620     {
621         // Update sample rate of Dtmf tone since the mixing frequency changed.
622         _dtmfGenerator.SetSampleRate(
623             (uint16_t)(_audioFrame.sample_rate_hz_));
624         // Reset the tone to be added taking the new sample rate into account.
625         _dtmfGenerator.ResetTone();
626     }
627
628     int16_t toneBuffer[320];
629     uint16_t toneSamples(0);
630     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
639     if (_audioFrame.num_channels_ == 1)
640     {
641         // mono
642         memcpy(_audioFrame.data_, toneBuffer, sizeof(int16_t)
643             * toneSamples);
644     } else
645     {
646         // stereo
647         for (int i = 0; i < _audioFrame.samples_per_channel_; i++)
648         {
649             _audioFrame.data_[2 * i] = toneBuffer[i];
650             _audioFrame.data_[2 * i + 1] = 0;
651         }
652     }
653     assert(_audioFrame.samples_per_channel_ == toneSamples);
654
655     return 0;
656 }
657
658 }  // namespace voe
659 }  // namespace webrtc