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