blob: daf0d4a306506bad2785e1525f8d2dea791af8d0 [file] [log] [blame]
andrew@webrtc.orga7b57da2012-10-22 18:19:23 +00001/*
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 "output_mixer.h"
12
13#include "audio_processing.h"
14#include "audio_frame_operations.h"
15#include "critical_section_wrapper.h"
16#include "file_wrapper.h"
17#include "output_mixer_internal.h"
18#include "statistics.h"
19#include "trace.h"
20#include "voe_external_media.h"
21
22namespace webrtc {
23
24namespace voe {
25
26void
27OutputMixer::NewMixedAudio(const WebRtc_Word32 id,
28 const AudioFrame& generalAudioFrame,
29 const AudioFrame** uniqueAudioFrames,
30 const WebRtc_UWord32 size)
31{
32 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
33 "OutputMixer::NewMixedAudio(id=%d, size=%u)", id, size);
34
35 _audioFrame = generalAudioFrame;
36 _audioFrame.id_ = id;
37}
38
39void OutputMixer::MixedParticipants(
40 const WebRtc_Word32 id,
41 const ParticipantStatistics* participantStatistics,
42 const WebRtc_UWord32 size)
43{
44 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
45 "OutputMixer::MixedParticipants(id=%d, size=%u)", id, size);
46}
47
48void OutputMixer::VADPositiveParticipants(
49 const WebRtc_Word32 id,
50 const ParticipantStatistics* participantStatistics,
51 const WebRtc_UWord32 size)
52{
53 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
54 "OutputMixer::VADPositiveParticipants(id=%d, size=%u)",
55 id, size);
56}
57
58void OutputMixer::MixedAudioLevel(const WebRtc_Word32 id,
59 const WebRtc_UWord32 level)
60{
61 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
62 "OutputMixer::MixedAudioLevel(id=%d, level=%u)", id, level);
63}
64
65void OutputMixer::PlayNotification(const WebRtc_Word32 id,
66 const WebRtc_UWord32 durationMs)
67{
68 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
69 "OutputMixer::PlayNotification(id=%d, durationMs=%d)",
70 id, durationMs);
71 // Not implement yet
72}
73
74void OutputMixer::RecordNotification(const WebRtc_Word32 id,
75 const WebRtc_UWord32 durationMs)
76{
77 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
78 "OutputMixer::RecordNotification(id=%d, durationMs=%d)",
79 id, durationMs);
80
81 // Not implement yet
82}
83
84void OutputMixer::PlayFileEnded(const WebRtc_Word32 id)
85{
86 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
87 "OutputMixer::PlayFileEnded(id=%d)", id);
88
89 // not needed
90}
91
92void OutputMixer::RecordFileEnded(const WebRtc_Word32 id)
93{
94 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
95 "OutputMixer::RecordFileEnded(id=%d)", id);
96 assert(id == _instanceId);
97
98 CriticalSectionScoped cs(&_fileCritSect);
99 _outputFileRecording = false;
100 WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId,-1),
101 "OutputMixer::RecordFileEnded() =>"
102 "output file recorder module is shutdown");
103}
104
105WebRtc_Word32
106OutputMixer::Create(OutputMixer*& mixer, const WebRtc_UWord32 instanceId)
107{
108 WEBRTC_TRACE(kTraceMemory, kTraceVoice, instanceId,
109 "OutputMixer::Create(instanceId=%d)", instanceId);
110 mixer = new OutputMixer(instanceId);
111 if (mixer == NULL)
112 {
113 WEBRTC_TRACE(kTraceMemory, kTraceVoice, instanceId,
114 "OutputMixer::Create() unable to allocate memory for"
115 "mixer");
116 return -1;
117 }
118 return 0;
119}
120
121OutputMixer::OutputMixer(const WebRtc_UWord32 instanceId) :
122 _callbackCritSect(*CriticalSectionWrapper::CreateCriticalSection()),
123 _fileCritSect(*CriticalSectionWrapper::CreateCriticalSection()),
124 _mixerModule(*AudioConferenceMixer::Create(instanceId)),
125 _audioLevel(),
126 _dtmfGenerator(instanceId),
127 _instanceId(instanceId),
128 _externalMediaCallbackPtr(NULL),
129 _externalMedia(false),
130 _panLeft(1.0f),
131 _panRight(1.0f),
132 _mixingFrequencyHz(8000),
133 _outputFileRecorderPtr(NULL),
134 _outputFileRecording(false)
135{
136 WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId,-1),
137 "OutputMixer::OutputMixer() - ctor");
138
139 if ((_mixerModule.RegisterMixedStreamCallback(*this) == -1) ||
140 (_mixerModule.RegisterMixerStatusCallback(*this, 100) == -1))
141 {
142 WEBRTC_TRACE(kTraceError, kTraceVoice, VoEId(_instanceId,-1),
143 "OutputMixer::OutputMixer() failed to register mixer"
144 "callbacks");
145 }
146
147 _dtmfGenerator.Init();
148}
149
150void
151OutputMixer::Destroy(OutputMixer*& mixer)
152{
153 if (mixer)
154 {
155 delete mixer;
156 mixer = NULL;
157 }
158}
159
160OutputMixer::~OutputMixer()
161{
162 WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId,-1),
163 "OutputMixer::~OutputMixer() - dtor");
164 if (_externalMedia)
165 {
166 DeRegisterExternalMediaProcessing();
167 }
168 {
169 CriticalSectionScoped cs(&_fileCritSect);
170 if (_outputFileRecorderPtr)
171 {
172 _outputFileRecorderPtr->RegisterModuleFileCallback(NULL);
173 _outputFileRecorderPtr->StopRecording();
174 FileRecorder::DestroyFileRecorder(_outputFileRecorderPtr);
175 _outputFileRecorderPtr = NULL;
176 }
177 }
178 _mixerModule.UnRegisterMixerStatusCallback();
179 _mixerModule.UnRegisterMixedStreamCallback();
180 delete &_mixerModule;
181 delete &_callbackCritSect;
182 delete &_fileCritSect;
183}
184
185WebRtc_Word32
186OutputMixer::SetEngineInformation(voe::Statistics& engineStatistics)
187{
188 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
189 "OutputMixer::SetEngineInformation()");
190 _engineStatisticsPtr = &engineStatistics;
191 return 0;
192}
193
194WebRtc_Word32
195OutputMixer::SetAudioProcessingModule(
196 AudioProcessing* audioProcessingModule)
197{
198 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
199 "OutputMixer::SetAudioProcessingModule("
200 "audioProcessingModule=0x%x)", audioProcessingModule);
201 _audioProcessingModulePtr = audioProcessingModule;
202 return 0;
203}
204
205int OutputMixer::RegisterExternalMediaProcessing(
206 VoEMediaProcess& proccess_object)
207{
208 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
209 "OutputMixer::RegisterExternalMediaProcessing()");
210
211 CriticalSectionScoped cs(&_callbackCritSect);
212 _externalMediaCallbackPtr = &proccess_object;
213 _externalMedia = true;
214
215 return 0;
216}
217
218int OutputMixer::DeRegisterExternalMediaProcessing()
219{
220 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
221 "OutputMixer::DeRegisterExternalMediaProcessing()");
222
223 CriticalSectionScoped cs(&_callbackCritSect);
224 _externalMedia = false;
225 _externalMediaCallbackPtr = NULL;
226
227 return 0;
228}
229
230int OutputMixer::PlayDtmfTone(WebRtc_UWord8 eventCode, int lengthMs,
231 int attenuationDb)
232{
233 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
234 "OutputMixer::PlayDtmfTone()");
235 if (_dtmfGenerator.AddTone(eventCode, lengthMs, attenuationDb) != 0)
236 {
237 _engineStatisticsPtr->SetLastError(VE_STILL_PLAYING_PREV_DTMF,
238 kTraceError,
239 "OutputMixer::PlayDtmfTone()");
240 return -1;
241 }
242 return 0;
243}
244
245int OutputMixer::StartPlayingDtmfTone(WebRtc_UWord8 eventCode,
246 int attenuationDb)
247{
248 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
249 "OutputMixer::StartPlayingDtmfTone()");
250 if (_dtmfGenerator.StartTone(eventCode, attenuationDb) != 0)
251 {
252 _engineStatisticsPtr->SetLastError(
253 VE_STILL_PLAYING_PREV_DTMF,
254 kTraceError,
255 "OutputMixer::StartPlayingDtmfTone())");
256 return -1;
257 }
258 return 0;
259}
260
261int OutputMixer::StopPlayingDtmfTone()
262{
263 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
264 "OutputMixer::StopPlayingDtmfTone()");
265 return (_dtmfGenerator.StopTone());
266}
267
268WebRtc_Word32
269OutputMixer::SetMixabilityStatus(MixerParticipant& participant,
270 const bool mixable)
271{
272 return _mixerModule.SetMixabilityStatus(participant, mixable);
273}
274
275WebRtc_Word32
276OutputMixer::SetAnonymousMixabilityStatus(MixerParticipant& participant,
277 const bool mixable)
278{
279 return _mixerModule.SetAnonymousMixabilityStatus(participant,mixable);
280}
281
282WebRtc_Word32
283OutputMixer::MixActiveChannels()
284{
285 return _mixerModule.Process();
286}
287
288int
289OutputMixer::GetSpeechOutputLevel(WebRtc_UWord32& level)
290{
291 WebRtc_Word8 currentLevel = _audioLevel.Level();
292 level = static_cast<WebRtc_UWord32> (currentLevel);
293 WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId,-1),
294 "GetSpeechOutputLevel() => level=%u", level);
295 return 0;
296}
297
298int
299OutputMixer::GetSpeechOutputLevelFullRange(WebRtc_UWord32& level)
300{
301 WebRtc_Word16 currentLevel = _audioLevel.LevelFullRange();
302 level = static_cast<WebRtc_UWord32> (currentLevel);
303 WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId,-1),
304 "GetSpeechOutputLevelFullRange() => level=%u", level);
305 return 0;
306}
307
308int
309OutputMixer::SetOutputVolumePan(float left, float right)
310{
311 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
312 "OutputMixer::SetOutputVolumePan()");
313 _panLeft = left;
314 _panRight = right;
315 return 0;
316}
317
318int
319OutputMixer::GetOutputVolumePan(float& left, float& right)
320{
321 left = _panLeft;
322 right = _panRight;
323 WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId,-1),
324 "GetOutputVolumePan() => left=%2.1f, right=%2.1f",
325 left, right);
326 return 0;
327}
328
329int OutputMixer::StartRecordingPlayout(const char* fileName,
330 const CodecInst* codecInst)
331{
332 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
333 "OutputMixer::StartRecordingPlayout(fileName=%s)", fileName);
334
335 if (_outputFileRecording)
336 {
337 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId,-1),
338 "StartRecordingPlayout() is already recording");
339 return 0;
340 }
341
342 FileFormats format;
343 const WebRtc_UWord32 notificationTime(0);
344 CodecInst dummyCodec={100,"L16",16000,320,1,320000};
345
346 if ((codecInst != NULL) &&
347 ((codecInst->channels < 1) || (codecInst->channels > 2)))
348 {
349 _engineStatisticsPtr->SetLastError(
350 VE_BAD_ARGUMENT, kTraceError,
351 "StartRecordingPlayout() invalid compression");
352 return(-1);
353 }
354 if(codecInst == NULL)
355 {
356 format = kFileFormatPcm16kHzFile;
357 codecInst=&dummyCodec;
358 }
359 else if((STR_CASE_CMP(codecInst->plname,"L16") == 0) ||
360 (STR_CASE_CMP(codecInst->plname,"PCMU") == 0) ||
361 (STR_CASE_CMP(codecInst->plname,"PCMA") == 0))
362 {
363 format = kFileFormatWavFile;
364 }
365 else
366 {
367 format = kFileFormatCompressedFile;
368 }
369
370 CriticalSectionScoped cs(&_fileCritSect);
371
372 // Destroy the old instance
373 if (_outputFileRecorderPtr)
374 {
375 _outputFileRecorderPtr->RegisterModuleFileCallback(NULL);
376 FileRecorder::DestroyFileRecorder(_outputFileRecorderPtr);
377 _outputFileRecorderPtr = NULL;
378 }
379
380 _outputFileRecorderPtr = FileRecorder::CreateFileRecorder(
381 _instanceId,
382 (const FileFormats)format);
383 if (_outputFileRecorderPtr == NULL)
384 {
385 _engineStatisticsPtr->SetLastError(
386 VE_INVALID_ARGUMENT, kTraceError,
387 "StartRecordingPlayout() fileRecorder format isnot correct");
388 return -1;
389 }
390
391 if (_outputFileRecorderPtr->StartRecordingAudioFile(
392 fileName,
393 (const CodecInst&)*codecInst,
394 notificationTime) != 0)
395 {
396 _engineStatisticsPtr->SetLastError(
397 VE_BAD_FILE, kTraceError,
398 "StartRecordingAudioFile() failed to start file recording");
399 _outputFileRecorderPtr->StopRecording();
400 FileRecorder::DestroyFileRecorder(_outputFileRecorderPtr);
401 _outputFileRecorderPtr = NULL;
402 return -1;
403 }
404 _outputFileRecorderPtr->RegisterModuleFileCallback(this);
405 _outputFileRecording = true;
406
407 return 0;
408}
409
410int OutputMixer::StartRecordingPlayout(OutStream* stream,
411 const CodecInst* codecInst)
412{
413 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
414 "OutputMixer::StartRecordingPlayout()");
415
416 if (_outputFileRecording)
417 {
418 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId,-1),
419 "StartRecordingPlayout() is already recording");
420 return 0;
421 }
422
423 FileFormats format;
424 const WebRtc_UWord32 notificationTime(0);
425 CodecInst dummyCodec={100,"L16",16000,320,1,320000};
426
427 if (codecInst != NULL && codecInst->channels != 1)
428 {
429 _engineStatisticsPtr->SetLastError(
430 VE_BAD_ARGUMENT, kTraceError,
431 "StartRecordingPlayout() invalid compression");
432 return(-1);
433 }
434 if(codecInst == NULL)
435 {
436 format = kFileFormatPcm16kHzFile;
437 codecInst=&dummyCodec;
438 }
439 else if((STR_CASE_CMP(codecInst->plname,"L16") == 0) ||
440 (STR_CASE_CMP(codecInst->plname,"PCMU") == 0) ||
441 (STR_CASE_CMP(codecInst->plname,"PCMA") == 0))
442 {
443 format = kFileFormatWavFile;
444 }
445 else
446 {
447 format = kFileFormatCompressedFile;
448 }
449
450 CriticalSectionScoped cs(&_fileCritSect);
451
452 // Destroy the old instance
453 if (_outputFileRecorderPtr)
454 {
455 _outputFileRecorderPtr->RegisterModuleFileCallback(NULL);
456 FileRecorder::DestroyFileRecorder(_outputFileRecorderPtr);
457 _outputFileRecorderPtr = NULL;
458 }
459
460 _outputFileRecorderPtr = FileRecorder::CreateFileRecorder(
461 _instanceId,
462 (const FileFormats)format);
463 if (_outputFileRecorderPtr == NULL)
464 {
465 _engineStatisticsPtr->SetLastError(
466 VE_INVALID_ARGUMENT, kTraceError,
467 "StartRecordingPlayout() fileRecorder format isnot correct");
468 return -1;
469 }
470
471 if (_outputFileRecorderPtr->StartRecordingAudioFile(*stream,
472 *codecInst,
473 notificationTime) != 0)
474 {
475 _engineStatisticsPtr->SetLastError(VE_BAD_FILE, kTraceError,
476 "StartRecordingAudioFile() failed to start file recording");
477 _outputFileRecorderPtr->StopRecording();
478 FileRecorder::DestroyFileRecorder(_outputFileRecorderPtr);
479 _outputFileRecorderPtr = NULL;
480 return -1;
481 }
482
483 _outputFileRecorderPtr->RegisterModuleFileCallback(this);
484 _outputFileRecording = true;
485
486 return 0;
487}
488
489int OutputMixer::StopRecordingPlayout()
490{
491 WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,-1),
492 "OutputMixer::StopRecordingPlayout()");
493
494 if (!_outputFileRecording)
495 {
496 WEBRTC_TRACE(kTraceError, kTraceVoice, VoEId(_instanceId,-1),
497 "StopRecordingPlayout() file isnot recording");
498 return -1;
499 }
500
501 CriticalSectionScoped cs(&_fileCritSect);
502
503 if (_outputFileRecorderPtr->StopRecording() != 0)
504 {
505 _engineStatisticsPtr->SetLastError(
506 VE_STOP_RECORDING_FAILED, kTraceError,
507 "StopRecording(), could not stop recording");
508 return -1;
509 }
510 _outputFileRecorderPtr->RegisterModuleFileCallback(NULL);
511 FileRecorder::DestroyFileRecorder(_outputFileRecorderPtr);
512 _outputFileRecorderPtr = NULL;
513 _outputFileRecording = false;
514
515 return 0;
516}
517
518int OutputMixer::GetMixedAudio(int sample_rate_hz,
519 int num_channels,
520 AudioFrame* frame) {
521 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
522 "OutputMixer::GetMixedAudio(sample_rate_hz=%d, num_channels=%d)",
523 sample_rate_hz, num_channels);
524
525 // --- Record playout if enabled
526 {
527 CriticalSectionScoped cs(&_fileCritSect);
528 if (_outputFileRecording && _outputFileRecorderPtr)
529 _outputFileRecorderPtr->RecordAudioToFile(_audioFrame);
530 }
531
532 frame->num_channels_ = num_channels;
533 frame->sample_rate_hz_ = sample_rate_hz;
534 // TODO(andrew): Ideally the downmixing would occur much earlier, in
535 // AudioCodingModule.
536 return RemixAndResample(_audioFrame, &_resampler, frame);
537}
538
539WebRtc_Word32
540OutputMixer::DoOperationsOnCombinedSignal()
541{
542 if (_audioFrame.sample_rate_hz_ != _mixingFrequencyHz)
543 {
544 WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,-1),
545 "OutputMixer::DoOperationsOnCombinedSignal() => "
546 "mixing frequency = %d", _audioFrame.sample_rate_hz_);
547 _mixingFrequencyHz = _audioFrame.sample_rate_hz_;
548 }
549
550 // --- Insert inband Dtmf tone
551 if (_dtmfGenerator.IsAddingTone())
552 {
553 InsertInbandDtmfTone();
554 }
555
556 // Scale left and/or right channel(s) if balance is active
557 if (_panLeft != 1.0 || _panRight != 1.0)
558 {
559 if (_audioFrame.num_channels_ == 1)
560 {
561 AudioFrameOperations::MonoToStereo(&_audioFrame);
562 }
563 else
564 {
565 // Pure stereo mode (we are receiving a stereo signal).
566 }
567
568 assert(_audioFrame.num_channels_ == 2);
569 AudioFrameOperations::Scale(_panLeft, _panRight, _audioFrame);
570 }
571
572 // --- Far-end Voice Quality Enhancement (AudioProcessing Module)
573
574 APMAnalyzeReverseStream();
575
576 // --- External media processing
577
578 if (_externalMedia)
579 {
580 CriticalSectionScoped cs(&_callbackCritSect);
581 const bool isStereo = (_audioFrame.num_channels_ == 2);
582 if (_externalMediaCallbackPtr)
583 {
584 _externalMediaCallbackPtr->Process(
585 -1,
586 kPlaybackAllChannelsMixed,
587 (WebRtc_Word16*)_audioFrame.data_,
588 _audioFrame.samples_per_channel_,
589 _audioFrame.sample_rate_hz_,
590 isStereo);
591 }
592 }
593
594 // --- Measure audio level (0-9) for the combined signal
595 _audioLevel.ComputeLevel(_audioFrame);
596
597 return 0;
598}
599
600// ----------------------------------------------------------------------------
601// Private methods
602// ----------------------------------------------------------------------------
603
604void OutputMixer::APMAnalyzeReverseStream() {
605 // Convert from mixing to AudioProcessing sample rate, determined by the send
606 // side. Downmix to mono.
607 AudioFrame frame;
608 frame.num_channels_ = 1;
609 frame.sample_rate_hz_ = _audioProcessingModulePtr->sample_rate_hz();
610 if (RemixAndResample(_audioFrame, &_apmResampler, &frame) == -1)
611 return;
612
613 if (_audioProcessingModulePtr->AnalyzeReverseStream(&frame) == -1) {
614 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId,-1),
615 "AudioProcessingModule::AnalyzeReverseStream() => error");
616 }
617}
618
619int
620OutputMixer::InsertInbandDtmfTone()
621{
622 WebRtc_UWord16 sampleRate(0);
623 _dtmfGenerator.GetSampleRate(sampleRate);
624 if (sampleRate != _audioFrame.sample_rate_hz_)
625 {
626 // Update sample rate of Dtmf tone since the mixing frequency changed.
627 _dtmfGenerator.SetSampleRate(
628 (WebRtc_UWord16)(_audioFrame.sample_rate_hz_));
629 // Reset the tone to be added taking the new sample rate into account.
630 _dtmfGenerator.ResetTone();
631 }
632
633 WebRtc_Word16 toneBuffer[320];
634 WebRtc_UWord16 toneSamples(0);
635 if (_dtmfGenerator.Get10msTone(toneBuffer, toneSamples) == -1)
636 {
637 WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
638 "OutputMixer::InsertInbandDtmfTone() inserting Dtmf"
639 "tone failed");
640 return -1;
641 }
642
643 // replace mixed audio with Dtmf tone
644 if (_audioFrame.num_channels_ == 1)
645 {
646 // mono
647 memcpy(_audioFrame.data_, toneBuffer, sizeof(WebRtc_Word16)
648 * toneSamples);
649 } else
650 {
651 // stereo
652 for (int i = 0; i < _audioFrame.samples_per_channel_; i++)
653 {
654 _audioFrame.data_[2 * i] = toneBuffer[i];
655 _audioFrame.data_[2 * i + 1] = 0;
656 }
657 }
658 assert(_audioFrame.samples_per_channel_ == toneSamples);
659
660 return 0;
661}
662
663} // namespace voe
664
665} // namespace webrtc