diff options
author | Ying Wang <wangying@google.com> | 2011-02-18 12:00:38 -0800 |
---|---|---|
committer | Ying Wang <wangying@google.com> | 2011-02-18 12:00:38 -0800 |
commit | 2896e59ac088f73318bc010fce3a4a31f0c3ff45 (patch) | |
tree | 4a65630d0ae505020ae02b54ca7f2600592ded89 /libaudio | |
parent | b5381afd299fd924758820613c02a3fc8218aebc (diff) | |
download | wingray-2896e59ac088f73318bc010fce3a4a31f0c3ff45.tar.gz |
Split out new device wingray.
Change-Id: I19c27ff5f3961e9adb6ca25a6f458920fdafa932
Diffstat (limited to 'libaudio')
-rw-r--r-- | libaudio/Android.mk | 74 | ||||
-rw-r--r-- | libaudio/AudioHardware.cpp | 1899 | ||||
-rw-r--r-- | libaudio/AudioHardware.h | 341 | ||||
-rw-r--r-- | libaudio/AudioPolicyManager.cpp | 44 | ||||
-rw-r--r-- | libaudio/AudioPolicyManager.h | 46 | ||||
-rw-r--r-- | libaudio/AudioPostProcessor.cpp | 755 | ||||
-rw-r--r-- | libaudio/AudioPostProcessor.h | 122 | ||||
-rw-r--r-- | libaudio/MODULE_LICENSE_APACHE2 | 0 | ||||
-rw-r--r-- | libaudio/NOTICE | 190 |
9 files changed, 3471 insertions, 0 deletions
diff --git a/libaudio/Android.mk b/libaudio/Android.mk new file mode 100644 index 0000000..36c9696 --- /dev/null +++ b/libaudio/Android.mk @@ -0,0 +1,74 @@ +ifneq ($(BUILD_TINY_ANDROID),true) + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + AudioPolicyManager.cpp + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libutils \ + libmedia + +LOCAL_STATIC_LIBRARIES := libaudiopolicybase + +LOCAL_MODULE:= libaudiopolicy + +ifeq ($(BOARD_HAVE_BLUETOOTH),true) + LOCAL_CFLAGS += -DWITH_A2DP +endif + +include $(BUILD_SHARED_LIBRARY) + + +include $(CLEAR_VARS) + +LOCAL_MODULE := libaudio + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libutils \ + libmedia \ + libhardware_legacy + +ifeq ($TARGET_OS)-$(TARGET_SIMULATOR),linux-true) +LOCAL_LDLIBS += -ldl +endif + +ifneq ($(TARGET_SIMULATOR),true) +LOCAL_SHARED_LIBRARIES += libdl +endif + +LOCAL_SRC_FILES += AudioHardware.cpp + +LOCAL_CFLAGS += -fno-short-enums + +LOCAL_STATIC_LIBRARIES += libaudiointerface + +ifeq ($(USE_PROPRIETARY_AUDIO_EXTENSIONS),true) +LOCAL_SRC_FILES += AudioPostProcessor.cpp +LOCAL_STATIC_LIBRARIES += \ + libEverest_motomm-r \ + libCortexA9_aie-r \ + libCortexA9_sas-r \ + libCortexA9_se-r \ + libCortexA9_motovoice-r \ + libCortexA9_ecns-r \ + libsamplerateconverter \ + libCortexA9_anm-r + +LOCAL_CFLAGS += -DUSE_PROPRIETARY_AUDIO_EXTENSIONS +LOCAL_C_INCLUDES += vendor/moto/stingray/motomm/ghdr +LOCAL_C_INCLUDES += vendor/moto/stingray/motomm/rate_conv +endif + +ifeq ($(BOARD_HAVE_BLUETOOTH),true) + LOCAL_SHARED_LIBRARIES += liba2dp +endif + +include $(BUILD_SHARED_LIBRARY) + +endif # not BUILD_TINY_ANDROID + diff --git a/libaudio/AudioHardware.cpp b/libaudio/AudioHardware.cpp new file mode 100644 index 0000000..17969d1 --- /dev/null +++ b/libaudio/AudioHardware.cpp @@ -0,0 +1,1899 @@ +/* +** Copyright 2008-2010, The Android Open-Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include <math.h> + +//#define LOG_NDEBUG 0 +#define LOG_TAG "AudioHardwareTegra" +#include <utils/Log.h> +#include <utils/String8.h> + +#include <stdio.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <dlfcn.h> +#include <fcntl.h> + +#include "AudioHardware.h" +#include <media/AudioRecord.h> + +namespace android { +const uint32_t AudioHardware::inputSamplingRates[] = { + 8000, 11025, 12000, 16000, 22050, 32000, 44100, 48000 +}; + +// number of times to attempt init() before giving up +const uint32_t MAX_INIT_TRIES = 10; + +// ---------------------------------------------------------------------------- + +// always succeeds, must call init() immediately after +AudioHardware::AudioHardware() : + mInit(false), mMicMute(false), mBluetoothNrec(true), mBluetoothId(0), + mOutput(0), /*mCurOut/InDevice*/ mCpcapCtlFd(-1), mHwOutRate(0), mHwInRate(0), + mMasterVol(1.0), mVoiceVol(1.0), + /*mCpcapGain*/ + mSpkrVolume(-1), mMicVolume(-1) +{ + LOGV("AudioHardware constructor"); +} + +// designed to be called multiple times for retries +status_t AudioHardware::init() { + + if (mInit) { + return NO_ERROR; + } + + mCpcapCtlFd = ::open("/dev/audio_ctl", O_RDWR); + if (mCpcapCtlFd < 0) { + LOGE("open /dev/audio_ctl failed: %s", strerror(errno)); + goto error; + } + + if (::ioctl(mCpcapCtlFd, CPCAP_AUDIO_OUT_GET_OUTPUT, &mCurOutDevice) < 0) { + LOGE("could not get output device: %s", strerror(errno)); + goto error; + } + if (::ioctl(mCpcapCtlFd, CPCAP_AUDIO_IN_GET_INPUT, &mCurInDevice) < 0) { + LOGE("could not get input device: %s", strerror(errno)); + goto error; + } + // For bookkeeping only + if (::ioctl(mCpcapCtlFd, CPCAP_AUDIO_OUT_GET_RATE, &mHwOutRate) < 0) { + LOGE("could not get output rate: %s", strerror(errno)); + goto error; + } + if (::ioctl(mCpcapCtlFd, CPCAP_AUDIO_IN_GET_RATE, &mHwInRate) < 0) { + LOGE("could not get input rate: %s", strerror(errno)); + goto error; + } + +#ifdef USE_PROPRIETARY_AUDIO_EXTENSIONS + // Init the MM Audio Post Processing + mAudioPP.setAudioDev(&mCurOutDevice, &mCurInDevice, false, false, false); +#endif + + readHwGainFile(); + + mInit = true; + return NO_ERROR; + +error: + if (mCpcapCtlFd >= 0) { + (void) ::close(mCpcapCtlFd); + mCpcapCtlFd = -1; + } + return NO_INIT; +} + +AudioHardware::~AudioHardware() +{ + LOGV("AudioHardware destructor"); + for (size_t index = 0; index < mInputs.size(); index++) { + closeInputStream((AudioStreamIn*)mInputs[index]); + } + mInputs.clear(); + closeOutputStream((AudioStreamOut*)mOutput); + if (mCpcapCtlFd >= 0) { + (void) ::close(mCpcapCtlFd); + mCpcapCtlFd = -1; + } +} + +void AudioHardware::readHwGainFile() +{ + int fd; + int rc=0; + int i; + uint32_t format, version, barker; + fd = open("/system/etc/cpcap_gain.bin", O_RDONLY); + if (fd>=0) { + ::read(fd, &format, sizeof(uint32_t)); + ::read(fd, &version, sizeof(uint32_t)); + ::read(fd, &barker, sizeof(uint32_t)); + rc = ::read(fd, mCpcapGain, sizeof(mCpcapGain)); + LOGD("Read gain file, format %X version %X", format, version); + ::close(fd); + } + if (rc != sizeof(mCpcapGain) || format != 0x30303032) { + int gain; + LOGE("CPCAP gain file not valid. Using defaults."); + for (int i=0; i<AUDIO_HW_GAIN_NUM_DIRECTIONS; i++) { + if (i==AUDIO_HW_GAIN_SPKR_GAIN) + gain = 11; + else + gain = 31; + for (int j=0; j<AUDIO_HW_GAIN_NUM_USECASES; j++) + for (int k=0; k<AUDIO_HW_GAIN_NUM_PATHS; k++) + mCpcapGain[i][j][k]=gain; + } + } + return; +} + +status_t AudioHardware::initCheck() +{ + return mInit ? NO_ERROR : NO_INIT; +} + +AudioStreamOut* AudioHardware::openOutputStream( + uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status) +{ + { // scope for the lock + Mutex::Autolock lock(mLock); + + // only one output stream allowed + if (mOutput) { + if (status) { + *status = INVALID_OPERATION; + } + return 0; + } + + // create new output stream + AudioStreamOutTegra* out = new AudioStreamOutTegra(); + for (unsigned tries = 0; tries < MAX_INIT_TRIES; ++tries) { + if (NO_ERROR == out->init()) + break; + LOGW("AudioStreamOutTegra::init failed soft, retrying"); + sleep(1); + } + status_t lStatus; + lStatus = out->initCheck(); + if (NO_ERROR != lStatus) { + LOGE("AudioStreamOutTegra::init failed hard"); + } else { + lStatus = out->set(this, devices, format, channels, sampleRate); + } + if (status) { + *status = lStatus; + } + if (lStatus == NO_ERROR) { + mOutput = out; + } else { + mLock.unlock(); + delete out; + out = NULL; + mLock.lock(); + } + } + return mOutput; +} + +void AudioHardware::closeOutputStream(AudioStreamOut* out) { + Mutex::Autolock lock(mLock); + if (mOutput == 0 || mOutput != out) { + LOGW("Attempt to close invalid output stream"); + } + else { + // AudioStreamOutTegra destructor calls standby which locks + mOutput = 0; + mLock.unlock(); + delete out; + mLock.lock(); + } +} + +AudioStreamIn* AudioHardware::openInputStream( + uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status, + AudioSystem::audio_in_acoustics acoustic_flags) +{ + // check for valid input source + if (!AudioSystem::isInputDevice((AudioSystem::audio_devices)devices)) { + return 0; + } + + Mutex::Autolock lock(mLock); + + AudioStreamInTegra* in = new AudioStreamInTegra(); + // this serves a similar purpose as init() + status_t lStatus = in->set(this, devices, format, channels, sampleRate, acoustic_flags); + if (status) { + *status = lStatus; + } + if (lStatus != NO_ERROR) { + mLock.unlock(); + delete in; + mLock.lock(); + return 0; + } + + mInputs.add(in); + + return in; +} + +void AudioHardware::closeInputStream(AudioStreamIn* in) +{ + Mutex::Autolock lock(mLock); + + ssize_t index = mInputs.indexOf((AudioStreamInTegra *)in); + if (index < 0) { + LOGW("Attempt to close invalid input stream"); + } else { + mInputs.removeAt(index); + mLock.unlock(); + delete in; + mLock.lock(); + } +} + +status_t AudioHardware::setMode(int mode) +{ + AutoMutex lock(mLock); + bool wasInCall = isInCall(); + LOGV("setMode() : new %d, old %d", mode, mMode); + status_t status = AudioHardwareBase::setMode(mode); + if (status == NO_ERROR) { + if (wasInCall ^ isInCall()) { + doRouting_l(); + if (wasInCall) { + setMicMute_l(false); + } + } + } + + return status; +} + +// Must be called with mLock held +status_t AudioHardware::doStandby(int stop_fd, bool output, bool enable) +{ + status_t status = NO_ERROR; + struct cpcap_audio_stream standby; + + LOGV("AudioHardware::doStandby() putting %s in %s mode", + output ? "output" : "input", + enable ? "standby" : "online" ); + +// Debug code + if (!mLock.tryLock()) { + LOGE("doStandby called without mLock held."); + mLock.unlock(); + } +// end Debug code + + if (output) { + standby.id = CPCAP_AUDIO_OUT_STANDBY; + standby.on = enable; + + if (enable) { + /* Flush the queued playback data. Putting the output in standby + * will cause CPCAP to not drive the i2s interface, and write() + * will block until playback is resumed. + */ + if (mOutput) + mOutput->flush(); + } + + if (::ioctl(mCpcapCtlFd, CPCAP_AUDIO_OUT_SET_OUTPUT, &standby) < 0) { + LOGE("could not turn off current output device: %s", + strerror(errno)); + status = errno; + } + + if (::ioctl(mCpcapCtlFd, CPCAP_AUDIO_OUT_GET_OUTPUT, &mCurOutDevice) < 0) { + LOGE("could not get current output device after standby: %s", + strerror(errno)); + } + LOGV("%s: after standby %s, output device %d is %s", __FUNCTION__, + enable ? "enable" : "disable", mCurOutDevice.id, + mCurOutDevice.on ? "on" : "off"); + } else { + standby.id = CPCAP_AUDIO_IN_STANDBY; + standby.on = enable; + + if (enable && stop_fd >= 0) { + /* Stop recording, if ongoing. Muting the microphone will cause + * CPCAP to not send data through the i2s interface, and read() + * will block until recording is resumed. + */ + LOGV("%s: stop recording", __FUNCTION__); + if (::ioctl(stop_fd, TEGRA_AUDIO_IN_STOP) < 0) { + LOGE("could not stop recording: %s", + strerror(errno)); + } + } + + if (::ioctl(mCpcapCtlFd, CPCAP_AUDIO_IN_SET_INPUT, &standby) < 0) { + LOGE("could not turn off current input device: %s", + strerror(errno)); + status = errno; + } + ::ioctl(mCpcapCtlFd, CPCAP_AUDIO_IN_GET_INPUT, &mCurInDevice); + LOGV("%s: after standby %s, input device %d is %s", __FUNCTION__, + enable ? "enable" : "disable", mCurInDevice.id, + mCurInDevice.on ? "on" : "off"); + } + + return status; +} + +status_t AudioHardware::setMicMute(bool state) +{ + Mutex::Autolock lock(mLock); + return setMicMute_l(state); +} + +status_t AudioHardware::setMicMute_l(bool state) +{ + if (mMicMute != state) { + mMicMute = state; + LOGV("setMicMute() %s", (state)?"ON":"OFF"); + } + return NO_ERROR; +} + +status_t AudioHardware::getMicMute(bool* state) +{ + *state = mMicMute; + return NO_ERROR; +} + +status_t AudioHardware::setParameters(const String8& keyValuePairs) +{ + AudioParameter param = AudioParameter(keyValuePairs); + String8 value; + String8 key; + const char BT_NREC_KEY[] = "bt_headset_nrec"; + const char BT_NAME_KEY[] = "bt_headset_name"; + const char BT_NREC_VALUE_ON[] = "on"; + + + LOGV("setParameters() %s", keyValuePairs.string()); + + if (keyValuePairs.length() == 0) return BAD_VALUE; + + key = String8(BT_NREC_KEY); + if (param.get(key, value) == NO_ERROR) { + if (value == BT_NREC_VALUE_ON) { + mBluetoothNrec = true; + LOGI("Turn on bluetooth NREC"); + } else { + mBluetoothNrec = false; + LOGI("Turning noise reduction and echo cancellation off for BT " + "headset"); + } + doRouting(); + } + key = String8(BT_NAME_KEY); + if (param.get(key, value) == NO_ERROR) { + mBluetoothId = 0; +#if 0 + for (int i = 0; i < mNumSndEndpoints; i++) { + if (!strcasecmp(value.string(), mSndEndpoints[i].name)) { + mBluetoothId = mSndEndpoints[i].id; + LOGI("Using custom acoustic parameters for %s", value.string()); + break; + } + } +#endif + if (mBluetoothId == 0) { + LOGI("Using default acoustic parameters " + "(%s not in acoustic database)", value.string()); + doRouting(); + } + } + return NO_ERROR; +} + +String8 AudioHardware::getParameters(const String8& keys) +{ + AudioParameter request = AudioParameter(keys); + AudioParameter reply = AudioParameter(); + String8 value; + String8 key; + + LOGV("getParameters() %s", keys.string()); + +#ifdef USE_PROPRIETARY_AUDIO_EXTENSIONS + key = "ec_supported"; + if (request.get(key, value) == NO_ERROR) { + value = "yes"; + reply.add(key, value); + } +#endif + + return reply.toString(); +} + +size_t AudioHardware::getInputBufferSize(uint32_t sampleRate, int format, int channelCount) +{ + size_t bufsize; + + if (format != AudioSystem::PCM_16_BIT) { + LOGW("getInputBufferSize bad format: %d", format); + return 0; + } + if (channelCount < 1 || channelCount > 2) { + LOGW("getInputBufferSize bad channel count: %d", channelCount); + return 0; + } + + // Return 20 msec input buffer size. + bufsize = sampleRate * sizeof(int16_t) * channelCount / 50; + if (bufsize & 0x7) { + // Not divisible by 8. + bufsize +=8; + bufsize &= ~0x7; + } + LOGD("%s: returns %d for rate %d", __FUNCTION__, bufsize, sampleRate); + return bufsize; +} + +//setVoiceVolume is only useful for setting sidetone gains with a baseband +//controlling volume. Don't adjust hardware volume with this API. +// +//(On Stingray, don't use mVoiceVol for anything.) +status_t AudioHardware::setVoiceVolume(float v) +{ + if (v < 0.0) + v = 0.0; + else if (v > 1.0) + v = 1.0; + + LOGI("Setting unused in-call vol to %f",v); + mVoiceVol = v; + + return NO_ERROR; +} + +status_t AudioHardware::setMasterVolume(float v) +{ + if (v < 0.0) + v = 0.0; + else if (v > 1.0) + v = 1.0; + + LOGV("Set master vol to %f.", v); + mMasterVol = v; + Mutex::Autolock lock(mLock); + int useCase = AUDIO_HW_GAIN_USECASE_MM; + AudioStreamInTegra *input = getActiveInput_l(); + if (input) { + if (isInCall() && mOutput && !mOutput->getStandby() && + input->source() == AUDIO_SOURCE_VOICE_COMMUNICATION) { + useCase = AUDIO_HW_GAIN_USECASE_VOICE; + } else if (input->source() == AUDIO_SOURCE_VOICE_RECOGNITION) { + useCase = AUDIO_HW_GAIN_USECASE_VOICE_REC; + } + } + setVolume_l(v, useCase); + return NO_ERROR; +} + +// Call with mLock held. +status_t AudioHardware::setVolume_l(float v, int usecase) +{ + int spkr = getGain(AUDIO_HW_GAIN_SPKR_GAIN, usecase); + int mic = getGain(AUDIO_HW_GAIN_MIC_GAIN, usecase); + + if (spkr==0) { + // no device to set volume on. Ignore request. + return -1; + } + + spkr = ceil(v * spkr); + if (mSpkrVolume != spkr) { + LOGD("Set tx volume to %d", spkr); + int ret = ::ioctl(mCpcapCtlFd, CPCAP_AUDIO_OUT_SET_VOLUME, spkr); + if (ret < 0) { + LOGE("could not set spkr volume: %s", strerror(errno)); + return ret; + } + mSpkrVolume = spkr; + } + if (mMicVolume != mic) { + LOGD("Set rx volume to %d", mic); + int ret = ::ioctl(mCpcapCtlFd, CPCAP_AUDIO_IN_SET_VOLUME, mic); + if (ret < 0) { + LOGE("could not set mic volume: %s", strerror(errno)); + return ret; + } + mMicVolume = mic; + } + + return NO_ERROR; +} + +uint8_t AudioHardware::getGain(int direction, int usecase) +{ + int path; + AudioStreamInTegra *input = getActiveInput_l(); + uint32_t inDev = (input == NULL) ? 0 : input->devices(); + if (!mOutput) { + LOGE("No output device."); + return 0; + } + uint32_t outDev = mOutput->devices(); + +// In case of an actual phone, with an actual earpiece, uncomment. +// if (outDev & AudioSystem::DEVICE_OUT_EARPIECE) +// path = AUDIO_HW_GAIN_EARPIECE; +// else + if (outDev & AudioSystem::DEVICE_OUT_WIRED_HEADPHONE) + path = AUDIO_HW_GAIN_HEADSET_NO_MIC; + else if (outDev & AudioSystem::DEVICE_OUT_WIRED_HEADSET) + path = AUDIO_HW_GAIN_HEADSET_W_MIC; + else if (outDev & AudioSystem::DEVICE_OUT_ANLG_DOCK_HEADSET) + path = AUDIO_HW_GAIN_EMU_DEVICE; + else + path = AUDIO_HW_GAIN_SPEAKERPHONE; + + LOGV("Picked gain[%d][%d][%d] which is %d.",direction, usecase, path, + mCpcapGain[direction][usecase][path]); + + return mCpcapGain[direction][usecase][path]; +} + +int AudioHardware::getActiveInputRate() +{ + AudioStreamInTegra *input = getActiveInput_l(); + return (input != NULL) ? input->sampleRate() : 0; +} + +status_t AudioHardware::doRouting() +{ + Mutex::Autolock lock(mLock); + return doRouting_l(); +} + +// Call this with mLock held. +status_t AudioHardware::doRouting_l() +{ + if (!mOutput) { + return NO_ERROR; + } + uint32_t outputDevices = mOutput->devices(); + AudioStreamInTegra *input = getActiveInput_l(); + uint32_t inputDevice = (input == NULL) ? 0 : input->devices(); + uint32_t btScoOutDevices = outputDevices & ( + AudioSystem::DEVICE_OUT_BLUETOOTH_SCO | + AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET | + AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT ); + uint32_t spdifOutDevices = outputDevices & ( + AudioSystem::DEVICE_OUT_DGTL_DOCK_HEADSET | + AudioSystem::DEVICE_OUT_AUX_DIGITAL ); + uint32_t speakerOutDevices = outputDevices ^ btScoOutDevices ^ spdifOutDevices; + uint32_t btScoInDevice = inputDevice & AudioSystem::DEVICE_IN_BLUETOOTH_SCO_HEADSET; + uint32_t micInDevice = inputDevice ^ btScoInDevice; + int sndOutDevice = -1; + int sndInDevice = -1; + bool btScoOn = btScoOutDevices||btScoInDevice; + + LOGV("%s: inputDevice %x, outputDevices %x", __FUNCTION__, + inputDevice, outputDevices); + + switch (inputDevice) { + case AudioSystem::DEVICE_IN_DEFAULT: + case AudioSystem::DEVICE_IN_BUILTIN_MIC: + sndInDevice = CPCAP_AUDIO_IN_MIC1; + break; + case AudioSystem::DEVICE_IN_WIRED_HEADSET: + sndInDevice = CPCAP_AUDIO_IN_MIC2; + break; + default: + break; + } + + switch (speakerOutDevices) { + case AudioSystem::DEVICE_OUT_EARPIECE: + case AudioSystem::DEVICE_OUT_DEFAULT: + case AudioSystem::DEVICE_OUT_SPEAKER: + sndOutDevice = CPCAP_AUDIO_OUT_SPEAKER; + break; + case AudioSystem::DEVICE_OUT_WIRED_HEADSET: + case AudioSystem::DEVICE_OUT_WIRED_HEADPHONE: + sndOutDevice = CPCAP_AUDIO_OUT_HEADSET; + break; + case AudioSystem::DEVICE_OUT_SPEAKER | AudioSystem::DEVICE_OUT_WIRED_HEADSET: + case AudioSystem::DEVICE_OUT_SPEAKER | AudioSystem::DEVICE_OUT_WIRED_HEADPHONE: + sndOutDevice = CPCAP_AUDIO_OUT_HEADSET_AND_SPEAKER; + break; + case AudioSystem::DEVICE_OUT_ANLG_DOCK_HEADSET: + sndOutDevice = CPCAP_AUDIO_OUT_ANLG_DOCK_HEADSET; + break; + case AudioSystem::DEVICE_OUT_DGTL_DOCK_HEADSET: + // To be implemented + break; + default: + break; + } + + if (sndInDevice != (int)mCurInDevice.id) { + if (sndInDevice == -1) { + LOGV("input device set %x not supported, defaulting to on-board mic", + inputDevice); + mCurInDevice.id = CPCAP_AUDIO_IN_MIC1; + } + else + mCurInDevice.id = sndInDevice; + + if (::ioctl(mCpcapCtlFd, CPCAP_AUDIO_IN_SET_INPUT, + &mCurInDevice) < 0) + LOGE("could not set input (%d, on %d): %s", + mCurInDevice.id, mCurInDevice.on, strerror(errno)); + + LOGV("current input %d, %s", + mCurInDevice.id, + mCurInDevice.on ? "on" : "off"); + } + + if (sndOutDevice != (int)mCurOutDevice.id) { + if (sndOutDevice == -1) { + LOGW("output device set %x not supported, defaulting to speaker", + outputDevices); + mCurOutDevice.id = CPCAP_AUDIO_OUT_SPEAKER; + } + else + mCurOutDevice.id = sndOutDevice; + + if (::ioctl(mCpcapCtlFd, CPCAP_AUDIO_OUT_SET_OUTPUT, + &mCurOutDevice) < 0) + LOGE("could not set output (%d, on %d): %s", + mCurOutDevice.id, mCurOutDevice.on, + strerror(errno)); + + LOGV("current output %d, %s", + mCurOutDevice.id, + mCurOutDevice.on ? "on" : "off"); + } + + bool ecnsEnabled = false; + // enable EC if: + // - the audio mode is IN_CALL or IN_COMMUNICATION AND + // - the output stream is active AND + // - an input stream with VOICE_COMMUNICATION source is active + if (isInCall() && !mOutput->getStandby() && + input && input->source() == AUDIO_SOURCE_VOICE_COMMUNICATION) { + ecnsEnabled = true; + } + + int oldInRate=mHwInRate, oldOutRate=mHwOutRate; +#ifdef USE_PROPRIETARY_AUDIO_EXTENSIONS + int ecnsRate = getActiveInputRate() < 16000? 8000 : 16000; + // Check input/output rates for HW. + if (ecnsEnabled) { + mHwInRate = ecnsRate; + mHwOutRate = mHwInRate; + LOGD("EC/NS active, requests rate as %d for in/out", mHwInRate); + } + else +#endif + { + mHwInRate = getActiveInputRate(); + mHwOutRate = AUDIO_HW_OUT_SAMPLERATE; + LOGV("No EC/NS, set input rate %d, output %d.", mHwInRate, mHwOutRate); + } + if (btScoOn) { + mHwOutRate = 8000; + mHwInRate = 8000; + LOGD("Bluetooth SCO active, rate forced to 8K"); + } + + if (input) { + // acquire mutex if not already locked by read() + if (!input->isLocked()) { + input->lock(); + } + if (mHwInRate != oldInRate) { + LOGV("Minor TODO: Flush input if active."); + if (::ioctl(mCpcapCtlFd, CPCAP_AUDIO_IN_SET_RATE, + mHwInRate) < 0) + LOGE("could not set input rate(%d): %s", + mHwInRate, strerror(errno)); + if (::ioctl(mCpcapCtlFd, CPCAP_AUDIO_IN_GET_RATE, &mHwInRate)) + LOGE("CPCAP driver error reading rates: %s", strerror(errno)); + } + } + + // acquire mutex if not already locked by write() + if (!mOutput->isLocked()) { + mOutput->lock(); + } + int speakerOutRate = 0; + if (::ioctl(mCpcapCtlFd, CPCAP_AUDIO_OUT_GET_RATE, &speakerOutRate)) + LOGE("could not read output rate: %s", + strerror(errno)); + if (mHwOutRate != oldOutRate || + (speakerOutRate!=AUDIO_HW_OUT_SAMPLERATE && btScoOn)) { + int speaker_rate = mHwOutRate; + if (btScoOn) { + speaker_rate = AUDIO_HW_OUT_SAMPLERATE; + } + // Flush old data (wrong rate) from I2S driver before changing rate. + if (mOutput) { + mOutput->flush(); + if (ecnsEnabled) { + mOutput->setNumBufs(AUDIO_HW_NUM_OUT_BUF); + } else { + mOutput->setNumBufs(AUDIO_HW_NUM_OUT_BUF_LONG); + } + } + // Now the DMA is empty, change the rate. + if (::ioctl(mCpcapCtlFd, CPCAP_AUDIO_OUT_SET_RATE, + speaker_rate) < 0) + LOGE("could not set output rate(%d): %s", + speaker_rate, strerror(errno)); + } +#ifdef USE_PROPRIETARY_AUDIO_EXTENSIONS + mAudioPP.setAudioDev(&mCurOutDevice, &mCurInDevice, + btScoOn, mBluetoothNrec, + spdifOutDevices?true:false); + mAudioPP.enableEcns(ecnsEnabled); +#endif + + mOutput->setDriver_l(speakerOutDevices?true:false, + btScoOn, + spdifOutDevices?true:false, mHwOutRate); + if (input) { + input->setDriver_l(micInDevice?true:false, + btScoInDevice?true:false, mHwInRate); + } + if (!mOutput->isLocked()) { + mOutput->unlock(); + } + if (input && !input->isLocked()) { + input->unlock(); + } + + // Since HW path may have changed, set the hardware gains. + int useCase = AUDIO_HW_GAIN_USECASE_MM; + if (ecnsEnabled) { + useCase = AUDIO_HW_GAIN_USECASE_VOICE; + } else if (input && input->source() == AUDIO_SOURCE_VOICE_RECOGNITION) { + useCase = AUDIO_HW_GAIN_USECASE_VOICE_REC; + } + setVolume_l(mMasterVol, useCase); + + return NO_ERROR; +} + +status_t AudioHardware::dumpInternals(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + result.append("AudioHardware::dumpInternals\n"); + snprintf(buffer, SIZE, "\tmInit: %s\n", mInit? "true": "false"); + result.append(buffer); + snprintf(buffer, SIZE, "\tmMicMute: %s\n", mMicMute? "true": "false"); + result.append(buffer); + snprintf(buffer, SIZE, "\tmBluetoothNrec: %s\n", mBluetoothNrec? "true": "false"); + result.append(buffer); + snprintf(buffer, SIZE, "\tmBluetoothId: %d\n", mBluetoothId); + result.append(buffer); + ::write(fd, result.string(), result.size()); + return NO_ERROR; +} + +status_t AudioHardware::dump(int fd, const Vector<String16>& args) +{ + dumpInternals(fd, args); + for (size_t index = 0; index < mInputs.size(); index++) { + mInputs[index]->dump(fd, args); + } + + if (mOutput) { + mOutput->dump(fd, args); + } + return NO_ERROR; +} + +uint32_t AudioHardware::getInputSampleRate(uint32_t sampleRate) +{ + uint32_t i; + uint32_t prevDelta; + uint32_t delta; + + for (i = 0, prevDelta = 0xFFFFFFFF; i < sizeof(inputSamplingRates)/sizeof(uint32_t); i++, prevDelta = delta) { + delta = abs(sampleRate - inputSamplingRates[i]); + if (delta > prevDelta) break; + } + // i is always > 0 here + return inputSamplingRates[i-1]; +} + +// getActiveInput_l() must be called with mLock held +AudioHardware::AudioStreamInTegra *AudioHardware::getActiveInput_l() +{ + for (size_t i = 0; i < mInputs.size(); i++) { + // return first input found not being in standby mode + // as only one input can be in this state + if (!mInputs[i]->getStandby()) { + return mInputs[i]; + } + } + + return NULL; +} + +// ---------------------------------------------------------------------------- +// Sample Rate Converter wrapper +// +#ifdef USE_PROPRIETARY_AUDIO_EXTENSIONS +AudioHardware::AudioStreamSrc::AudioStreamSrc() : + mSrcInitted(false) +{ +} +AudioHardware::AudioStreamSrc::~AudioStreamSrc() +{ +} + +void AudioHardware::AudioStreamSrc::init(int inRate, int outRate) +{ + if (mSrcBuffer == NULL) + mSrcBuffer = new char[src_memory_required_stereo(MAX_FRAME_LEN, MAX_CONVERT_RATIO)]; + if (mSrcBuffer == NULL) { + LOGE("Failed to allocate memory for sample rate converter."); + return; + } + mSrcInit.memory = (SRC16*)(mSrcBuffer); + mSrcInit.input_rate = inRate; + mSrcInit.output_rate = outRate; + mSrcInit.frame_length = MAX_FRAME_LEN; + mSrcInit.stereo_flag = SRC_OFF; + mSrcInit.input_interleaved = SRC_OFF; + mSrcInit.output_interleaved = SRC_OFF; + rate_convert_init(&mSrcInit, &mSrcObj); + + mSrcInitted = true; + mSrcInRate = inRate; + mSrcOutRate = outRate; +} +#endif + +// ---------------------------------------------------------------------------- + +// always succeeds, must call init() immediately after +AudioHardware::AudioStreamOutTegra::AudioStreamOutTegra() : + mHardware(0), mFd(-1), mFdCtl(-1), + mBtFd(-1), mBtFdCtl(-1), mBtFdIoCtl(-1), + mSpdifFd(-1), mSpdifFdCtl(-1), + mStartCount(0), mRetryCount(0), mDevices(0), + mIsSpkrEnabled(false), mIsBtEnabled(false), mIsSpdifEnabled(false), + mIsSpkrEnabledReq(false), mIsBtEnabledReq(false), mIsSpdifEnabledReq(false), + mSpareSample(0), mHaveSpareSample(false), + mState(AUDIO_STREAM_IDLE), /*mSrc*/ mLocked(false), mDriverRate(AUDIO_HW_OUT_SAMPLERATE), + mInit(false) +{ + LOGV("AudioStreamOutTegra constructor"); +} + +// designed to be called multiple times for retries +status_t AudioHardware::AudioStreamOutTegra::init() +{ + if (mInit) { + return NO_ERROR; + } + +#define OPEN_FD(fd, dev) fd = ::open(dev, O_RDWR); \ + if (fd < 0) { \ + LOGE("open " dev " failed: %s", strerror(errno)); \ + goto error; \ + } + OPEN_FD(mFd, "/dev/audio0_out") + OPEN_FD(mFdCtl, "/dev/audio0_out_ctl") + OPEN_FD(mBtFd, "/dev/audio1_out") + OPEN_FD(mBtFdCtl, "/dev/audio1_out_ctl") + OPEN_FD(mBtFdIoCtl, "/dev/audio1_ctl") + // may need to be changed to warnings + OPEN_FD(mSpdifFd, "/dev/spdif_out") + OPEN_FD(mSpdifFdCtl, "/dev/spdif_out_ctl") +#undef OPEN_FD + + setNumBufs(AUDIO_HW_NUM_OUT_BUF_LONG); + + mInit = true; + return NO_ERROR; + +error: +#define CLOSE_FD(fd) if (fd >= 0) { \ + (void) ::close(fd); \ + fd = -1; \ + } + CLOSE_FD(mFd) + CLOSE_FD(mFdCtl) + CLOSE_FD(mBtFd) + CLOSE_FD(mBtFdCtl) + CLOSE_FD(mBtFdIoCtl) + CLOSE_FD(mSpdifFd) + CLOSE_FD(mSpdifFdCtl) +#undef CLOSE_FD + return NO_INIT; +} + +status_t AudioHardware::AudioStreamOutTegra::initCheck() +{ + return mInit ? NO_ERROR : NO_INIT; +} + +// Called with mHardware->mLock and mLock held. +void AudioHardware::AudioStreamOutTegra::setDriver_l( + bool speaker, bool bluetooth, bool spdif, int sampleRate) +{ + int bit_format = TEGRA_AUDIO_BIT_FORMAT_DEFAULT; + bool is_bt_bypass = false; + + LOGV("%s: Analog speaker? %s. Bluetooth? %s. S/PDIF? %s. sampleRate %d", __FUNCTION__, + speaker?"yes":"no", bluetooth?"yes":"no", spdif?"yes":"no", sampleRate); + + // force some reconfiguration at next write() + if (mState == AUDIO_STREAM_CONFIGURED) { + if (mIsSpkrEnabled != speaker || mIsBtEnabled != bluetooth || mIsSpdifEnabled != spdif) { + mState = AUDIO_STREAM_CONFIG_REQ; + } else if (sampleRate != mDriverRate) { + mState = AUDIO_STREAM_NEW_RATE_REQ; + } + } + + mIsSpkrEnabledReq = speaker; + mIsBtEnabledReq = bluetooth; + mIsSpdifEnabledReq = spdif; + +} + +status_t AudioHardware::AudioStreamOutTegra::set( + AudioHardware* hw, uint32_t devices, int *pFormat, uint32_t *pChannels, uint32_t *pRate) +{ + int lFormat = pFormat ? *pFormat : 0; + uint32_t lChannels = pChannels ? *pChannels : 0; + uint32_t lRate = pRate ? *pRate : 0; + + mHardware = hw; + + // fix up defaults + if (lFormat == 0) lFormat = format(); + if (lChannels == 0) lChannels = channels(); + if (lRate == 0) lRate = sampleRate(); + + // check values + if ((lFormat != format()) || + (lChannels != channels()) || + (lRate != sampleRate())) { + if (pFormat) *pFormat = format(); + if (pChannels) *pChannels = channels(); + if (pRate) *pRate = sampleRate(); + return BAD_VALUE; + } + +#ifdef USE_PROPRIETARY_AUDIO_EXTENSIONS + mHardware->mAudioPP.setPlayAudioRate(lRate); +#endif + + if (pFormat) *pFormat = lFormat; + if (pChannels) *pChannels = lChannels; + if (pRate) *pRate = lRate; + + mDevices = devices; + if (mFd >= 0 && mFdCtl >= 0 && + mBtFd >= 0 && + mBtFdCtl >= 0 && + mBtFdIoCtl >= 0) { + if (mSpdifFd < 0 || mSpdifFdCtl < 0) + LOGW("s/pdif driver not present"); + return NO_ERROR; + } else { + LOGE("Problem opening device files - Is your kernel compatible?"); + return NO_INIT; + } +} + +AudioHardware::AudioStreamOutTegra::~AudioStreamOutTegra() +{ + standby(); + // Prevent someone from flushing the fd during a close. + Mutex::Autolock lock(mFdLock); + if (mFd >= 0) { ::close(mFd); mFd = -1; } + if (mFdCtl >= 0) { ::close(mFdCtl); mFdCtl = -1; } + if (mBtFd >= 0) { ::close(mBtFd); mBtFd = -1; } + if (mBtFdCtl >= 0) { ::close(mBtFdCtl); mBtFdCtl = -1; } + if (mBtFdIoCtl >= 0) { ::close(mBtFdIoCtl); mBtFdIoCtl = -1; } + if (mSpdifFd >= 0) { ::close(mSpdifFd); mSpdifFd = -1; } + if (mSpdifFdCtl >= 0) { ::close(mSpdifFdCtl); mSpdifFdCtl = -1; } +} + +ssize_t AudioHardware::AudioStreamOutTegra::write(const void* buffer, size_t bytes) +{ + status_t status; + if (!mHardware) { + LOGE("%s: mHardware is null", __FUNCTION__); + return NO_INIT; + } + // LOGD("AudioStreamOutTegra::write(%p, %u) TID %d", buffer, bytes, gettid()); + // Protect output state during the write process. + + bool needsOnline = false; + if (mState < AUDIO_STREAM_CONFIGURED) { + mHardware->mLock.lock(); + if (mState < AUDIO_STREAM_CONFIGURED) { + needsOnline = true; + } else { + mHardware->mLock.unlock(); + } + } + + { // scope for the lock + Mutex::Autolock lock(mLock); + + ssize_t written = 0; + const uint8_t* p = static_cast<const uint8_t*>(buffer); + size_t outsize = bytes; + int outFd = mFd; + bool stereo; + ssize_t writtenToSpdif = 0; + + if (needsOnline) { + status = online_l(); + mHardware->mLock.unlock(); + if (status < 0) { + goto error; + } + } + stereo = mIsBtEnabled ? false : (channels() == AudioSystem::CHANNEL_OUT_STEREO); + +#ifdef USE_PROPRIETARY_AUDIO_EXTENSIONS + // Do Multimedia processing if appropriate for device and usecase. + mHardware->mAudioPP.doMmProcessing((void *)buffer, bytes / frameSize()); +#endif + + if (mIsSpkrEnabled && mIsBtEnabled) { + // When dual routing to CPCAP and Bluetooth, piggyback CPCAP audio now, + // and then down convert for the BT. + // CPCAP is always 44.1 in this case. + // This also works in the three-way routing case. + Mutex::Autolock lock2(mFdLock); + ::write(outFd, buffer, outsize); + } + if (mIsSpdifEnabled) { + // When dual routing to Speaker and HDMI, piggyback HDMI now, since it + // has no mic we'll leave the rest of the acoustic processing for the + // CPCAP hardware path. + // This also works in the three-way routing case, except the acoustic + // tuning will be done on Bluetooth, since it has the exclusive mic amd + // it also needs the sample rate conversion + Mutex::Autolock lock2(mFdLock); + if (mSpdifFd >= 0) { + writtenToSpdif = ::write(mSpdifFd, buffer, outsize); + LOGV("%s: written %d bytes to SPDIF", __FUNCTION__, (int)writtenToSpdif); + } else { + LOGW("s/pdif enabled but unavailable"); + } + } + if (mIsBtEnabled) { + outFd = mBtFd; + } else if (mIsSpdifEnabled && !mIsSpkrEnabled) { + outFd = -1; + } + +#ifdef USE_PROPRIETARY_AUDIO_EXTENSIONS + // Check if sample rate conversion or ECNS are required. + // Caution: Upconversion (from 44.1 to 48) would require a new output buffer larger than the + // original one. + if (mDriverRate != (int)sampleRate()) { + if (!mSrc.initted() || + mSrc.inRate() != (int)sampleRate() || + mSrc.outRate() != mDriverRate) { + LOGI("%s: downconvert started from %d to %d",__FUNCTION__, + sampleRate(), mDriverRate); + mSrc.init(sampleRate(), mDriverRate); + if (!mSrc.initted()) { + status = -1; + goto error; + } + // Workaround to give multiple of 4 bytes to driver: Keep one sample + // buffered in case SRC returns an odd number of samples. + mHaveSpareSample = false; + } + } else { + mSrc.deinit(); + } + + if (mHardware->mAudioPP.isEcnsEnabled() || mSrc.initted()) + { + // cut audio down to Mono for SRC or ECNS + if (channels() == AudioSystem::CHANNEL_OUT_STEREO) + { + // Do stereo-to-mono downmix before SRC, in-place + int16_t *destBuf = (int16_t *) buffer; + for (int i = 0; i < (int)bytes/2; i++) { + destBuf[i] = (destBuf[i*2]>>1) + (destBuf[i*2+1]>>1); + } + outsize >>= 1; + } + } + + if (mSrc.initted()) { + // Apply the sample rate conversion. + mSrc.mIoData.in_buf_ch1 = (SRC16 *) (buffer); + mSrc.mIoData.in_buf_ch2 = 0; + mSrc.mIoData.input_count = outsize / sizeof(SRC16); + mSrc.mIoData.out_buf_ch1 = (SRC16 *) (buffer); + mSrc.mIoData.out_buf_ch2 = 0; + mSrc.mIoData.output_count = outsize / sizeof(SRC16); + if (mHaveSpareSample) { + // Leave room for placing the spare. + mSrc.mIoData.out_buf_ch1++; + mSrc.mIoData.output_count--; + } + mSrc.srcConvert(); + LOGV("Converted %d bytes at %d to %d bytes at %d", + outsize, sampleRate(), mSrc.mIoData.output_count*2, mDriverRate); + if (mHaveSpareSample) { + int16_t *bufp = (int16_t*)buffer; + bufp[0]=mSpareSample; + mSrc.mIoData.output_count++; + mHaveSpareSample = false; + } + outsize = mSrc.mIoData.output_count*2; + LOGV("Outsize is now %d", outsize); + } + if (mHardware->mAudioPP.isEcnsEnabled()) { + // EC/NS is a blocking interface, to synchronise with read. + // It also consumes data when EC/NS is running. + // It expects MONO data. + // If EC/NS is not running, it will return 0, and we need to write this data to the + // driver ourselves. + + // Indicate that it is safe to call setDriver_l() without locking mLock: if the input + // stream is started, doRouting_l() will not block when setDriver_l() is called. + mLocked = true; + LOGV("writeDownlinkEcns size %d", outsize); + written = mHardware->mAudioPP.writeDownlinkEcns(outFd,(void *)buffer, + stereo, outsize, &mFdLock); + mLocked = false; + } + if (mHardware->mAudioPP.isEcnsEnabled() || mSrc.initted()) { + // Move audio back up to Stereo, if the EC/NS wasn't in fact running and we're + // writing to a stereo device. + if (stereo && + written != (ssize_t)outsize) { + // Back up to stereo, in place. + int16_t *destBuf = (int16_t *) buffer; + for (int i = outsize/2-1; i >= 0; i--) { + destBuf[i*2] = destBuf[i]; + destBuf[i*2+1] = destBuf[i]; + } + outsize <<= 1; + } + } +#endif + + if (written != (ssize_t)outsize) { + // The sample rate conversion modifies the output size. + if (outsize&0x3) { + int16_t* bufp = (int16_t *)buffer; +// LOGV("Keep the spare sample away from the driver."); + mHaveSpareSample = true; + mSpareSample = bufp[outsize/2 - 1]; + } + + if (outFd >= 0) { + Mutex::Autolock lock2(mFdLock); + written = ::write(outFd, buffer, outsize&(~0x3)); + if (written != ((ssize_t)outsize&(~0x3))) { + status = written; + goto error; + } + } else { + written = writtenToSpdif; + } + } + if (written < 0) { + LOGE("Error writing %d bytes to output: %s", outsize, strerror(errno)); + status = written; + goto error; + } + + // Sample rate converter may be stashing a couple of bytes here or there, + // so just report that all bytes were consumed. (it would be a bug not to.) + LOGV("write() written %d", bytes); + return bytes; + + } +error: + LOGE("write(): error, return %d", status); + standby(); + usleep(bytes * 1000 / frameSize() / sampleRate() * 1000); + + return status; +} + +void AudioHardware::AudioStreamOutTegra::flush() +{ + // Prevent someone from writing the fd while we flush + Mutex::Autolock lock(mFdLock); + LOGD("AudioStreamOutTegra::flush()"); + if (::ioctl(mFdCtl, TEGRA_AUDIO_OUT_FLUSH) < 0) + LOGE("could not flush playback: %s", strerror(errno)); + if (::ioctl(mBtFdCtl, TEGRA_AUDIO_OUT_FLUSH) < 0) + LOGE("could not flush bluetooth: %s", strerror(errno)); + if (mSpdifFdCtl >= 0 && ::ioctl(mSpdifFdCtl, TEGRA_AUDIO_OUT_FLUSH) < 0) + LOGE("could not flush spdif: %s", strerror(errno)); + LOGD("AudioStreamOutTegra::flush() returns"); +} + +// FIXME: this is a workaround for issue 3387419 with impact on latency +// to be removed when root cause is fixed +void AudioHardware::AudioStreamOutTegra::setNumBufs(int numBufs) +{ + Mutex::Autolock lock(mFdLock); + LOGD("AudioStreamOutTegra::setNumBufs()"); + if (::ioctl(mFdCtl, TEGRA_AUDIO_OUT_SET_NUM_BUFS, &numBufs) < 0) + LOGE("could not set number of output buffers: %s", strerror(errno)); +} + +// Called with mLock and mHardware->mLock held +status_t AudioHardware::AudioStreamOutTegra::online_l() +{ + status_t status = NO_ERROR; + + if (mState < AUDIO_STREAM_NEW_RATE_REQ) { + if (mState == AUDIO_STREAM_IDLE) { + LOGV("output %p going online", this); + mState = AUDIO_STREAM_CONFIG_REQ; + // update EC state if necessary + AudioStreamInTegra *input = mHardware->getActiveInput_l(); + if (mHardware->isInCall() && + input && input->source() == AUDIO_SOURCE_VOICE_COMMUNICATION) { + // doRouting_l() will not try to lock mLock when calling setDriver_l() + mLocked = true; + mHardware->doRouting_l(); + mLocked = false; + } + } + + // If there's no hardware speaker, leave the HW alone. (i.e. SCO/SPDIF is on) + if (mIsSpkrEnabledReq) { + status = mHardware->doStandby(mFdCtl, true, false); // output, online + } else { + status = mHardware->doStandby(mFdCtl, true, true); // output, standby + } + mIsSpkrEnabled = mIsSpkrEnabledReq; + + if ((mIsBtEnabled && !mIsBtEnabledReq) || + (mIsSpdifEnabled && !mIsSpdifEnabledReq)) { + flush(); + } + mIsBtEnabled = mIsBtEnabledReq; + mIsSpdifEnabled = mIsSpdifEnabledReq; + + int bit_format = TEGRA_AUDIO_BIT_FORMAT_DEFAULT; + bool is_bt_bypass = false; + if (mIsBtEnabled) { + bit_format = TEGRA_AUDIO_BIT_FORMAT_DSP; + is_bt_bypass = true; + } + // Setup the I2S2-> DAP2/4 capture/playback path. + if (::ioctl(mBtFdIoCtl, TEGRA_AUDIO_SET_BIT_FORMAT, &bit_format) < 0) { + LOGE("could not set bit format %s", strerror(errno)); + } + if (::ioctl(mHardware->mCpcapCtlFd, CPCAP_AUDIO_SET_BLUETOOTH_BYPASS, is_bt_bypass) < 0) { + LOGE("could not set bluetooth bypass %s", strerror(errno)); + } + + } + + mDriverRate = mHardware->mHwOutRate; + + mState = AUDIO_STREAM_CONFIGURED; + + return status; +} + +status_t AudioHardware::AudioStreamOutTegra::standby() +{ + if (!mHardware) { + return NO_INIT; + } + + status_t status = NO_ERROR; + Mutex::Autolock lock(mHardware->mLock); + Mutex::Autolock lock2(mLock); + + if (mState != AUDIO_STREAM_IDLE) { + LOGV("output %p going into standby", this); + mState = AUDIO_STREAM_IDLE; + + // update EC state if necessary + AudioStreamInTegra *input = mHardware->getActiveInput_l(); + if (mHardware->isInCall() && + input && input->source() == AUDIO_SOURCE_VOICE_COMMUNICATION) { + // doRouting_l will not try to lock mLock when calling setDriver_l() + mLocked = true; + mHardware->doRouting_l(); + mLocked = false; + } + +#ifdef USE_PROPRIETARY_AUDIO_EXTENSIONS + // Prevent EC/NS from writing to the file anymore. + mHardware->mAudioPP.writeDownlinkEcns(-1,0,false,0,&mFdLock); +#endif + if (mIsSpkrEnabled) { + // doStandby() calls flush() which also handles the case where multiple devices + // including bluetooth or SPDIF are selected + status = mHardware->doStandby(mFdCtl, true, true); // output, standby + } else if (mIsBtEnabled || mIsSpdifEnabled) { + flush(); + } + } + + return status; +} + +status_t AudioHardware::AudioStreamOutTegra::dump(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + result.append("AudioStreamOutTegra::dump\n"); + snprintf(buffer, SIZE, "\tsample rate: %d\n", sampleRate()); + result.append(buffer); + snprintf(buffer, SIZE, "\tbuffer size: %d\n", bufferSize()); + result.append(buffer); + snprintf(buffer, SIZE, "\tchannels: %d\n", channels()); + result.append(buffer); + snprintf(buffer, SIZE, "\tformat: %d\n", format()); + result.append(buffer); + snprintf(buffer, SIZE, "\tmHardware: %p\n", mHardware); + result.append(buffer); + snprintf(buffer, SIZE, "\tmFd: %d\n", mFd); + result.append(buffer); + snprintf(buffer, SIZE, "\tmStartCount: %d\n", mStartCount); + result.append(buffer); + snprintf(buffer, SIZE, "\tmRetryCount: %d\n", mRetryCount); + result.append(buffer); + if (mHardware) + snprintf(buffer, SIZE, "\tmStandby: %s\n", + mHardware->mCurOutDevice.on ? "false": "true"); + else + snprintf(buffer, SIZE, "\tmStandby: unknown\n"); + + result.append(buffer); + ::write(fd, result.string(), result.size()); + return NO_ERROR; +} + +bool AudioHardware::AudioStreamOutTegra::getStandby() +{ + return mState == AUDIO_STREAM_IDLE;; +} + +status_t AudioHardware::AudioStreamOutTegra::setParameters(const String8& keyValuePairs) +{ + AudioParameter param = AudioParameter(keyValuePairs); + String8 key = String8(AudioParameter::keyRouting); + status_t status = NO_ERROR; + int device; + LOGV("AudioStreamOutTegra::setParameters() %s", keyValuePairs.string()); + + if (param.getInt(key, device) == NO_ERROR) { + if (device != 0) { + mDevices = device; + LOGV("set output routing %x", mDevices); + status = mHardware->doRouting(); + } + param.remove(key); + } + + if (param.size()) { + status = BAD_VALUE; + } + return status; +} + +String8 AudioHardware::AudioStreamOutTegra::getParameters(const String8& keys) +{ + AudioParameter param = AudioParameter(keys); + String8 value; + String8 key = String8(AudioParameter::keyRouting); + + if (param.get(key, value) == NO_ERROR) { + LOGV("get routing %x", mDevices); + param.addInt(key, (int)mDevices); + } + + LOGV("AudioStreamOutTegra::getParameters() %s", param.toString().string()); + return param.toString(); +} + +status_t AudioHardware::AudioStreamOutTegra::getRenderPosition(uint32_t *dspFrames) +{ + //TODO: enable when supported by driver + return INVALID_OPERATION; +} + +// ---------------------------------------------------------------------------- + +// always succeeds, must call set() immediately after +AudioHardware::AudioStreamInTegra::AudioStreamInTegra() : + mHardware(0), mFd(-1), mFdCtl(-1), mState(AUDIO_STREAM_IDLE), mRetryCount(0), + mFormat(AUDIO_HW_IN_FORMAT), mChannels(AUDIO_HW_IN_CHANNELS), + mSampleRate(AUDIO_HW_IN_SAMPLERATE), mBufferSize(AUDIO_HW_IN_BUFFERSIZE), + mAcoustics((AudioSystem::audio_in_acoustics)0), mDevices(0), + mIsMicEnabled(0), mIsBtEnabled(0), + mSource(AUDIO_SOURCE_DEFAULT), mLocked(false), mTotalBuffersRead(0), + mDriverRate(AUDIO_HW_IN_SAMPLERATE) +{ + LOGV("AudioStreamInTegra constructor"); +} + +// serves a similar purpose as init() +status_t AudioHardware::AudioStreamInTegra::set( + AudioHardware* hw, uint32_t devices, int *pFormat, uint32_t *pChannels, uint32_t *pRate, + AudioSystem::audio_in_acoustics acoustic_flags) +{ + Mutex::Autolock lock(mLock); + status_t status = BAD_VALUE; + mHardware = hw; + if (pFormat == 0) + return status; + if (*pFormat != AUDIO_HW_IN_FORMAT) { + LOGE("wrong in format %d, expecting %lld", *pFormat, AUDIO_HW_IN_FORMAT); + *pFormat = AUDIO_HW_IN_FORMAT; + return status; + } + + if (pRate == 0) + return status; + + uint32_t rate = hw->getInputSampleRate(*pRate); + if (rate != *pRate) { + LOGE("wrong sample rate %d, expecting %d", *pRate, rate); + *pRate = rate; + return status; + } + + if (pChannels == 0) + return status; + + if (*pChannels != AudioSystem::CHANNEL_IN_MONO && + *pChannels != AudioSystem::CHANNEL_IN_STEREO) { + LOGE("wrong number of channels %d", *pChannels); + *pChannels = AUDIO_HW_IN_CHANNELS; + return status; + } + + LOGV("AudioStreamInTegra::set(%d, %d, %u)", *pFormat, *pChannels, *pRate); + + mDevices = devices; + mFormat = AUDIO_HW_IN_FORMAT; + mChannels = *pChannels; + mSampleRate = *pRate; + mBufferSize = mHardware->getInputBufferSize(mSampleRate, AudioSystem::PCM_16_BIT, + AudioSystem::popCount(mChannels)); + return NO_ERROR; +} + +AudioHardware::AudioStreamInTegra::~AudioStreamInTegra() +{ + LOGV("AudioStreamInTegra destructor"); + + standby(); + + if (mFd >= 0) { + ::close(mFd); + mFd = -1; + } + + if (mFdCtl >= 0) { + ::close(mFdCtl); + mFdCtl = -1; + } +} + +// Called with mHardware->mLock and mLock held. +void AudioHardware::AudioStreamInTegra::setDriver_l(bool mic, bool bluetooth, int sampleRate) +{ + LOGD("%s: Analog mic? %s. Bluetooth? %s.", __FUNCTION__, + mic?"yes":"no", bluetooth?"yes":"no"); + + // force some reconfiguration at next read() + // Note: mState always == AUDIO_STREAM_CONFIGURED when setDriver_l() is called on an input + if (mic != mIsMicEnabled || bluetooth != mIsBtEnabled) { + mState = AUDIO_STREAM_CONFIG_REQ; + } else if (sampleRate != mDriverRate) { + mState = AUDIO_STREAM_NEW_RATE_REQ; + } + + mIsMicEnabled = mic; + mIsBtEnabled = bluetooth; + +} + +ssize_t AudioHardware::AudioStreamInTegra::read(void* buffer, ssize_t bytes) +{ + status_t status; + if (!mHardware) { + LOGE("%s: mHardware is null", __FUNCTION__); + return NO_INIT; + } + // LOGV("AudioStreamInTegra::read(%p, %ld) TID %d", buffer, bytes, gettid()); + + bool needsOnline = false; + if (mState < AUDIO_STREAM_CONFIGURED) { + mHardware->mLock.lock(); + if (mState < AUDIO_STREAM_CONFIGURED) { + needsOnline = true; + } else { + mHardware->mLock.unlock(); + } + } + + { // scope for mLock + Mutex::Autolock lock(mLock); + + ssize_t ret; + bool srcReqd; + int hwReadBytes; + int16_t * inbuf; + + if (needsOnline) { + status = online_l(); + mHardware->mLock.unlock(); + if (status != NO_ERROR) { + LOGE("%s: Problem switching to online.",__FUNCTION__); + goto error; + } + } + + srcReqd = (mDriverRate != (int)mSampleRate); + +#ifdef USE_PROPRIETARY_AUDIO_EXTENSIONS + if (srcReqd) { + hwReadBytes = ( bytes*mDriverRate/mSampleRate ) & (~0x7); + LOGV("Running capture SRC. HW=%d bytes at %d, Flinger=%d bytes at %d", + hwReadBytes, mDriverRate, (int)bytes, mSampleRate); + inbuf = mInScratch; + if ((size_t)bytes > sizeof(mInScratch)) { + LOGE("read: buf size problem. %d>%d",(int)bytes,sizeof(mInScratch)); + status = BAD_VALUE; + goto error; + } + // Check if we need to init the rate converter + if (!mSrc.initted() || + mSrc.inRate() != mDriverRate || + mSrc.outRate() != (int)mSampleRate) { + LOGI ("%s: Upconvert started from %d to %d", __FUNCTION__, + mDriverRate, mSampleRate); + mSrc.init(mDriverRate, mSampleRate); + if (!mSrc.initted()) { + status = NO_INIT; + goto error; + } + reopenReconfigDriver(); + } + } else { + hwReadBytes = bytes; + inbuf = (int16_t *)buffer; + mSrc.deinit(); + } + // Read from driver, or ECNS thread, as appropriate. + ret = mHardware->mAudioPP.read(mFd, inbuf, hwReadBytes, mDriverRate); + if (ret>0 && srcReqd) { + mSrc.mIoData.in_buf_ch1 = (SRC16 *) (inbuf); + mSrc.mIoData.in_buf_ch2 = 0; + mSrc.mIoData.input_count = hwReadBytes / sizeof(SRC16); + mSrc.mIoData.out_buf_ch1 = (SRC16 *) (buffer); + mSrc.mIoData.out_buf_ch2 = 0; + mSrc.mIoData.output_count = bytes/sizeof(SRC16); + mSrc.srcConvert(); + ret = mSrc.mIoData.output_count*sizeof(SRC16); + if (ret > bytes) { + LOGE("read: buffer overrun"); + } + } +#else + if (srcReqd) { + LOGE("%s: sample rate mismatch HAL %d, driver %d", + __FUNCTION__, mSampleRate, mDriverRate); + status = INVALID_OPERATION; + goto error; + } + ret = ::read(mFd, buffer, hwReadBytes); +#endif + + // It is not optimal to mute after all the above processing but it is necessary to + // keep the clock sync from input device. It also avoids glitches on output streams due + // to EC being turned on and off + bool muted; + mHardware->getMicMute(&muted); + if (muted) { + LOGV("%s muted",__FUNCTION__); + memset(buffer, 0, bytes); + } + + LOGV("%s returns %d.",__FUNCTION__, (int)ret); + if (ret < 0) { + status = ret; + goto error; + } + + mTotalBuffersRead++; + return ret; + } + +error: + LOGE("read(): error, return %d", status); + standby(); + usleep(bytes * 1000 / frameSize() / sampleRate() * 1000); + return status; +} + +bool AudioHardware::AudioStreamInTegra::getStandby() const +{ + return mState == AUDIO_STREAM_IDLE; +} + +status_t AudioHardware::AudioStreamInTegra::standby() +{ + if (!mHardware) { + return NO_INIT; + } + + Mutex::Autolock lock(mHardware->mLock); + Mutex::Autolock lock2(mLock); + status_t status = NO_ERROR; + if (mState != AUDIO_STREAM_IDLE) { + LOGV("input %p going into standby", this); + mState = AUDIO_STREAM_IDLE; + // setDriver_l() will not try to lock mLock when called by doRouting_l() + mLocked = true; + mHardware->doRouting_l(); + mLocked = false; + status = mHardware->doStandby(mFdCtl, false, true); // input, standby + if (mFd >= 0) { + ::close(mFd); + mFd = -1; + } + if (mFdCtl >= 0) { + ::close(mFdCtl); + mFdCtl = -1; + } + } + + return status; +} + +// Called with mLock and mHardware->mLock held +status_t AudioHardware::AudioStreamInTegra::online_l() +{ + status_t status = NO_ERROR; + + if (mState < AUDIO_STREAM_NEW_RATE_REQ) { + + reopenReconfigDriver(); + + // configuration + struct tegra_audio_in_config config; + status = ::ioctl(mFdCtl, TEGRA_AUDIO_IN_GET_CONFIG, &config); + if (status < 0) { + LOGE("cannot read input config: %s", strerror(errno)); + return status; + } + config.stereo = AudioSystem::popCount(mChannels) == 2; + config.rate = mSampleRate; + status = ::ioctl(mFdCtl, TEGRA_AUDIO_IN_SET_CONFIG, &config); + + if (status < 0) { + LOGE("cannot set input config: %s", strerror(errno)); + if (::ioctl(mFdCtl, TEGRA_AUDIO_IN_GET_CONFIG, &config) == 0) { + if (config.stereo) { + mChannels = AudioSystem::CHANNEL_IN_STEREO; + } else { + mChannels = AudioSystem::CHANNEL_IN_MONO; + } + } + } + + // Use standby to flush the driver. mHardware->mLock should already be held + + mHardware->doStandby(mFdCtl, false, true); + if (mDevices & ~AudioSystem::DEVICE_IN_BLUETOOTH_SCO_HEADSET) { + status = mHardware->doStandby(mFdCtl, false, false); + } + + if (mState == AUDIO_STREAM_IDLE) { + mState = AUDIO_STREAM_CONFIG_REQ; + LOGV("input %p going online", this); + // setDriver_l() will not try to lock mLock when called by doRouting_l() + mLocked = true; + mHardware->doRouting_l(); + mLocked = false; + mTotalBuffersRead = 0; + mStartTimeNs = systemTime(); + } + } + + mDriverRate = mHardware->mHwInRate; + + mState = AUDIO_STREAM_CONFIGURED; + + return status; +} + +// serves a similar purpose as the init() method of other classes +void AudioHardware::AudioStreamInTegra::reopenReconfigDriver() +{ + // Need to "restart" the driver when changing the buffer configuration. + if (mFdCtl >= 0 && ::ioctl(mFdCtl, TEGRA_AUDIO_IN_STOP) < 0) { + LOGE("%s: could not stop recording: %s", __FUNCTION__, strerror(errno)); + } + if (mFd >= 0) { + ::close(mFd); + mFd = -1; + } + if (mFdCtl >= 0) { + ::close(mFdCtl); + mFdCtl = -1; + } + + // This does not have a retry loop to avoid blocking if another record session already in progress + mFd = ::open("/dev/audio1_in", O_RDWR); + if (mFd < 0) { + LOGE("open /dev/audio1_in failed: %s", strerror(errno)); + } + mFdCtl = ::open("/dev/audio1_in_ctl", O_RDWR); + if (mFdCtl < 0) { + LOGE("open /dev/audio1_in_ctl failed: %s", strerror(errno)); + if (mFd >= 0) { + ::close(mFd); + mFd = -1; + } + } else { + // here we would set mInit = true; + } +} + +status_t AudioHardware::AudioStreamInTegra::dump(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + result.append("AudioStreamInTegra::dump\n"); + snprintf(buffer, SIZE, "\tsample rate: %d\n", sampleRate()); + result.append(buffer); + snprintf(buffer, SIZE, "\tbuffer size: %d\n", bufferSize()); + result.append(buffer); + snprintf(buffer, SIZE, "\tchannels: %d\n", channels()); + result.append(buffer); + snprintf(buffer, SIZE, "\tformat: %d\n", format()); + result.append(buffer); + snprintf(buffer, SIZE, "\tmHardware: %p\n", mHardware); + result.append(buffer); + snprintf(buffer, SIZE, "\tmFd count: %d\n", mFd); + result.append(buffer); + snprintf(buffer, SIZE, "\tmState: %d\n", mState); + result.append(buffer); + snprintf(buffer, SIZE, "\tmRetryCount: %d\n", mRetryCount); + result.append(buffer); + ::write(fd, result.string(), result.size()); + return NO_ERROR; +} + +status_t AudioHardware::AudioStreamInTegra::setParameters(const String8& keyValuePairs) +{ + AudioParameter param = AudioParameter(keyValuePairs); + String8 key = String8(AudioParameter::keyRouting); + status_t status = NO_ERROR; + int device; + int source; + LOGV("AudioStreamInTegra::setParameters() %s", keyValuePairs.string()); + + // read source before device so that it is upto date when doRouting() is called + if (param.getInt(String8(AudioParameter::keyInputSource), source) == NO_ERROR) { + mSource = source; + param.remove(String8(AudioParameter::keyInputSource)); + } + + if (param.getInt(key, device) == NO_ERROR) { + LOGV("set input routing %x", device); + if (device & (device - 1)) { + status = BAD_VALUE; + } else { + mDevices = device; + if (!getStandby() && device != 0) { + status = mHardware->doRouting(); + } + } + param.remove(key); + } + + if (param.size()) { + status = BAD_VALUE; + } + return status; +} + +String8 AudioHardware::AudioStreamInTegra::getParameters(const String8& keys) +{ + AudioParameter param = AudioParameter(keys); + String8 value; + String8 key = String8(AudioParameter::keyRouting); + + if (param.get(key, value) == NO_ERROR) { + LOGV("get routing %x", mDevices); + param.addInt(key, (int)mDevices); + } + + LOGV("AudioStreamInTegra::getParameters() %s", param.toString().string()); + return param.toString(); +} + +unsigned int AudioHardware::AudioStreamInTegra::getInputFramesLost() const +{ + Mutex::Autolock _l(mLock); + unsigned int lostFrames = 0; + if (!getStandby()) { + unsigned int framesPerBuffer = bufferSize() / frameSize(); + uint64_t expectedFrames = ((systemTime() - mStartTimeNs) * mSampleRate) / 1000000000; + expectedFrames = (expectedFrames / framesPerBuffer) * framesPerBuffer; + uint64_t actualFrames = (uint64_t)mTotalBuffersRead * framesPerBuffer; + if (expectedFrames > actualFrames) { + lostFrames = (unsigned int)(expectedFrames - actualFrames); + LOGW("getInputFramesLost() expected %d actual %d lost %d", + (unsigned int)expectedFrames, (unsigned int)actualFrames, lostFrames); + } + } + + mTotalBuffersRead = 0; + mStartTimeNs = systemTime(); + + return lostFrames; +} + +// ---------------------------------------------------------------------------- + +extern "C" AudioHardwareInterface* createAudioHardware(void) { + AudioHardware *hw = new AudioHardware(); + for (unsigned tries = 0; tries < MAX_INIT_TRIES; ++tries) { + if (NO_ERROR == hw->init()) + break; + LOGW("AudioHardware::init failed soft, retrying"); + sleep(1); + } + if (NO_ERROR != hw->initCheck()) { + LOGE("AudioHardware::init failed hard"); + delete hw; + hw = NULL; + } + return hw; +} + +}; // namespace android diff --git a/libaudio/AudioHardware.h b/libaudio/AudioHardware.h new file mode 100644 index 0000000..6882832 --- /dev/null +++ b/libaudio/AudioHardware.h @@ -0,0 +1,341 @@ +/* +** Copyright 2008-2010, The Android Open-Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef ANDROID_AUDIO_HARDWARE_H +#define ANDROID_AUDIO_HARDWARE_H + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/threads.h> +#include <utils/SortedVector.h> + +#include <hardware_legacy/AudioHardwareBase.h> +#include <media/mediarecorder.h> +#include "AudioPostProcessor.h" +#ifdef USE_PROPRIETARY_AUDIO_EXTENSIONS +extern "C" { +#include "rate_conv.h" +} +#endif + +namespace android { + +#include <linux/cpcap_audio.h> +#include <linux/tegra_audio.h> + +#define AUDIO_HW_OUT_SAMPLERATE 44100 +#define AUDIO_HW_NUM_OUT_BUF 2 +#define AUDIO_HW_OUT_LATENCY_MS 0 + +// FIXME: this is a workaround for issue 3387419 with impact on latency +// to be removed when root cause is fixed +#define AUDIO_HW_NUM_OUT_BUF_LONG 4 + +#define AUDIO_HW_IN_SAMPLERATE 11025 // Default audio input sample rate +#define AUDIO_HW_IN_CHANNELS (AudioSystem::CHANNEL_IN_MONO) // Default audio input channel mask +#define AUDIO_HW_IN_BUFFERSIZE (4096) // Default audio input buffer size +#define AUDIO_HW_IN_FORMAT (AudioSystem::PCM_16_BIT) // Default audio input sample format + +enum { + AUDIO_HW_GAIN_SPKR_GAIN = 0, + AUDIO_HW_GAIN_MIC_GAIN, + AUDIO_HW_GAIN_NUM_DIRECTIONS +}; +enum { + AUDIO_HW_GAIN_USECASE_VOICE= 0, + AUDIO_HW_GAIN_USECASE_MM, + AUDIO_HW_GAIN_USECASE_VOICE_REC, + AUDIO_HW_GAIN_NUM_USECASES +}; +enum { + AUDIO_HW_GAIN_EARPIECE = 0, + AUDIO_HW_GAIN_SPEAKERPHONE, + AUDIO_HW_GAIN_HEADSET_W_MIC, + AUDIO_HW_GAIN_MONO_HEADSET, + AUDIO_HW_GAIN_HEADSET_NO_MIC, + AUDIO_HW_GAIN_EMU_DEVICE, + AUDIO_HW_GAIN_RSVD1, + AUDIO_HW_GAIN_RSVD2, + AUDIO_HW_GAIN_RSVD3, + AUDIO_HW_GAIN_RSVD4, + AUDIO_HW_GAIN_RSVD5, + AUDIO_HW_GAIN_NUM_PATHS +}; + +enum input_state { + AUDIO_STREAM_IDLE, + AUDIO_STREAM_CONFIG_REQ, + AUDIO_STREAM_NEW_RATE_REQ, + AUDIO_STREAM_CONFIGURED +}; + +class AudioHardware : public AudioHardwareBase +{ + class AudioStreamOutTegra; + class AudioStreamInTegra; + class AudioStreamSrc; + +public: + // AudioHardwareInterface + AudioHardware(); + virtual ~AudioHardware(); + virtual status_t initCheck(); + + virtual status_t setVoiceVolume(float volume); + virtual status_t setMasterVolume(float volume); + + virtual status_t setMode(int mode); + + // mic mute + virtual status_t setMicMute(bool state); + virtual status_t getMicMute(bool* state); + + virtual status_t setParameters(const String8& keyValuePairs); + virtual String8 getParameters(const String8& keys); + + // create I/O streams + virtual AudioStreamOut* openOutputStream( + uint32_t devices, + int *format=0, + uint32_t *channels=0, + uint32_t *sampleRate=0, + status_t *status=0); + + virtual AudioStreamIn* openInputStream( + + uint32_t devices, + int *format, + uint32_t *channels, + uint32_t *sampleRate, + status_t *status, + AudioSystem::audio_in_acoustics acoustics); + + virtual void closeOutputStream(AudioStreamOut* out); + virtual void closeInputStream(AudioStreamIn* in); + + virtual size_t getInputBufferSize(uint32_t sampleRate, int format, int channelCount); + // AudioHardwareBase provides default implementation + //virtual status_t dumpState(int fd, const Vector<String16>& args); + + // added by AudioHardware + status_t init(); + +protected: + // AudioHardwareBase provides default implementation + //virtual bool isModeInCall(int mode); + //virtual bool isInCall(); + + // AudioHardwareInterface + virtual status_t dump(int fd, const Vector<String16>& args); + + // added by AudioHardware + int getActiveInputRate(); + +private: + + status_t dumpInternals(int fd, const Vector<String16>& args); + uint32_t getInputSampleRate(uint32_t sampleRate); + status_t doStandby(int stop_fd, bool output, bool enable); + status_t doRouting_l(); + status_t doRouting(); + status_t setVolume_l(float v, int usecase); + uint8_t getGain(int direction, int usecase); + void readHwGainFile(); + + AudioStreamInTegra* getActiveInput_l(); + status_t setMicMute_l(bool state); + +#ifdef USE_PROPRIETARY_AUDIO_EXTENSIONS + class AudioStreamSrc { + public: + AudioStreamSrc(); + ~AudioStreamSrc(); + inline int inRate() {return mSrcInRate;}; + inline int outRate() {return mSrcOutRate;}; + inline bool initted() {return mSrcInitted;}; + void init(int inRate, int outRate); + inline void deinit() {mSrcInitted = false;}; + SRC_IO_T mIoData; + inline void srcConvert() { rate_convert(&mIoData, &mSrcObj, 0x0800); }; + private: + SRC_OBJ_T mSrcObj; + char * mSrcBuffer; + SRC_INIT_T mSrcInit; + int mSrcInRate; + int mSrcOutRate; + bool mSrcInitted; + }; +#endif + + class AudioStreamOutTegra : public AudioStreamOut { + public: + AudioStreamOutTegra(); + virtual ~AudioStreamOutTegra(); + status_t init(); + status_t initCheck(); + void setDriver_l(bool speaker, bool bluetooth, bool spdif, int sampleRate); + status_t set(AudioHardware* mHardware, + uint32_t devices, + int *pFormat, + uint32_t *pChannels, + uint32_t *pRate); + virtual uint32_t sampleRate() const { return AUDIO_HW_OUT_SAMPLERATE; } + // must be 32-bit aligned - driver only seems to like 4800 + virtual size_t bufferSize() const { return 4096; } + virtual uint32_t channels() const { return AudioSystem::CHANNEL_OUT_STEREO; } + virtual int format() const { return AudioSystem::PCM_16_BIT; } + virtual uint32_t latency() const { return (1000*AUDIO_HW_NUM_OUT_BUF*(bufferSize()/frameSize()))/sampleRate()+AUDIO_HW_OUT_LATENCY_MS; } + virtual status_t setVolume(float left, float right) { return INVALID_OPERATION; } + virtual ssize_t write(const void* buffer, size_t bytes); + void flush(); + virtual status_t standby(); + status_t online_l(); + virtual status_t dump(int fd, const Vector<String16>& args); + bool getStandby(); + virtual status_t setParameters(const String8& keyValuePairs); + virtual String8 getParameters(const String8& keys); + uint32_t devices() { return mDevices; } + virtual status_t getRenderPosition(uint32_t *dspFrames); + void lock() { mLock.lock(); } + void unlock() { mLock.unlock(); } + bool isLocked() { return mLocked; } + void setNumBufs(int numBufs); + + private: + AudioHardware* mHardware; + Mutex mLock; + int mFd; + int mFdCtl; + int mBtFd; + int mBtFdCtl; + int mBtFdIoCtl; + int mSpdifFd; + int mSpdifFdCtl; + int mStartCount; + int mRetryCount; + uint32_t mDevices; + Mutex mFdLock; + bool mIsSpkrEnabled; + bool mIsBtEnabled; + bool mIsSpdifEnabled; + bool mIsSpkrEnabledReq; + bool mIsBtEnabledReq; + bool mIsSpdifEnabledReq; + int16_t mSpareSample; + bool mHaveSpareSample; + int mState; +#ifdef USE_PROPRIETARY_AUDIO_EXTENSIONS + AudioStreamSrc mSrc; +#endif + bool mLocked; // setDriver() doesn't have to lock if true + int mDriverRate; + bool mInit; + }; + + class AudioStreamInTegra : public AudioStreamIn { + public: + AudioStreamInTegra(); + virtual ~AudioStreamInTegra(); + status_t set(AudioHardware* mHardware, + uint32_t devices, + int *pFormat, + uint32_t *pChannels, + uint32_t *pRate, + AudioSystem::audio_in_acoustics acoustics); + virtual size_t bufferSize() const { return mBufferSize; } + virtual uint32_t channels() const { return mChannels; } + virtual int format() const { return mFormat; } + virtual uint32_t sampleRate() const { return mSampleRate; } + virtual status_t setGain(float gain) { return INVALID_OPERATION; } + virtual ssize_t read(void* buffer, ssize_t bytes); + virtual status_t dump(int fd, const Vector<String16>& args); + virtual status_t standby(); + virtual status_t online_l(); + bool getStandby() const; + virtual status_t setParameters(const String8& keyValuePairs); + virtual String8 getParameters(const String8& keys); + virtual unsigned int getInputFramesLost() const; + uint32_t devices() { return mDevices; } + void setDriver_l(bool mic, bool bluetooth, int sampleRate); + int source() const { return mSource; } + void lock() { mLock.lock(); } + void unlock() { mLock.unlock(); } + bool isLocked() { return mLocked; } + + private: + void reopenReconfigDriver(); + + AudioHardware* mHardware; + mutable Mutex mLock; + int mFd; + int mFdCtl; + int mState; + int mRetryCount; + int mFormat; + uint32_t mChannels; + uint32_t mSampleRate; + size_t mBufferSize; + AudioSystem::audio_in_acoustics mAcoustics; + uint32_t mDevices; + bool mIsMicEnabled; + bool mIsBtEnabled; + int mSource; + // 20 millisecond scratch buffer + int16_t mInScratch[48000/50]; +#ifdef USE_PROPRIETARY_AUDIO_EXTENSIONS + AudioStreamSrc mSrc; +#endif + bool mLocked; // setDriver() doesn't have to lock if true + mutable uint32_t mTotalBuffersRead; + mutable nsecs_t mStartTimeNs; + int mDriverRate; + }; + + static const uint32_t inputSamplingRates[]; + bool mInit; + bool mMicMute; + bool mBluetoothNrec; + uint32_t mBluetoothId; + AudioStreamOutTegra* mOutput; + SortedVector <AudioStreamInTegra*> mInputs; + + struct cpcap_audio_stream mCurOutDevice; + struct cpcap_audio_stream mCurInDevice; + + friend class AudioStreamInTegra; + Mutex mLock; + + int mCpcapCtlFd; + int mHwOutRate; + int mHwInRate; + float mMasterVol; + float mVoiceVol; + uint8_t mCpcapGain[AUDIO_HW_GAIN_NUM_DIRECTIONS] + [AUDIO_HW_GAIN_NUM_USECASES] + [AUDIO_HW_GAIN_NUM_PATHS]; +#ifdef USE_PROPRIETARY_AUDIO_EXTENSIONS + AudioPostProcessor mAudioPP; +#endif + int mSpkrVolume; + int mMicVolume; +}; + +// ---------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_AUDIO_HARDWARE_H diff --git a/libaudio/AudioPolicyManager.cpp b/libaudio/AudioPolicyManager.cpp new file mode 100644 index 0000000..42887f4 --- /dev/null +++ b/libaudio/AudioPolicyManager.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "AudioPolicyManager" +//#define LOG_NDEBUG 0 +#include <utils/Log.h> +#include "AudioPolicyManager.h" +#include <media/mediarecorder.h> + +namespace android { + + + +// ---------------------------------------------------------------------------- +// Common audio policy manager code is implemented in AudioPolicyManagerBase class +// ---------------------------------------------------------------------------- + +// --- class factory + + +extern "C" AudioPolicyInterface* createAudioPolicyManager(AudioPolicyClientInterface *clientInterface) +{ + return new AudioPolicyManager(clientInterface); +} + +extern "C" void destroyAudioPolicyManager(AudioPolicyInterface *interface) +{ + delete interface; +} + +}; // namespace android diff --git a/libaudio/AudioPolicyManager.h b/libaudio/AudioPolicyManager.h new file mode 100644 index 0000000..b591c1f --- /dev/null +++ b/libaudio/AudioPolicyManager.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include <stdint.h> +#include <sys/types.h> +#include <utils/Timers.h> +#include <utils/Errors.h> +#include <utils/KeyedVector.h> +#include <hardware_legacy/AudioPolicyManagerBase.h> + + +namespace android { + +class AudioPolicyManager: public AudioPolicyManagerBase +{ + +public: + AudioPolicyManager(AudioPolicyClientInterface *clientInterface) + : AudioPolicyManagerBase(clientInterface) {} + + virtual ~AudioPolicyManager() {} + +protected: + // true is current platform implements a back microphone + virtual bool hasBackMicrophone() const { return false; } +#ifdef WITH_A2DP + // true is current platform supports suplication of notifications and ringtones over A2DP output + virtual bool a2dpUsedForSonification() const { return true; } +#endif + +}; +}; diff --git a/libaudio/AudioPostProcessor.cpp b/libaudio/AudioPostProcessor.cpp new file mode 100644 index 0000000..9d81654 --- /dev/null +++ b/libaudio/AudioPostProcessor.cpp @@ -0,0 +1,755 @@ +/* +** Copyright 2010, The Android Open-Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "AudioPostProcessor" +#include <fcntl.h> +#include <utils/Log.h> +#include "AudioHardware.h" +#include "AudioPostProcessor.h" +#include <sys/stat.h> +#include "mot_acoustics.h" +// hardware specific functions +extern uint16_t HC_CTO_AUDIO_MM_PARAMETER_TABLE[]; +/////////////////////////////////// +// Some logging #defines +#define ECNS_LOG_ENABLE_OFFSET 1 // 2nd word of the configuration buffer +#define ECNS_LOGGING_BITS 0xBFFF // 15 possible logpoints + +#define MOT_LOG_DELIMITER_START 0xFEED +#define MOT_LOG_DELIMITER_END 0xF00D +#define BASIC_DOCK_PROP_VALUE 0 + +#define ECNSLOGPATH "/data/ecns" +#define DOCK_PROP_PATH "/sys/class/switch/dock/dock_prop" + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) +#endif + +//#define DEBUG_TIMING +#ifdef DEBUG_TIMING +struct timeval mtv1, mtv2, mtv3, mtv4, mtv5, mtv6, mtv7, mtv8; +#define GETTIMEOFDAY gettimeofday +#else +#define GETTIMEOFDAY(a,b) +#endif + +namespace android { + +AudioPostProcessor::AudioPostProcessor() : + mEcnsScratchBuf(0), mLogNumPoints(0), mEcnsDlBuf(0), mEcnsThread(0) +{ + LOGD("%s",__FUNCTION__); + + // One-time CTO Audio configuration + mAudioMmEnvVar.cto_audio_mm_param_block_ptr = HC_CTO_AUDIO_MM_PARAMETER_TABLE; + mAudioMmEnvVar.cto_audio_mm_pcmlogging_buffer_block_ptr = mPcmLoggingBuf; + mAudioMmEnvVar.pcmlogging_buffer_block_size = ARRAY_SIZE(mPcmLoggingBuf); + mAudioMmEnvVar.cto_audio_mm_runtime_param_mem_ptr = mRuntimeParam; + mAudioMmEnvVar.cto_audio_mm_static_memory_block_ptr = mStaticMem; + mAudioMmEnvVar.cto_audio_mm_scratch_memory_block_ptr = mScratchMem; + mAudioMmEnvVar.accy = CTO_AUDIO_MM_ACCY_INVALID; + mAudioMmEnvVar.sample_rate = CTO_AUDIO_MM_SAMPL_44100; + + mEcnsThread = new EcnsThread(); + // Initial conditions for EC/NS + stopEcns(); +} + +AudioPostProcessor::~AudioPostProcessor() +{ + if (mEcnsRunning) { + LOGD("%s",__FUNCTION__); + enableEcns(false); + } +} + +uint32_t AudioPostProcessor::convOutDevToCTO(uint32_t outDev) +{ + int32_t dock_prop = 0; + // Only loudspeaker and audio docks are currently in this table + switch (outDev) { + case CPCAP_AUDIO_OUT_SPEAKER: + return CTO_AUDIO_MM_ACCY_LOUDSPEAKER; + case CPCAP_AUDIO_OUT_ANLG_DOCK_HEADSET: + dock_prop = read_dock_prop(DOCK_PROP_PATH); + if ((dock_prop < 0) || (dock_prop == BASIC_DOCK_PROP_VALUE)) { + // Basic dock, or error getting the dock ID + return CTO_AUDIO_MM_ACCY_INVALID; + } + else // speaker dock + return CTO_AUDIO_MM_ACCY_DOCK; + default: + return CTO_AUDIO_MM_ACCY_INVALID; + } +} + +uint32_t AudioPostProcessor::convRateToCto(uint32_t rate) +{ + switch (rate) { + case 44100: // Most likely. + return CTO_AUDIO_MM_SAMPL_44100; + case 8000: + return CTO_AUDIO_MM_SAMPL_8000; + case 11025: + return CTO_AUDIO_MM_SAMPL_11025; + case 12000: + return CTO_AUDIO_MM_SAMPL_12000; + case 16000: + return CTO_AUDIO_MM_SAMPL_16000; + case 22050: + return CTO_AUDIO_MM_SAMPL_22050; + case 24000: + return CTO_AUDIO_MM_SAMPL_24000; + case 32000: + return CTO_AUDIO_MM_SAMPL_32000; + case 48000: + return CTO_AUDIO_MM_SAMPL_48000; + default: + return CTO_AUDIO_MM_SAMPL_44100; + } +} + +void AudioPostProcessor::configMmAudio() +{ + if (mAudioMmEnvVar.accy != CTO_AUDIO_MM_ACCY_INVALID) { + LOGD("Configure CTO Audio MM processing"); + // fetch the corresponding runtime audio parameter + api_cto_audio_mm_param_parser(&(mAudioMmEnvVar), (int16_t *)0, (int16_t *)0); + // Initialize algorithm static memory + api_cto_audio_mm_init(&(mAudioMmEnvVar), (int16_t *)0, (int16_t *)0); + } else { + LOGD("CTO Audio MM processing is disabled."); + } +} + +void AudioPostProcessor::enableEcns(bool value) +{ + if (mEcnsEnabled!=value) { + LOGD("enableEcns(%s)",value?"true":"false"); + mEcnsEnabled = value; + if (value==false) { + mEcnsThread->requestExitAndWait(); + stopEcns(); + cleanupEcns(); + } + } +} + +void AudioPostProcessor::setAudioDev(struct cpcap_audio_stream *outDev, + struct cpcap_audio_stream *inDev, + bool is_bt, bool is_bt_ec, bool is_spdif) +{ + int32_t dock_prop = 0; + uint32_t mm_accy = convOutDevToCTO(outDev->id); + Mutex::Autolock lock(mMmLock); + + if (is_bt) { + if (is_bt_ec) + mEcnsMode = CTO_AUDIO_USECASE_NB_BLUETOOTH_WITH_ECNS; + else + mEcnsMode = CTO_AUDIO_USECASE_NB_BLUETOOTH_WITHOUT_ECNS; + } else if (is_spdif) // May need a more complex check here for HDMI vs. others + mEcnsMode = CTO_AUDIO_USECASE_NB_ACCY_1; + else if (outDev->id==CPCAP_AUDIO_OUT_HEADSET && inDev->id==CPCAP_AUDIO_IN_MIC1) + mEcnsMode = CTO_AUDIO_USECASE_NB_HEADSET_WITH_HANDSET_MIC; + else if (outDev->id==CPCAP_AUDIO_OUT_HEADSET) + mEcnsMode = CTO_AUDIO_USECASE_NB_HEADSET; + else if (outDev->id==CPCAP_AUDIO_OUT_ANLG_DOCK_HEADSET) { + dock_prop = read_dock_prop(DOCK_PROP_PATH); + if ((dock_prop < 0) || (dock_prop == BASIC_DOCK_PROP_VALUE)) + // Basic dock, or error getting the dock ID + mEcnsMode = CTO_AUDIO_USECASE_NB_ACCY_1; + else + // Speaker Dock + mEcnsMode = CTO_AUDIO_USECASE_NB_DEDICATED_DOCK; + } + else + mEcnsMode = CTO_AUDIO_USECASE_NB_SPKRPHONE; + + if (mEcnsEnabled) { + // We may need to reset the EC/NS if the output device changed. + stopEcns(); + } + + LOGV("setAudioDev %d", outDev->id); + if (mm_accy != mAudioMmEnvVar.accy) { + mAudioMmEnvVar.accy = mm_accy; + configMmAudio(); + } +} + +// Setting the HW sampling rate may require reconfiguration of audio processing. +void AudioPostProcessor::setPlayAudioRate(int sampRate) +{ + uint32_t rate = convRateToCto(sampRate); + Mutex::Autolock lock(mMmLock); + + LOGD("AudioPostProcessor::setPlayAudioRate %d", sampRate); + if (rate != mAudioMmEnvVar.sample_rate) { + mAudioMmEnvVar.sample_rate = rate; + configMmAudio(); + } +} + +void AudioPostProcessor::doMmProcessing(void * buffer, int numSamples) +{ + Mutex::Autolock lock(mMmLock); + + if (mAudioMmEnvVar.accy != CTO_AUDIO_MM_ACCY_INVALID && + !mEcnsEnabled) { + // Apply the CTO audio effects in-place. + mAudioMmEnvVar.frame_size = numSamples; + api_cto_audio_mm_main(&mAudioMmEnvVar, (int16_t *)buffer, (int16_t *)buffer); + } +} + +int AudioPostProcessor::getEcnsRate (void) +{ + return mEcnsRate; +} + +void AudioPostProcessor::initEcns(int rate, int bytes) +{ + LOGD("%s",__FUNCTION__); + CTO_AUDIO_USECASES_CTRL mode; + Mutex::Autolock lock(mEcnsBufLock); + + if (rate != 8000 && rate != 16000) { + LOGW("Invalid rate for EC/NS, disabling"); + mEcnsEnabled = 0; + mEcnsRunning = 0; + return; + } + mode = mEcnsMode; + mEcnsRate = rate; + if (mEcnsRate==16000) { + // Offset to the 16K (WB) block in the coefficients file + mode = CTO_AUDIO_USECASES_CTRL(mode + CTO_AUDIO_USECASE_WB_HANDSET); + } + LOGD("%s for mode %d at %d size %d",__FUNCTION__, mode, mEcnsRate, bytes); + mEcnsCtrl.framesize = bytes/2; + mEcnsCtrl.micFlag = 0; // 0- one mic. 1- dual mic. 2- three mic. + mEcnsCtrl.digital_mode = (rate == 8000) ? 0 : 1; // 8K or 16K + mEcnsCtrl.usecase = mode; + mMemBlocks.staticMemory_1 = mStaticMemory_1; + mMemBlocks.staticMemory_2 = NULL; + mMemBlocks.mot_datalog = mMotDatalog; + mMemBlocks.gainTableMemory = mParamTable; + + FILE * fp = fopen("/system/etc/voip_aud_params.bin", "r"); + if (fp) { + if (fread(mParamTable, sizeof(mParamTable), 1, fp) < 1) { + LOGE("Cannot read VOIP parameter file. Disabling EC/NS."); + fclose(fp); + mEcnsEnabled = 0; + mEcnsRunning = 0; + return; + } + fclose(fp); + } + else { + LOGE("Cannot open VOIP parameter file. Disabling EC/NS."); + mEcnsEnabled = 0; + mEcnsRunning = 0; + return; + } + + mEcnsRunning = 1; + mEcnsOutBuf = 0; + mEcnsOutBufSize = 0; + mEcnsOutBufReadOffset = 0; + + // Send setup parameters to the EC/NS module, init the module. + API_MOT_SETUP(&mEcnsCtrl, &mMemBlocks); + API_MOT_INIT(&mEcnsCtrl, &mMemBlocks); +} + +void AudioPostProcessor::stopEcns (void) +{ + AutoMutex lock(mEcnsBufLock); + if (mEcnsRunning) { + LOGD("%s",__FUNCTION__); + mEcnsRunning = 0; + } +} + +void AudioPostProcessor::cleanupEcns(void) +{ + AutoMutex lock(mEcnsBufLock); + mEcnsRate = 0; + if (mEcnsScratchBuf) { + free(mEcnsScratchBuf); + mEcnsScratchBuf = 0; + } + mEcnsScratchBufSize = 0; + mEcnsOutFd = -1; + + if (mEcnsDlBuf) { + free(mEcnsDlBuf); + mEcnsDlBuf = 0; + } + mEcnsDlBufSize = 0; + // In case write() is blocked, set it free. + mEcnsBufCond.signal(); + + ecnsLogToFile(); +} + + +// Returns: Bytes written (actually "to-be-written" by EC/NS thread). +int AudioPostProcessor::writeDownlinkEcns(int fd, void * buffer, bool stereo, + int bytes, Mutex *fdLock) +{ + int written = 0; + mEcnsBufLock.lock(); + if (mEcnsEnabled && !mEcnsRunning) { + long usecs = 20*1000; + // Give the read thread a chance to catch up. + LOGV("%s: delay %d msecs for ec/ns to start",__FUNCTION__, (int)(usecs/1000)); + mEcnsBufLock.unlock(); + usleep(usecs); + mEcnsBufLock.lock(); + written = bytes; // Pretend all data was consumed even if ecns isn't running + } + if (mEcnsRunning) { + // Only run through here after initEcns has been done by read thread. + mEcnsOutFd = fd; + mEcnsOutBuf = buffer; + mEcnsOutBufSize = bytes; + mEcnsOutBufReadOffset = 0; + mEcnsOutFdLockp = fdLock; + mEcnsOutStereo = stereo; + if (mEcnsBufCond.waitRelative(mEcnsBufLock, seconds(1)) != NO_ERROR) { + LOGE("%s: Capture thread is stalled.", __FUNCTION__); + } + if (mEcnsOutBufSize != 0) + LOGD("%s: Buffer not consumed", __FUNCTION__); + else + written = bytes; // All data consumed + } + mEcnsBufLock.unlock(); + return written; +} + +// Returns: Bytes read. +int AudioPostProcessor::read(int fd, void * buffer, int bytes, int rate) +{ + if (mEcnsEnabled) { + return mEcnsThread->readData(fd, buffer, bytes, rate, this); + } + ssize_t ret; + ret = ::read(fd, buffer, bytes); + if (ret < 0) + LOGE("Error reading from audio in: %s", strerror(errno)); + return (int)ret; +} + +// Returns: Bytes processed. +int AudioPostProcessor::applyUplinkEcns(void * buffer, int bytes, int rate) +{ + static int16 ul_gbuff2[160]; + int16_t *dl_buf; + int16_t *ul_buf = (int16_t *)buffer; + int dl_buf_bytes=0; + // The write thread could have left us with one frame of data in the + // driver when we started reading. + static bool onetime; + + if (!mEcnsEnabled) + return 0; + + LOGV("%s %d bytes at %d Hz",__FUNCTION__, bytes, rate); + if (mEcnsEnabled && !mEcnsRunning) { + initEcns(rate, bytes); + onetime=true; + } + + // In case the rate switched.. + if (mEcnsEnabled && rate != mEcnsRate) { + stopEcns(); + initEcns(rate, bytes); + onetime=true; + } + + if (!mEcnsRunning) { + LOGE("EC/NS failed to init, read returns."); + return -1; + } + + mEcnsBufLock.lock(); + // Need a contiguous stereo playback buffer in the end. + if (bytes*2 != mEcnsDlBufSize || !mEcnsDlBuf) { + if (mEcnsDlBuf) + free(mEcnsDlBuf); + mEcnsDlBuf = (int16_t*)malloc(bytes*2); + if (mEcnsDlBuf) + mEcnsDlBufSize = bytes*2; + } + dl_buf = mEcnsDlBuf; + if (!dl_buf) { + mEcnsBufLock.unlock(); + return -1; + } + + // Need to gather appropriate amount of downlink speech. + // Take oldest scratch data first. The scratch buffer holds fractions of buffers + // that were too small for processing. + if (mEcnsScratchBuf && mEcnsScratchBufSize) { + dl_buf_bytes = mEcnsScratchBufSize > bytes ? bytes:mEcnsScratchBufSize; + memcpy(dl_buf, mEcnsScratchBuf, dl_buf_bytes); + //LOGD("Took %d bytes from mEcnsScratchBuf", dl_buf_bytes); + mEcnsScratchBufSize -= dl_buf_bytes; + if (mEcnsScratchBufSize==0) { + // This should always be true. + free(mEcnsScratchBuf); + mEcnsScratchBuf = 0; + mEcnsScratchBufSize = 0; + } + } + // Take fresh data from write thread second. + if (dl_buf_bytes < bytes) { + int bytes_to_copy = mEcnsOutBufSize - mEcnsOutBufReadOffset; + bytes_to_copy = bytes_to_copy + dl_buf_bytes > bytes? + bytes-dl_buf_bytes:bytes_to_copy; + if (bytes_to_copy) { + memcpy((void *)((unsigned int)dl_buf+dl_buf_bytes), + (void *)((unsigned int)mEcnsOutBuf+mEcnsOutBufReadOffset), + bytes_to_copy); + dl_buf_bytes += bytes_to_copy; + } + //LOGD("Took %d bytes from mEcnsOutBuf. Need %d more.", bytes_to_copy, + // bytes-dl_buf_bytes); + mEcnsOutBufReadOffset += bytes_to_copy; + if (mEcnsOutBufSize - mEcnsOutBufReadOffset < bytes) { + // We've depleted the output buffer, it's smaller than one uplink "frame". + // First take any unused data into scratch, then free the write thread. + if (mEcnsScratchBuf) { + LOGE("Memleak - coding error"); + free(mEcnsScratchBuf); + } + if (mEcnsOutBufSize - mEcnsOutBufReadOffset > 0) { + if ((mEcnsScratchBuf=malloc(mEcnsOutBufSize - mEcnsOutBufReadOffset)) == 0) { + LOGE("%s: Alloc failed, scratch data lost.",__FUNCTION__); + } else { + mEcnsScratchBufSize = mEcnsOutBufSize - mEcnsOutBufReadOffset; + //LOGD("....store %d bytes into scratch buf %p", + // mEcnsScratchBufSize, mEcnsScratchBuf); + memcpy(mEcnsScratchBuf, + (void *)((unsigned int)mEcnsOutBuf+mEcnsOutBufReadOffset), + mEcnsScratchBufSize); + } + } + mEcnsOutBuf = 0; + mEcnsOutBufSize = 0; + mEcnsOutBufReadOffset = 0; + //LOGD("Signal write thread - need data."); + mEcnsBufCond.signal(); + } + } + + mEcnsBufLock.unlock(); + + // Pad downlink with zeroes as last resort. We have to process the UL speech. + if (dl_buf_bytes < bytes) { + LOGV("%s:EC/NS Starved for downlink data. have %d need %d.", + __FUNCTION__,dl_buf_bytes, bytes); + memset(&dl_buf[dl_buf_bytes/sizeof(int16_t)], + 0, + bytes-dl_buf_bytes); + } + + // Do Echo Cancellation + GETTIMEOFDAY(&mtv4, NULL); + API_MOT_LOG_RESET(&mEcnsCtrl, &mMemBlocks); + API_MOT_DOWNLINK(&mEcnsCtrl, &mMemBlocks, (int16*)dl_buf, (int16*)ul_buf, &(ul_gbuff2[0])); + API_MOT_UPLINK(&mEcnsCtrl, &mMemBlocks, (int16*)dl_buf, (int16*)ul_buf, &(ul_gbuff2[0])); + + // Playback the echo-cancelled speech to driver. + // Include zero padding. Our echo canceller needs a consistent path. + if (mEcnsOutStereo) { + // Convert up to stereo, in place. + for (int i = bytes/2-1; i >= 0; i--) { + dl_buf[i*2] = dl_buf[i]; + dl_buf[i*2+1] = dl_buf[i]; + } + dl_buf_bytes *= 2; + } + GETTIMEOFDAY(&mtv5, NULL); + if (mEcnsOutFd != -1) { + mEcnsOutFdLockp->lock(); + ::write(mEcnsOutFd, &dl_buf[0], + bytes*(mEcnsOutStereo?2:1)); + mEcnsOutFdLockp->unlock(); + } + // Do the CTO SuperAPI internal logging. + // (Do this after writing output to avoid adding latency.) + GETTIMEOFDAY(&mtv6, NULL); + ecnsLogToRam(bytes); + return bytes; +} +void AudioPostProcessor::ecnsLogToRam (int bytes) +{ + uint16_t *logp; + int mode = mEcnsMode + (mEcnsRate==16000?CTO_AUDIO_USECASE_WB_HANDSET:0); + uint16_t *audioProfile = &mParamTable[AUDIO_PROFILE_PARAMETER_BLOCK_WORD16_SIZE*mode]; + + if (audioProfile[ECNS_LOG_ENABLE_OFFSET] & ECNS_LOGGING_BITS) { + if (!mLogBuf[0]) { + mLogNumPoints = 0; + mLogOffset = 0; + LOGE("EC/NS AUDIO LOGGER CONFIGURATION:"); + LOGE("log enable %04X", + audioProfile[ECNS_LOG_ENABLE_OFFSET]); + mkdir(ECNSLOGPATH, 00770); + for (uint16_t i=1; i>0; i<<=1) { + if (i&ECNS_LOGGING_BITS&audioProfile[ECNS_LOG_ENABLE_OFFSET]) { + mLogNumPoints++; + } + } + LOGE("Number of log points is %d.", mLogNumPoints); + logp = mMotDatalog; + mLogSize = 10*60*50*bytes; + for (int i=0; i<mLogNumPoints; i++) { + // Allocate 10 minutes of logging per point + mLogBuf[i]=(char *)malloc(mLogSize); + if (!mLogBuf[i]) { + LOGE("%s: Memory allocation failed.", __FUNCTION__); + for (int j=0; j<i; j++) { + free(mLogBuf[j]); + mLogBuf[j]=0; + } + return; + } + } + } + if (mLogOffset+bytes > mLogSize) + return; + logp = mMotDatalog; + for (int i=0; i<mLogNumPoints; i++) { + if (mLogBuf[i]) { + mLogPoint[i] = logp[1]; + memcpy(&mLogBuf[i][mLogOffset], &logp[4], logp[2]*sizeof(uint16_t)); + logp += 4+logp[2]; + } else { + LOGE("EC/NS logging enabled, but memory not allocated"); + } + } + mLogOffset += bytes; + } +} + +void AudioPostProcessor::ecnsLogToFile() +{ + if (mLogNumPoints && mLogOffset > 16000*2) { + for (int i=0; i<mLogNumPoints; i++) { + FILE * fp; + char fname[80]; + sprintf(fname, ECNSLOGPATH"/log-0x%04X.pcm", mLogPoint[i]); + fp = fopen((const char *)fname, "w"); + if (fp) { + LOGE("Writing %d bytes to %s", mLogOffset, fname); + fwrite(mLogBuf[i], mLogOffset, 1, fp); + fclose(fp); + } else { + LOGE("Problem writing to %s", fname); + } + } + } + mLogOffset = 0; +} + +int AudioPostProcessor::read_dock_prop(char const *path) +{ + int fd = -1; + const size_t SIZE = 7; + static int already_warned = -1; + char buffer[SIZE]; + /* the docks come with a property id AC000 for basic docks + and AC002 for speaker docks, numbers might change, keeping + them for now. + */ + unsigned long int basic_dock_prop = 0xAC000; + unsigned long int spkr_dock_prop; + + buffer[SIZE - 1] = '\0'; + fd = open(path, O_RDONLY); + if (fd >= 0) { + int amt = ::read(fd, buffer, SIZE-1); + if (amt != SIZE-1) { + LOGE("Incomplete dock property read, cannot validate dock"); + return -1; + } + spkr_dock_prop = strtoul(buffer, NULL, 16); + if (spkr_dock_prop <= 0) { + LOGE("dock property conversion error"); + return -EINVAL; + } + close(fd); + LOGV("buffer = %s, spkr_dock_prop = 0x%lX", buffer, spkr_dock_prop); + spkr_dock_prop = spkr_dock_prop ^ basic_dock_prop; + LOGV("dock_prop returned = %lX", spkr_dock_prop); + return spkr_dock_prop; + } else { + if (already_warned == -1) { + LOGE("read_dock_prop failed to open %s\n", path); + already_warned = 1; + } + return -errno; + } +} +// --------------------------------------------------------------------------------------------- +// Echo Canceller thread +// Needed to isolate the EC/NS module from scheduling jitter of it's clients. +// +AudioPostProcessor::EcnsThread::EcnsThread() : + mReadBuf(0), mIsRunning(0) +{ +} + +AudioPostProcessor::EcnsThread::~EcnsThread() +{ +} + +int AudioPostProcessor::EcnsThread::readData(int fd, void * buffer, int bytes, int rate, + AudioPostProcessor * pp) +{ + LOGV("%s: read %d bytes at %d rate", __FUNCTION__, bytes, rate); + Mutex::Autolock lock(mEcnsReadLock); + mProcessor = pp; + mFd = fd; + mClientBuf = buffer; + mReadSize = bytes; + mRate = rate; + if (!mIsRunning) { + LOGD("Create (run) the ECNS thread"); + run("AudioPostProcessor::EcnsThread", ANDROID_PRIORITY_HIGHEST); + mIsRunning = true; + } + if (mEcnsReadCond.waitRelative(mEcnsReadLock, seconds(1)) != NO_ERROR) { + LOGE("%s: ECNS thread is stalled.", __FUNCTION__); + mClientBuf = 0; + return -1; + } + return bytes; +} + +bool AudioPostProcessor::EcnsThread::threadLoop() +{ +#ifdef DEBUG_TIMING + int count = 0; + int small_jitter = 0; + int medium_jitter = 0; + int large_jitter = 0; +#endif + ssize_t ret1 = 0, ret2; + struct timeval tv1, tv2; + int usecs; + bool half_done = false; + + LOGD("%s: Enter thread loop size %d rate %d", __FUNCTION__, + mReadSize, mRate); + + mReadBuf = (int16_t *) malloc(mReadSize); + if (!mReadBuf) + goto error; + + while (mProcessor->isEcnsEnabled()) { + GETTIMEOFDAY(&mtv1, NULL); + if (!half_done) + ret1 = ::read(mFd, mReadBuf, mReadSize/2); + GETTIMEOFDAY(&mtv2, NULL); + ret2 = ::read(mFd, (char *)mReadBuf+mReadSize/2, mReadSize/2); + if(!mProcessor->isEcnsEnabled()) + goto error; + if (ret1 <= 0 || ret2 <= 0) { + LOGE("%s: Problem reading.", __FUNCTION__); + goto error; + } + GETTIMEOFDAY(&mtv3, NULL); + mEcnsReadLock.lock(); + mProcessor->applyUplinkEcns(mReadBuf, mReadSize, mRate); + if (mClientBuf && mReadSize) { + // Give the buffer to the client. + memcpy(mClientBuf, mReadBuf, mReadSize); + // Avoid read overflow by reading before signaling the similar-priority read thread. + ret1 = ::read(mFd, mReadBuf, mReadSize/2); + half_done = true; + GETTIMEOFDAY(&mtv7, NULL); + mEcnsReadCond.signal(); + mClientBuf = 0; + } else { + half_done = false; + LOGD("%s: Read overflow (ECNS sanity preserved)", __FUNCTION__); + } + mEcnsReadLock.unlock(); + GETTIMEOFDAY(&mtv8, NULL); + +#ifdef DEBUG_TIMING + count++; + tv1.tv_sec = mtv1.tv_sec; + tv1.tv_usec = mtv1.tv_usec; + tv2.tv_sec = mtv8.tv_sec; + tv2.tv_usec = mtv8.tv_usec; + // Compare first and last timestamps + tv2.tv_sec -= tv1.tv_sec; + if(tv2.tv_usec < tv1.tv_usec) { + tv2.tv_sec--; + tv2.tv_usec = 1000000 + tv2.tv_usec - tv1.tv_usec; + } else { + tv2.tv_usec = tv2.tv_usec - tv1.tv_usec; + } + usecs = tv2.tv_usec + tv2.tv_sec*1000000; + if (usecs > 25000) { + if (usecs > 30000) + large_jitter++; + else + medium_jitter++; + LOGD("jitter: usecs = %d should be 20000", usecs); + LOGD("Point 1 ( start): %03d.%06d:", (int)mtv1.tv_sec, (int)mtv1.tv_usec); + LOGD("Point 2 (after read1): %03d.%06d:", (int)mtv2.tv_sec, (int)mtv2.tv_usec); + LOGD("Point 3 (after read2): %03d.%06d:", (int)mtv3.tv_sec, (int)mtv3.tv_usec); + LOGD("Point 4 (before ECNS): %03d.%06d:", (int)mtv4.tv_sec, (int)mtv4.tv_usec); + LOGD("Point 5 (after ECNS): %03d.%06d:", (int)mtv5.tv_sec, (int)mtv5.tv_usec); + LOGD("Point 6 (after write): %03d.%06d:", (int)mtv6.tv_sec, (int)mtv6.tv_usec); + LOGD("Point 7 (before sgnl): %03d.%06d:", (int)mtv7.tv_sec, (int)mtv7.tv_usec); + LOGD("Point 8 (after unlck): %03d.%06d:", (int)mtv8.tv_sec, (int)mtv8.tv_usec); + } else if ((usecs > 22000) || (usecs < 18000)) { + small_jitter++; + LOGD("jitter: usecs = %d should be 20000", usecs); + } + if ((count % 500)== 0) { + LOGD("====================================== Statistics ==========================="); + LOGD(" After %d seconds:", count/50); + LOGD(" Small jitters- %d (%02.5f%%)", small_jitter, ((float)small_jitter)*100/count); + LOGD(" Medium jitters- %d (%02.5f%%)", medium_jitter, ((float)medium_jitter)*100/count); + LOGD(" Large jitters- %d (%02.5f%%)", large_jitter, ((float)large_jitter)*100/count); + LOGD("============================================================================="); + } +#endif + } +error: + LOGD("%s: Exit thread loop, enabled = %d", __FUNCTION__,mProcessor->isEcnsEnabled()); + if (mReadBuf) { + free (mReadBuf); + mReadBuf = 0; + } + mIsRunning = false; + return false; +} + +} //namespace android diff --git a/libaudio/AudioPostProcessor.h b/libaudio/AudioPostProcessor.h new file mode 100644 index 0000000..0d8201d --- /dev/null +++ b/libaudio/AudioPostProcessor.h @@ -0,0 +1,122 @@ +/* +** Copyright 2010, The Android Open-Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef ANDROID_AUDIO_POST_PROCESSOR_H +#define ANDROID_AUDIO_POST_PROCESSOR_H +#ifdef USE_PROPRIETARY_AUDIO_EXTENSIONS + +extern "C" { +#include "cto_audio_mm.h" +} +#include "mot_acoustics.h" + +namespace android { + +class AudioPostProcessor +{ +public: + AudioPostProcessor(); + ~AudioPostProcessor(); + void setPlayAudioRate(int rate); + void setAudioDev(struct cpcap_audio_stream *outDev, + struct cpcap_audio_stream *inDev, + bool is_bt, bool is_bt_ec, bool is_spdif); + void doMmProcessing(void * buffer, int numSamples); + int getEcnsRate(void); + + void enableEcns(bool value); + int writeDownlinkEcns(int fd, void * buffer, + bool stereo, int bytes, Mutex * fdLockp); + int read(int fd, void * buffer, int bytes, int rate); + int applyUplinkEcns(void * buffer, int bytes, int rate); + bool isEcnsEnabled(void) { return mEcnsEnabled; }; + +private: + void configMmAudio(void); + uint32_t convOutDevToCTO(uint32_t outDev); + uint32_t convRateToCto(uint32_t rate); + + void initEcns(int rate, int bytes); + void stopEcns(void); + void cleanupEcns(void); + void ecnsLogToRam(int bytes); + void ecnsLogToFile(void); + int read_dock_prop(char const *path); + + // CTO Multimedia Audio Processing storage buffers + int16_t mPcmLoggingBuf[((CTO_AUDIO_MM_DATALOGGING_BUFFER_BLOCK_BYTESIZE)/2)]; + uint32_t mNoiseEst[((CTO_AUDIO_MM_NOISE_EST_BLOCK_BYTESIZE)/4)]; + uint16_t mRuntimeParam[((CTO_AUDIO_MM_RUNTIME_PARAM_BYTESIZE)/2)]; + uint16_t mStaticMem[((CTO_AUDIO_MM_STATICMEM_BLOCK_BYTESIZE)/2)]; + uint16_t mScratchMem[((CTO_AUDIO_MM_SCRATCHMEM_BLOCK_BYTESIZE)/2)]; + CTO_AUDIO_MM_ENV_VAR mAudioMmEnvVar; + Mutex mMmLock; + + // EC/NS configuration etc. + Mutex mEcnsBufLock; + Condition mEcnsBufCond; // Signal to unblock write thread + bool mEcnsEnabled; // Enabled by libaudio + bool mEcnsRunning; // ECNS module init done by read thread + int mEcnsRate; + void * mEcnsScratchBuf; // holding cell for downlink speech "consumed". + int mEcnsScratchBufSize; + void * mEcnsOutBuf; // buffer from downlink "write()" + int mEcnsOutBufSize; + int mEcnsOutBufReadOffset; + int mEcnsOutFd; // fd pointing to output driver + Mutex * mEcnsOutFdLockp; + CTO_AUDIO_USECASES_CTRL mEcnsMode; + char * mLogBuf[15]; + int mLogOffset; + int mLogSize; + int mLogNumPoints; + uint16_t mLogPoint[15]; + int16_t * mEcnsDlBuf; + int mEcnsDlBufSize; + bool mEcnsOutStereo; + + // EC/NS Module memory + T_MOT_MEM_BLOCKS mMemBlocks; + T_MOT_CTRL mEcnsCtrl; + uint16_t mStaticMemory_1[API_MOT_STATIC_MEM_WORD16_SIZE]; + uint16_t mMotDatalog[API_MOT_DATALOGGING_MEM_WORD16_SIZE]; + uint16_t mParamTable[AUDIO_PROFILE_PARAMETER_BLOCK_WORD16_SIZE*CTO_AUDIO_USECASE_TOTAL_NUMBER]; + + // ECNS Thread + class EcnsThread : public Thread { +public: + EcnsThread(); + ~EcnsThread(); + int readData(int fd, void * buffer, int bytes, int rate, + AudioPostProcessor * pp); +private: + bool threadLoop(); + Mutex mEcnsReadLock; + Condition mEcnsReadCond; // Signal to unblock read thread + AudioPostProcessor * mProcessor; + void * mClientBuf; + int mReadSize; + int16_t * mReadBuf; + int mFd; + int mRate; + bool mIsRunning; + }; + sp <EcnsThread> mEcnsThread; +}; +} // namespace android + +#endif // USE_PROPRIETARY_AUDIO_EXTENSIONS +#endif // ANDROID_AUDIO_POST_PROCESSOR_H diff --git a/libaudio/MODULE_LICENSE_APACHE2 b/libaudio/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/libaudio/MODULE_LICENSE_APACHE2 diff --git a/libaudio/NOTICE b/libaudio/NOTICE new file mode 100644 index 0000000..3237da6 --- /dev/null +++ b/libaudio/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2008-2009, The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + |