/*
 *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "voe_hardware_impl.h"

#include <cassert>

#include "cpu_wrapper.h"
#include "critical_section_wrapper.h"
#include "trace.h"
#include "voe_errors.h"
#include "voice_engine_impl.h"

namespace webrtc
{

VoEHardware* VoEHardware::GetInterface(VoiceEngine* voiceEngine)
{
#ifndef WEBRTC_VOICE_ENGINE_HARDWARE_API
    return NULL;
#else
    if (NULL == voiceEngine)
    {
        return NULL;
    }
    VoiceEngineImpl* s = reinterpret_cast<VoiceEngineImpl*>(voiceEngine);
    s->AddRef();
    return s;
#endif
}

#ifdef WEBRTC_VOICE_ENGINE_HARDWARE_API

VoEHardwareImpl::VoEHardwareImpl(voe::SharedData* shared) :
    _cpu(NULL), _shared(shared)
{
    WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_shared->instance_id(), -1),
                 "VoEHardwareImpl() - ctor");

    _cpu = CpuWrapper::CreateCpu();
    if (_cpu)
    {
        _cpu->CpuUsage(); // init cpu usage
    }
}

VoEHardwareImpl::~VoEHardwareImpl()
{
    WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_shared->instance_id(), -1),
                 "~VoEHardwareImpl() - dtor");

    if (_cpu)
    {
        delete _cpu;
        _cpu = NULL;
    }
}

int VoEHardwareImpl::SetAudioDeviceLayer(AudioLayers audioLayer)
{
    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
                 "SetAudioDeviceLayer(audioLayer=%d)", audioLayer);

    // Don't allow a change if VoE is initialized
    if (_shared->statistics().Initialized())
    {
        _shared->SetLastError(VE_ALREADY_INITED, kTraceError);
        return -1;
    }

    // Map to AudioDeviceModule::AudioLayer
    AudioDeviceModule::AudioLayer
        wantedLayer(AudioDeviceModule::kPlatformDefaultAudio);
    switch (audioLayer)
    {
        case kAudioPlatformDefault:
            // already set above
            break;
        case kAudioWindowsCore:
            wantedLayer = AudioDeviceModule::kWindowsCoreAudio;
            break;
        case kAudioWindowsWave:
            wantedLayer = AudioDeviceModule::kWindowsWaveAudio;
            break;
        case kAudioLinuxAlsa:
            wantedLayer = AudioDeviceModule::kLinuxAlsaAudio;
            break;
        case kAudioLinuxPulse:
            wantedLayer = AudioDeviceModule::kLinuxPulseAudio;
            break;
    }

    // Save the audio device layer for Init()
    _shared->set_audio_device_layer(wantedLayer);

    return 0;
}

int VoEHardwareImpl::GetAudioDeviceLayer(AudioLayers& audioLayer)
{
    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
               "GetAudioDeviceLayer(devices=?)");

    // Can always be called regardless of VoE state

    AudioDeviceModule::AudioLayer
        activeLayer(AudioDeviceModule::kPlatformDefaultAudio);

    if (_shared->audio_device())
    {
        // Get active audio layer from ADM
        if (_shared->audio_device()->ActiveAudioLayer(&activeLayer) != 0)
        {
            _shared->SetLastError(VE_UNDEFINED_SC_ERR, kTraceError,
                "  Audio Device error");
            return -1;
        }
    }
    else
    {
        // Return VoE's internal layer setting
        activeLayer = _shared->audio_device_layer();
    }

    // Map to AudioLayers
    switch (activeLayer)
    {
        case AudioDeviceModule::kPlatformDefaultAudio:
            audioLayer = kAudioPlatformDefault;
            break;
        case AudioDeviceModule::kWindowsCoreAudio:
            audioLayer = kAudioWindowsCore;
            break;
        case AudioDeviceModule::kWindowsWaveAudio:
            audioLayer = kAudioWindowsWave;
            break;
        case AudioDeviceModule::kLinuxAlsaAudio:
            audioLayer = kAudioLinuxAlsa;
            break;
        case AudioDeviceModule::kLinuxPulseAudio:
            audioLayer = kAudioLinuxPulse;
            break;
        default:
            _shared->SetLastError(VE_UNDEFINED_SC_ERR, kTraceError,
                "  unknown audio layer");
    }

    WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
        VoEId(_shared->instance_id(), -1),
        "  Output: audioLayer=%d", audioLayer);

    return 0;
}
int VoEHardwareImpl::GetNumOfRecordingDevices(int& devices)
{
    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
                 "GetNumOfRecordingDevices(devices=?)");
    ANDROID_NOT_SUPPORTED(_shared->statistics());
    IPHONE_NOT_SUPPORTED(_shared->statistics());

    if (!_shared->statistics().Initialized())
    {
        _shared->SetLastError(VE_NOT_INITED, kTraceError);
        return -1;
    }

    devices = static_cast<int> (_shared->audio_device()->RecordingDevices());

    WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
        VoEId(_shared->instance_id(), -1), "  Output: devices=%d", devices);

    return 0;
}

int VoEHardwareImpl::GetNumOfPlayoutDevices(int& devices)
{
    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
                 "GetNumOfPlayoutDevices(devices=?)");
    ANDROID_NOT_SUPPORTED(_shared->statistics());
    IPHONE_NOT_SUPPORTED(_shared->statistics());

    if (!_shared->statistics().Initialized())
    {
        _shared->SetLastError(VE_NOT_INITED, kTraceError);
        return -1;
    }

    devices = static_cast<int> (_shared->audio_device()->PlayoutDevices());

    WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
        VoEId(_shared->instance_id(), -1),
        "  Output: devices=%d", devices);

    return 0;
}

int VoEHardwareImpl::GetRecordingDeviceName(int index,
                                            char strNameUTF8[128],
                                            char strGuidUTF8[128])
{
    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
                 "GetRecordingDeviceName(index=%d)", index);
    ANDROID_NOT_SUPPORTED(_shared->statistics());
    IPHONE_NOT_SUPPORTED(_shared->statistics());

    if (!_shared->statistics().Initialized())
    {
        _shared->SetLastError(VE_NOT_INITED, kTraceError);
        return -1;
    }
    if (strNameUTF8 == NULL)
    {
        _shared->SetLastError(VE_INVALID_ARGUMENT, kTraceError,
            "GetRecordingDeviceName() invalid argument");
        return -1;
    }

    // Note that strGuidUTF8 is allowed to be NULL

    // Init len variable to length of supplied vectors
    const WebRtc_UWord16 strLen = 128;

    // Check if length has been changed in module
    assert(strLen == kAdmMaxDeviceNameSize);
    assert(strLen == kAdmMaxGuidSize);

    char name[strLen];
    char guid[strLen];

    // Get names from module
    if (_shared->audio_device()->RecordingDeviceName(index, name, guid) != 0)
    {
        _shared->SetLastError(VE_CANNOT_RETRIEVE_DEVICE_NAME, kTraceError,
            "GetRecordingDeviceName() failed to get device name");
        return -1;
    }

    // Copy to vectors supplied by user
    strncpy(strNameUTF8, name, strLen);
    WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
        VoEId(_shared->instance_id(), -1),
        "  Output: strNameUTF8=%s", strNameUTF8);

    if (strGuidUTF8 != NULL)
    {
        strncpy(strGuidUTF8, guid, strLen);
        WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
            VoEId(_shared->instance_id(), -1),
            "  Output: strGuidUTF8=%s", strGuidUTF8);
    }

    return 0;
}

int VoEHardwareImpl::GetPlayoutDeviceName(int index,
                                          char strNameUTF8[128],
                                          char strGuidUTF8[128])
{
    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
                 "GetPlayoutDeviceName(index=%d)", index);
    ANDROID_NOT_SUPPORTED(_shared->statistics());
    IPHONE_NOT_SUPPORTED(_shared->statistics());

    if (!_shared->statistics().Initialized())
    {
        _shared->SetLastError(VE_NOT_INITED, kTraceError);
        return -1;
    }
    if (strNameUTF8 == NULL)
    {
        _shared->SetLastError(VE_INVALID_ARGUMENT, kTraceError,
            "GetPlayoutDeviceName() invalid argument");
        return -1;
    }

    // Note that strGuidUTF8 is allowed to be NULL

    // Init len variable to length of supplied vectors
    const WebRtc_UWord16 strLen = 128;

    // Check if length has been changed in module
    assert(strLen == kAdmMaxDeviceNameSize);
    assert(strLen == kAdmMaxGuidSize);

    char name[strLen];
    char guid[strLen];

    // Get names from module
    if (_shared->audio_device()->PlayoutDeviceName(index, name, guid) != 0)
    {
        _shared->SetLastError(VE_CANNOT_RETRIEVE_DEVICE_NAME, kTraceError,
            "GetPlayoutDeviceName() failed to get device name");
        return -1;
    }

    // Copy to vectors supplied by user
    strncpy(strNameUTF8, name, strLen);
    WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
        VoEId(_shared->instance_id(), -1),
        "  Output: strNameUTF8=%s", strNameUTF8);

    if (strGuidUTF8 != NULL)
    {
        strncpy(strGuidUTF8, guid, strLen);
        WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
            VoEId(_shared->instance_id(), -1),
            "  Output: strGuidUTF8=%s", strGuidUTF8);
    }

    return 0;
}

int VoEHardwareImpl::SetRecordingDevice(int index,
                                        StereoChannel recordingChannel)
{
    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
                 "SetRecordingDevice(index=%d, recordingChannel=%d)",
                 index, (int) recordingChannel);
    CriticalSectionScoped cs(_shared->crit_sec());
    IPHONE_NOT_SUPPORTED(_shared->statistics());
    // TODO(leozwang): Add this api to Android OpenSL ES implementation.

    if (!_shared->statistics().Initialized())
    {
        _shared->SetLastError(VE_NOT_INITED, kTraceError);
        return -1;
    }

    bool isRecording(false);

    // Store state about activated recording to be able to restore it after the
    // recording device has been modified.
    if (_shared->audio_device()->Recording())
    {
        WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_shared->instance_id(), -1),
                     "SetRecordingDevice() device is modified while recording"
                     " is active...");
        isRecording = true;
        if (_shared->audio_device()->StopRecording() == -1)
        {
            _shared->SetLastError(VE_AUDIO_DEVICE_MODULE_ERROR, kTraceError,
                "SetRecordingDevice() unable to stop recording");
            return -1;
        }
    }

    // We let the module do the index sanity

    // Set recording channel
    AudioDeviceModule::ChannelType recCh =
        AudioDeviceModule::kChannelBoth;
    switch (recordingChannel)
    {
        case kStereoLeft:
            recCh = AudioDeviceModule::kChannelLeft;
            break;
        case kStereoRight:
            recCh = AudioDeviceModule::kChannelRight;
            break;
        case kStereoBoth:
            // default setting kChannelBoth (<=> mono)
            break;
    }

    if (_shared->audio_device()->SetRecordingChannel(recCh) != 0) {
      _shared->SetLastError(VE_AUDIO_DEVICE_MODULE_ERROR, kTraceWarning,
          "SetRecordingChannel() unable to set the recording channel");
    }

    // Map indices to unsigned since underlying functions need that
    WebRtc_UWord16 indexU = static_cast<WebRtc_UWord16> (index);

    WebRtc_Word32 res(0);

    if (index == -1)
    {
        res = _shared->audio_device()->SetRecordingDevice(
            AudioDeviceModule::kDefaultCommunicationDevice);
    }
    else if (index == -2)
    {
        res = _shared->audio_device()->SetRecordingDevice(
            AudioDeviceModule::kDefaultDevice);
    }
    else
    {
        res = _shared->audio_device()->SetRecordingDevice(indexU);
    }

    if (res != 0)
    {
        _shared->SetLastError(VE_AUDIO_DEVICE_MODULE_ERROR, kTraceError,
            "SetRecordingDevice() unable to set the recording device");
        return -1;
    }

    // Init microphone, so user can do volume settings etc
    if (_shared->audio_device()->InitMicrophone() == -1)
    {
        _shared->SetLastError(VE_CANNOT_ACCESS_MIC_VOL, kTraceWarning,
            "SetRecordingDevice() cannot access microphone");
    }

    // Set number of channels
    bool available = false;
    if (_shared->audio_device()->StereoRecordingIsAvailable(&available) != 0) {
      _shared->SetLastError(VE_SOUNDCARD_ERROR, kTraceWarning,
          "StereoRecordingIsAvailable() failed to query stereo recording");
    }

    if (_shared->audio_device()->SetStereoRecording(available) != 0)
    {
        _shared->SetLastError(VE_SOUNDCARD_ERROR, kTraceWarning,
            "SetRecordingDevice() failed to set mono recording mode");
    }

    // Restore recording if it was enabled already when calling this function.
    if (isRecording)
    {
        if (!_shared->ext_recording())
        {
            WEBRTC_TRACE(kTraceInfo, kTraceVoice,
                VoEId(_shared->instance_id(), -1),
                "SetRecordingDevice() recording is now being restored...");
            if (_shared->audio_device()->InitRecording() != 0)
            {
                WEBRTC_TRACE(kTraceError, kTraceVoice,
                    VoEId(_shared->instance_id(), -1),
                    "SetRecordingDevice() failed to initialize recording");
                return -1;
            }
            if (_shared->audio_device()->StartRecording() != 0)
            {
                WEBRTC_TRACE(kTraceError, kTraceVoice,
                             VoEId(_shared->instance_id(), -1),
                             "SetRecordingDevice() failed to start recording");
                return -1;
            }
        }
    }

    return 0;
}

int VoEHardwareImpl::SetPlayoutDevice(int index)
{
    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
                 "SetPlayoutDevice(index=%d)", index);
    CriticalSectionScoped cs(_shared->crit_sec());
    ANDROID_NOT_SUPPORTED(_shared->statistics());
    IPHONE_NOT_SUPPORTED(_shared->statistics());

    if (!_shared->statistics().Initialized())
    {
        _shared->SetLastError(VE_NOT_INITED, kTraceError);
        return -1;
    }

    bool isPlaying(false);

    // Store state about activated playout to be able to restore it after the
    // playout device has been modified.
    if (_shared->audio_device()->Playing())
    {
        WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_shared->instance_id(), -1),
                     "SetPlayoutDevice() device is modified while playout is "
                     "active...");
        isPlaying = true;
        if (_shared->audio_device()->StopPlayout() == -1)
        {
            _shared->SetLastError(VE_AUDIO_DEVICE_MODULE_ERROR, kTraceError,
                "SetPlayoutDevice() unable to stop playout");
            return -1;
        }
    }

    // We let the module do the index sanity

    // Map indices to unsigned since underlying functions need that
    WebRtc_UWord16 indexU = static_cast<WebRtc_UWord16> (index);

    WebRtc_Word32 res(0);

    if (index == -1)
    {
        res = _shared->audio_device()->SetPlayoutDevice(
            AudioDeviceModule::kDefaultCommunicationDevice);
    }
    else if (index == -2)
    {
        res = _shared->audio_device()->SetPlayoutDevice(
            AudioDeviceModule::kDefaultDevice);
    }
    else
    {
        res = _shared->audio_device()->SetPlayoutDevice(indexU);
    }

    if (res != 0)
    {
        _shared->SetLastError(VE_SOUNDCARD_ERROR, kTraceError,
            "SetPlayoutDevice() unable to set the playout device");
        return -1;
    }

    // Init speaker, so user can do volume settings etc
    if (_shared->audio_device()->InitSpeaker() == -1)
    {
        _shared->SetLastError(VE_CANNOT_ACCESS_SPEAKER_VOL, kTraceWarning,
            "SetPlayoutDevice() cannot access speaker");
    }

    // Set number of channels
    bool available = false;
    _shared->audio_device()->StereoPlayoutIsAvailable(&available);
    if (_shared->audio_device()->SetStereoPlayout(available) != 0)
    {
        _shared->SetLastError(VE_SOUNDCARD_ERROR, kTraceWarning,
            "SetPlayoutDevice() failed to set stereo playout mode");
    }

    // Restore playout if it was enabled already when calling this function.
    if (isPlaying)
    {
        if (!_shared->ext_playout())
        {
            WEBRTC_TRACE(kTraceInfo, kTraceVoice,
                VoEId(_shared->instance_id(), -1),
                "SetPlayoutDevice() playout is now being restored...");
            if (_shared->audio_device()->InitPlayout() != 0)
            {
                WEBRTC_TRACE(kTraceError, kTraceVoice,
                  VoEId(_shared->instance_id(), -1),
                  "SetPlayoutDevice() failed to initialize playout");
                return -1;
            }
            if (_shared->audio_device()->StartPlayout() != 0)
            {
                WEBRTC_TRACE(kTraceError, kTraceVoice,
                             VoEId(_shared->instance_id(), -1),
                             "SetPlayoutDevice() failed to start playout");
                return -1;
            }
        }
    }

    return 0;
}

int VoEHardwareImpl::GetRecordingDeviceStatus(bool& isAvailable)
{
    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
                 "GetRecordingDeviceStatus()");
    ANDROID_NOT_SUPPORTED(_shared->statistics());
    IPHONE_NOT_SUPPORTED(_shared->statistics());

    if (!_shared->statistics().Initialized())
    {
        _shared->SetLastError(VE_NOT_INITED, kTraceError);
        return -1;
    }

    // We let the module do isRecording sanity

    bool available(false);

    // Check availability
    if (_shared->audio_device()->RecordingIsAvailable(&available) != 0)
    {
        _shared->SetLastError(VE_UNDEFINED_SC_REC_ERR, kTraceError,
            "  Audio Device error");
        return -1;
    }

    isAvailable = available;

    WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
        VoEId(_shared->instance_id(), -1),
        "  Output: isAvailable = %d)", (int) isAvailable);

    return 0;
}

int VoEHardwareImpl::GetPlayoutDeviceStatus(bool& isAvailable)
{
    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
                 "GetPlayoutDeviceStatus()");
    ANDROID_NOT_SUPPORTED(_shared->statistics());
    IPHONE_NOT_SUPPORTED(_shared->statistics());

    if (!_shared->statistics().Initialized())
    {
        _shared->SetLastError(VE_NOT_INITED, kTraceError);
        return -1;
    }

    // We let the module do isPlaying sanity

    bool available(false);

    // Check availability
    if (_shared->audio_device()->PlayoutIsAvailable(&available) != 0)
    {
        _shared->SetLastError(VE_PLAY_UNDEFINED_SC_ERR, kTraceError,
            "  Audio Device error");
        return -1;
    }

    isAvailable = available;

    WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
        VoEId(_shared->instance_id(), -1),
        "  Output: isAvailable = %d)", (int) isAvailable);

    return 0;
}

int VoEHardwareImpl::ResetAudioDevice()
{
    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
                 "ResetAudioDevice()");
    ANDROID_NOT_SUPPORTED(_shared->statistics());

    if (!_shared->statistics().Initialized())
    {
        _shared->SetLastError(VE_NOT_INITED, kTraceError);
        return -1;
    }

#if defined(WEBRTC_IOS)
    if (_shared->audio_device()->ResetAudioDevice() < 0)
    {
        _shared->SetLastError(VE_SOUNDCARD_ERROR, kTraceError,
            "  Failed to reset sound device");
        return -1;
    }
#else
    _shared->SetLastError(VE_FUNC_NOT_SUPPORTED, kTraceError,
        "  no support for resetting sound device");
    return -1;
#endif

    return 0;
}

int VoEHardwareImpl::AudioDeviceControl(unsigned int par1, unsigned int par2,
                                        unsigned int par3)
{
    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
                 "AudioDeviceControl(%i, %i, %i)", par1, par2, par3);
    ANDROID_NOT_SUPPORTED(_shared->statistics());
    IPHONE_NOT_SUPPORTED(_shared->statistics());
    if (!_shared->statistics().Initialized())
    {
        _shared->SetLastError(VE_NOT_INITED, kTraceError);
        return -1;
    }
    _shared->SetLastError(VE_FUNC_NOT_SUPPORTED, kTraceError,
        "  no support for resetting sound device");
    return -1;
}

int VoEHardwareImpl::SetLoudspeakerStatus(bool enable)
{
    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
                 "SetLoudspeakerStatus(enable=%i)", (int) enable);
    IPHONE_NOT_SUPPORTED(_shared->statistics());

    if (!_shared->statistics().Initialized())
    {
        _shared->SetLastError(VE_NOT_INITED, kTraceError);
        return -1;
    }
#if defined(WEBRTC_ANDROID)
    if (_shared->audio_device()->SetLoudspeakerStatus(enable) < 0)
    {
        _shared->SetLastError(VE_IGNORED_FUNCTION, kTraceError,
            "  Failed to set loudspeaker status");
        return -1;
    }

    return 0;
#else
    _shared->SetLastError(VE_FUNC_NOT_SUPPORTED, kTraceError,
        "  no support for setting loudspeaker status");
    return -1;
#endif
}

int VoEHardwareImpl::GetLoudspeakerStatus(bool& enabled)
{
    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
                 "GetLoudspeakerStatus()");
    IPHONE_NOT_SUPPORTED(_shared->statistics());

#if defined(WEBRTC_ANDROID)
    if (!_shared->statistics().Initialized())
    {
        _shared->SetLastError(VE_NOT_INITED, kTraceError);
        return -1;
    }

    if (_shared->audio_device()->GetLoudspeakerStatus(&enabled) < 0)
    {
        _shared->SetLastError(VE_IGNORED_FUNCTION, kTraceError,
            "  Failed to get loudspeaker status");
        return -1;
    }

    return 0;
#else
    _shared->SetLastError(VE_FUNC_NOT_SUPPORTED, kTraceError,
      "  no support for setting loudspeaker status");
    return -1;
#endif
}

int VoEHardwareImpl::GetCPULoad(int& loadPercent)
{
    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
                 "GetCPULoad()");
    ANDROID_NOT_SUPPORTED(_shared->statistics());
    IPHONE_NOT_SUPPORTED(_shared->statistics());

    if (!_shared->statistics().Initialized())
    {
        _shared->SetLastError(VE_NOT_INITED, kTraceError);
        return -1;
    }

    // Get CPU load from ADM
    WebRtc_UWord16 load(0);
    if (_shared->audio_device()->CPULoad(&load) != 0)
    {
        _shared->SetLastError(VE_CPU_INFO_ERROR, kTraceError,
            "  error getting system CPU load");
        return -1;
    }

    loadPercent = static_cast<int> (load);

    WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
        VoEId(_shared->instance_id(), -1),
        "  Output: loadPercent = %d", loadPercent);

    return 0;
}

int VoEHardwareImpl::GetSystemCPULoad(int& loadPercent)
{
    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
                 "GetSystemCPULoad(loadPercent=?)");
    ANDROID_NOT_SUPPORTED(_shared->statistics());
    IPHONE_NOT_SUPPORTED(_shared->statistics());

    if (!_shared->statistics().Initialized())
    {
        _shared->SetLastError(VE_NOT_INITED, kTraceError);
        return -1;
    }

    // Check if implemented for this platform
    if (!_cpu)
    {
        _shared->SetLastError(VE_FUNC_NOT_SUPPORTED, kTraceError,
            "  no support for getting system CPU load");
        return -1;
    }

    // Get CPU load
    WebRtc_Word32 load = _cpu->CpuUsage();
    if (load < 0)
    {
        _shared->SetLastError(VE_CPU_INFO_ERROR, kTraceError,
            "  error getting system CPU load");
        return -1;
    }

    loadPercent = static_cast<int> (load);

    WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
        VoEId(_shared->instance_id(), -1),
        "  Output: loadPercent = %d", loadPercent);

    return 0;
}

int VoEHardwareImpl::EnableBuiltInAEC(bool enable)
{
    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
        "%s", __FUNCTION__);
    if (!_shared->statistics().Initialized())
    {
        _shared->SetLastError(VE_NOT_INITED, kTraceError);
        return -1;
    }

    return _shared->audio_device()->EnableBuiltInAEC(enable);
}

bool VoEHardwareImpl::BuiltInAECIsEnabled() const
{
    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
        "%s", __FUNCTION__);
    if (!_shared->statistics().Initialized())
    {
        _shared->SetLastError(VE_NOT_INITED, kTraceError);
        return false;
    }

    return _shared->audio_device()->BuiltInAECIsEnabled();
}

int VoEHardwareImpl::SetRecordingSampleRate(unsigned int samples_per_sec) {
  WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
               "%s", __FUNCTION__);
  if (!_shared->statistics().Initialized()) {
    _shared->SetLastError(VE_NOT_INITED, kTraceError);
    return false;
  }
  return _shared->audio_device()->SetRecordingSampleRate(samples_per_sec);
}

int VoEHardwareImpl::RecordingSampleRate(unsigned int* samples_per_sec) const {
  WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
               "%s", __FUNCTION__);
  if (!_shared->statistics().Initialized()) {
    _shared->SetLastError(VE_NOT_INITED, kTraceError);
    return false;
  }
  return _shared->audio_device()->RecordingSampleRate(samples_per_sec);
}

int VoEHardwareImpl::SetPlayoutSampleRate(unsigned int samples_per_sec) {
  WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
               "%s", __FUNCTION__);
  if (!_shared->statistics().Initialized()) {
    _shared->SetLastError(VE_NOT_INITED, kTraceError);
    return false;
  }
  return _shared->audio_device()->SetPlayoutSampleRate(samples_per_sec);
}

int VoEHardwareImpl::PlayoutSampleRate(unsigned int* samples_per_sec) const {
  WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
               "%s", __FUNCTION__);
  if (!_shared->statistics().Initialized()) {
    _shared->SetLastError(VE_NOT_INITED, kTraceError);
    return false;
  }
  return _shared->audio_device()->PlayoutSampleRate(samples_per_sec);
}

#endif  // WEBRTC_VOICE_ENGINE_HARDWARE_API

} // namespace webrtc
