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/voe_hardware_impl.h"
12
13#include <assert.h>
14
15#include "webrtc/system_wrappers/include/critical_section_wrapper.h"
16#include "webrtc/system_wrappers/include/trace.h"
17#include "webrtc/voice_engine/include/voe_errors.h"
18#include "webrtc/voice_engine/voice_engine_impl.h"
19
20namespace webrtc {
21
22VoEHardware* VoEHardware::GetInterface(VoiceEngine* voiceEngine) {
23#ifndef WEBRTC_VOICE_ENGINE_HARDWARE_API
24  return NULL;
25#else
26  if (NULL == voiceEngine) {
27    return NULL;
28  }
29  VoiceEngineImpl* s = static_cast<VoiceEngineImpl*>(voiceEngine);
30  s->AddRef();
31  return s;
32#endif
33}
34
35#ifdef WEBRTC_VOICE_ENGINE_HARDWARE_API
36
37VoEHardwareImpl::VoEHardwareImpl(voe::SharedData* shared) : _shared(shared) {
38  WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_shared->instance_id(), -1),
39               "VoEHardwareImpl() - ctor");
40}
41
42VoEHardwareImpl::~VoEHardwareImpl() {
43  WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_shared->instance_id(), -1),
44               "~VoEHardwareImpl() - dtor");
45}
46
47int VoEHardwareImpl::SetAudioDeviceLayer(AudioLayers audioLayer) {
48  WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
49               "SetAudioDeviceLayer(audioLayer=%d)", audioLayer);
50
51  // Don't allow a change if VoE is initialized
52  if (_shared->statistics().Initialized()) {
53    _shared->SetLastError(VE_ALREADY_INITED, kTraceError);
54    return -1;
55  }
56
57  // Map to AudioDeviceModule::AudioLayer
58  AudioDeviceModule::AudioLayer wantedLayer(
59      AudioDeviceModule::kPlatformDefaultAudio);
60  switch (audioLayer) {
61    case kAudioPlatformDefault:
62      // already set above
63      break;
64    case kAudioWindowsCore:
65      wantedLayer = AudioDeviceModule::kWindowsCoreAudio;
66      break;
67    case kAudioWindowsWave:
68      wantedLayer = AudioDeviceModule::kWindowsWaveAudio;
69      break;
70    case kAudioLinuxAlsa:
71      wantedLayer = AudioDeviceModule::kLinuxAlsaAudio;
72      break;
73    case kAudioLinuxPulse:
74      wantedLayer = AudioDeviceModule::kLinuxPulseAudio;
75      break;
76  }
77
78  // Save the audio device layer for Init()
79  _shared->set_audio_device_layer(wantedLayer);
80
81  return 0;
82}
83
84int VoEHardwareImpl::GetAudioDeviceLayer(AudioLayers& audioLayer) {
85  // Can always be called regardless of VoE state
86
87  AudioDeviceModule::AudioLayer activeLayer(
88      AudioDeviceModule::kPlatformDefaultAudio);
89
90  if (_shared->audio_device()) {
91    // Get active audio layer from ADM
92    if (_shared->audio_device()->ActiveAudioLayer(&activeLayer) != 0) {
93      _shared->SetLastError(VE_UNDEFINED_SC_ERR, kTraceError,
94                            "  Audio Device error");
95      return -1;
96    }
97  } else {
98    // Return VoE's internal layer setting
99    activeLayer = _shared->audio_device_layer();
100  }
101
102  // Map to AudioLayers
103  switch (activeLayer) {
104    case AudioDeviceModule::kPlatformDefaultAudio:
105      audioLayer = kAudioPlatformDefault;
106      break;
107    case AudioDeviceModule::kWindowsCoreAudio:
108      audioLayer = kAudioWindowsCore;
109      break;
110    case AudioDeviceModule::kWindowsWaveAudio:
111      audioLayer = kAudioWindowsWave;
112      break;
113    case AudioDeviceModule::kLinuxAlsaAudio:
114      audioLayer = kAudioLinuxAlsa;
115      break;
116    case AudioDeviceModule::kLinuxPulseAudio:
117      audioLayer = kAudioLinuxPulse;
118      break;
119    default:
120      _shared->SetLastError(VE_UNDEFINED_SC_ERR, kTraceError,
121                            "  unknown audio layer");
122  }
123
124  return 0;
125}
126int VoEHardwareImpl::GetNumOfRecordingDevices(int& devices) {
127  if (!_shared->statistics().Initialized()) {
128    _shared->SetLastError(VE_NOT_INITED, kTraceError);
129    return -1;
130  }
131
132  devices = static_cast<int>(_shared->audio_device()->RecordingDevices());
133
134  return 0;
135}
136
137int VoEHardwareImpl::GetNumOfPlayoutDevices(int& devices) {
138  if (!_shared->statistics().Initialized()) {
139    _shared->SetLastError(VE_NOT_INITED, kTraceError);
140    return -1;
141  }
142
143  devices = static_cast<int>(_shared->audio_device()->PlayoutDevices());
144
145  return 0;
146}
147
148int VoEHardwareImpl::GetRecordingDeviceName(int index,
149                                            char strNameUTF8[128],
150                                            char strGuidUTF8[128]) {
151  if (!_shared->statistics().Initialized()) {
152    _shared->SetLastError(VE_NOT_INITED, kTraceError);
153    return -1;
154  }
155  if (strNameUTF8 == NULL) {
156    _shared->SetLastError(VE_INVALID_ARGUMENT, kTraceError,
157                          "GetRecordingDeviceName() invalid argument");
158    return -1;
159  }
160
161  // Note that strGuidUTF8 is allowed to be NULL
162
163  // Init len variable to length of supplied vectors
164  const uint16_t strLen = 128;
165
166  // Check if length has been changed in module
167  static_assert(strLen == kAdmMaxDeviceNameSize, "");
168  static_assert(strLen == kAdmMaxGuidSize, "");
169
170  char name[strLen];
171  char guid[strLen];
172
173  // Get names from module
174  if (_shared->audio_device()->RecordingDeviceName(index, name, guid) != 0) {
175    _shared->SetLastError(VE_CANNOT_RETRIEVE_DEVICE_NAME, kTraceError,
176                          "GetRecordingDeviceName() failed to get device name");
177    return -1;
178  }
179
180  // Copy to vectors supplied by user
181  strncpy(strNameUTF8, name, strLen);
182
183  if (strGuidUTF8 != NULL) {
184    strncpy(strGuidUTF8, guid, strLen);
185  }
186
187  return 0;
188}
189
190int VoEHardwareImpl::GetPlayoutDeviceName(int index,
191                                          char strNameUTF8[128],
192                                          char strGuidUTF8[128]) {
193  if (!_shared->statistics().Initialized()) {
194    _shared->SetLastError(VE_NOT_INITED, kTraceError);
195    return -1;
196  }
197  if (strNameUTF8 == NULL) {
198    _shared->SetLastError(VE_INVALID_ARGUMENT, kTraceError,
199                          "GetPlayoutDeviceName() invalid argument");
200    return -1;
201  }
202
203  // Note that strGuidUTF8 is allowed to be NULL
204
205  // Init len variable to length of supplied vectors
206  const uint16_t strLen = 128;
207
208  // Check if length has been changed in module
209  static_assert(strLen == kAdmMaxDeviceNameSize, "");
210  static_assert(strLen == kAdmMaxGuidSize, "");
211
212  char name[strLen];
213  char guid[strLen];
214
215  // Get names from module
216  if (_shared->audio_device()->PlayoutDeviceName(index, name, guid) != 0) {
217    _shared->SetLastError(VE_CANNOT_RETRIEVE_DEVICE_NAME, kTraceError,
218                          "GetPlayoutDeviceName() failed to get device name");
219    return -1;
220  }
221
222  // Copy to vectors supplied by user
223  strncpy(strNameUTF8, name, strLen);
224
225  if (strGuidUTF8 != NULL) {
226    strncpy(strGuidUTF8, guid, strLen);
227  }
228
229  return 0;
230}
231
232int VoEHardwareImpl::SetRecordingDevice(int index,
233                                        StereoChannel recordingChannel) {
234  WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
235               "SetRecordingDevice(index=%d, recordingChannel=%d)", index,
236               (int)recordingChannel);
237  CriticalSectionScoped cs(_shared->crit_sec());
238
239  if (!_shared->statistics().Initialized()) {
240    _shared->SetLastError(VE_NOT_INITED, kTraceError);
241    return -1;
242  }
243
244  bool isRecording(false);
245
246  // Store state about activated recording to be able to restore it after the
247  // recording device has been modified.
248  if (_shared->audio_device()->Recording()) {
249    WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_shared->instance_id(), -1),
250                 "SetRecordingDevice() device is modified while recording"
251                 " is active...");
252    isRecording = true;
253    if (_shared->audio_device()->StopRecording() == -1) {
254      _shared->SetLastError(VE_AUDIO_DEVICE_MODULE_ERROR, kTraceError,
255                            "SetRecordingDevice() unable to stop recording");
256      return -1;
257    }
258  }
259
260  // We let the module do the index sanity
261
262  // Set recording channel
263  AudioDeviceModule::ChannelType recCh = AudioDeviceModule::kChannelBoth;
264  switch (recordingChannel) {
265    case kStereoLeft:
266      recCh = AudioDeviceModule::kChannelLeft;
267      break;
268    case kStereoRight:
269      recCh = AudioDeviceModule::kChannelRight;
270      break;
271    case kStereoBoth:
272      // default setting kChannelBoth (<=> mono)
273      break;
274  }
275
276  if (_shared->audio_device()->SetRecordingChannel(recCh) != 0) {
277    _shared->SetLastError(
278        VE_AUDIO_DEVICE_MODULE_ERROR, kTraceWarning,
279        "SetRecordingChannel() unable to set the recording channel");
280  }
281
282  // Map indices to unsigned since underlying functions need that
283  uint16_t indexU = static_cast<uint16_t>(index);
284
285  int32_t res(0);
286
287  if (index == -1) {
288    res = _shared->audio_device()->SetRecordingDevice(
289        AudioDeviceModule::kDefaultCommunicationDevice);
290  } else if (index == -2) {
291    res = _shared->audio_device()->SetRecordingDevice(
292        AudioDeviceModule::kDefaultDevice);
293  } else {
294    res = _shared->audio_device()->SetRecordingDevice(indexU);
295  }
296
297  if (res != 0) {
298    _shared->SetLastError(
299        VE_AUDIO_DEVICE_MODULE_ERROR, kTraceError,
300        "SetRecordingDevice() unable to set the recording device");
301    return -1;
302  }
303
304  // Init microphone, so user can do volume settings etc
305  if (_shared->audio_device()->InitMicrophone() == -1) {
306    _shared->SetLastError(VE_CANNOT_ACCESS_MIC_VOL, kTraceWarning,
307                          "SetRecordingDevice() cannot access microphone");
308  }
309
310  // Set number of channels
311  bool available = false;
312  if (_shared->audio_device()->StereoRecordingIsAvailable(&available) != 0) {
313    _shared->SetLastError(
314        VE_SOUNDCARD_ERROR, kTraceWarning,
315        "StereoRecordingIsAvailable() failed to query stereo recording");
316  }
317
318  if (_shared->audio_device()->SetStereoRecording(available) != 0) {
319    _shared->SetLastError(
320        VE_SOUNDCARD_ERROR, kTraceWarning,
321        "SetRecordingDevice() failed to set mono recording mode");
322  }
323
324  // Restore recording if it was enabled already when calling this function.
325  if (isRecording) {
326    WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_shared->instance_id(), -1),
327                 "SetRecordingDevice() recording is now being restored...");
328    if (_shared->audio_device()->InitRecording() != 0) {
329      WEBRTC_TRACE(kTraceError, kTraceVoice,
330                   VoEId(_shared->instance_id(), -1),
331                   "SetRecordingDevice() failed to initialize recording");
332      return -1;
333    }
334    if (_shared->audio_device()->StartRecording() != 0) {
335      WEBRTC_TRACE(kTraceError, kTraceVoice,
336                   VoEId(_shared->instance_id(), -1),
337                   "SetRecordingDevice() failed to start recording");
338      return -1;
339    }
340  }
341
342  return 0;
343}
344
345int VoEHardwareImpl::SetPlayoutDevice(int index) {
346  WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
347               "SetPlayoutDevice(index=%d)", index);
348  CriticalSectionScoped cs(_shared->crit_sec());
349
350  if (!_shared->statistics().Initialized()) {
351    _shared->SetLastError(VE_NOT_INITED, kTraceError);
352    return -1;
353  }
354
355  bool isPlaying(false);
356
357  // Store state about activated playout to be able to restore it after the
358  // playout device has been modified.
359  if (_shared->audio_device()->Playing()) {
360    WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_shared->instance_id(), -1),
361                 "SetPlayoutDevice() device is modified while playout is "
362                 "active...");
363    isPlaying = true;
364    if (_shared->audio_device()->StopPlayout() == -1) {
365      _shared->SetLastError(VE_AUDIO_DEVICE_MODULE_ERROR, kTraceError,
366                            "SetPlayoutDevice() unable to stop playout");
367      return -1;
368    }
369  }
370
371  // We let the module do the index sanity
372
373  // Map indices to unsigned since underlying functions need that
374  uint16_t indexU = static_cast<uint16_t>(index);
375
376  int32_t res(0);
377
378  if (index == -1) {
379    res = _shared->audio_device()->SetPlayoutDevice(
380        AudioDeviceModule::kDefaultCommunicationDevice);
381  } else if (index == -2) {
382    res = _shared->audio_device()->SetPlayoutDevice(
383        AudioDeviceModule::kDefaultDevice);
384  } else {
385    res = _shared->audio_device()->SetPlayoutDevice(indexU);
386  }
387
388  if (res != 0) {
389    _shared->SetLastError(
390        VE_SOUNDCARD_ERROR, kTraceError,
391        "SetPlayoutDevice() unable to set the playout device");
392    return -1;
393  }
394
395  // Init speaker, so user can do volume settings etc
396  if (_shared->audio_device()->InitSpeaker() == -1) {
397    _shared->SetLastError(VE_CANNOT_ACCESS_SPEAKER_VOL, kTraceWarning,
398                          "SetPlayoutDevice() cannot access speaker");
399  }
400
401  // Set number of channels
402  bool available = false;
403  _shared->audio_device()->StereoPlayoutIsAvailable(&available);
404  if (_shared->audio_device()->SetStereoPlayout(available) != 0) {
405    _shared->SetLastError(
406        VE_SOUNDCARD_ERROR, kTraceWarning,
407        "SetPlayoutDevice() failed to set stereo playout mode");
408  }
409
410  // Restore playout if it was enabled already when calling this function.
411  if (isPlaying) {
412    WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_shared->instance_id(), -1),
413                 "SetPlayoutDevice() playout is now being restored...");
414    if (_shared->audio_device()->InitPlayout() != 0) {
415      WEBRTC_TRACE(kTraceError, kTraceVoice,
416                   VoEId(_shared->instance_id(), -1),
417                   "SetPlayoutDevice() failed to initialize playout");
418      return -1;
419    }
420    if (_shared->audio_device()->StartPlayout() != 0) {
421      WEBRTC_TRACE(kTraceError, kTraceVoice,
422                   VoEId(_shared->instance_id(), -1),
423                   "SetPlayoutDevice() failed to start playout");
424      return -1;
425    }
426  }
427
428  return 0;
429}
430
431int VoEHardwareImpl::SetRecordingSampleRate(unsigned int samples_per_sec) {
432  WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
433               "%s", __FUNCTION__);
434  if (!_shared->statistics().Initialized()) {
435    _shared->SetLastError(VE_NOT_INITED, kTraceError);
436    return false;
437  }
438  return _shared->audio_device()->SetRecordingSampleRate(samples_per_sec);
439}
440
441int VoEHardwareImpl::RecordingSampleRate(unsigned int* samples_per_sec) const {
442  if (!_shared->statistics().Initialized()) {
443    _shared->SetLastError(VE_NOT_INITED, kTraceError);
444    return false;
445  }
446  return _shared->audio_device()->RecordingSampleRate(samples_per_sec);
447}
448
449int VoEHardwareImpl::SetPlayoutSampleRate(unsigned int samples_per_sec) {
450  WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
451               "%s", __FUNCTION__);
452  if (!_shared->statistics().Initialized()) {
453    _shared->SetLastError(VE_NOT_INITED, kTraceError);
454    return false;
455  }
456  return _shared->audio_device()->SetPlayoutSampleRate(samples_per_sec);
457}
458
459int VoEHardwareImpl::PlayoutSampleRate(unsigned int* samples_per_sec) const {
460  if (!_shared->statistics().Initialized()) {
461    _shared->SetLastError(VE_NOT_INITED, kTraceError);
462    return false;
463  }
464  return _shared->audio_device()->PlayoutSampleRate(samples_per_sec);
465}
466
467bool VoEHardwareImpl::BuiltInAECIsAvailable() const {
468  if (!_shared->statistics().Initialized()) {
469    _shared->SetLastError(VE_NOT_INITED, kTraceError);
470    return false;
471  }
472  return _shared->audio_device()->BuiltInAECIsAvailable();
473}
474
475int VoEHardwareImpl::EnableBuiltInAEC(bool enable) {
476  if (!_shared->statistics().Initialized()) {
477    _shared->SetLastError(VE_NOT_INITED, kTraceError);
478    return -1;
479  }
480  return _shared->audio_device()->EnableBuiltInAEC(enable);
481}
482
483bool VoEHardwareImpl::BuiltInAGCIsAvailable() const {
484  if (!_shared->statistics().Initialized()) {
485    _shared->SetLastError(VE_NOT_INITED, kTraceError);
486    return false;
487  }
488  return _shared->audio_device()->BuiltInAGCIsAvailable();
489}
490
491int VoEHardwareImpl::EnableBuiltInAGC(bool enable) {
492  if (!_shared->statistics().Initialized()) {
493    _shared->SetLastError(VE_NOT_INITED, kTraceError);
494    return -1;
495  }
496  return _shared->audio_device()->EnableBuiltInAGC(enable);
497}
498
499bool VoEHardwareImpl::BuiltInNSIsAvailable() const {
500  if (!_shared->statistics().Initialized()) {
501    _shared->SetLastError(VE_NOT_INITED, kTraceError);
502    return false;
503  }
504  return _shared->audio_device()->BuiltInNSIsAvailable();
505}
506
507int VoEHardwareImpl::EnableBuiltInNS(bool enable) {
508  if (!_shared->statistics().Initialized()) {
509    _shared->SetLastError(VE_NOT_INITED, kTraceError);
510    return -1;
511  }
512  return _shared->audio_device()->EnableBuiltInNS(enable);
513}
514
515#endif  // WEBRTC_VOICE_ENGINE_HARDWARE_API
516
517}  // namespace webrtc
518