summaryrefslogtreecommitdiff
path: root/libaudio
diff options
context:
space:
mode:
authorYing Wang <wangying@google.com>2011-02-18 12:00:38 -0800
committerYing Wang <wangying@google.com>2011-02-18 12:00:38 -0800
commit2896e59ac088f73318bc010fce3a4a31f0c3ff45 (patch)
tree4a65630d0ae505020ae02b54ca7f2600592ded89 /libaudio
parentb5381afd299fd924758820613c02a3fc8218aebc (diff)
downloadwingray-2896e59ac088f73318bc010fce3a4a31f0c3ff45.tar.gz
Split out new device wingray.
Change-Id: I19c27ff5f3961e9adb6ca25a6f458920fdafa932
Diffstat (limited to 'libaudio')
-rw-r--r--libaudio/Android.mk74
-rw-r--r--libaudio/AudioHardware.cpp1899
-rw-r--r--libaudio/AudioHardware.h341
-rw-r--r--libaudio/AudioPolicyManager.cpp44
-rw-r--r--libaudio/AudioPolicyManager.h46
-rw-r--r--libaudio/AudioPostProcessor.cpp755
-rw-r--r--libaudio/AudioPostProcessor.h122
-rw-r--r--libaudio/MODULE_LICENSE_APACHE20
-rw-r--r--libaudio/NOTICE190
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
+