Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Kang libaudio for gb

  • Loading branch information...
commit a973cf123ff755010a7de935362dc0fe03b831b4 1 parent dff5c46
Adnan Begovic Decad3nce authored
54 libaudio/Android.mk
View
@@ -0,0 +1,54 @@
+ifeq ($(TARGET_DEVICE),epic)
+
+LOCAL_PATH:= $(call my-dir)
+
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES:= aplay.c alsa_pcm.c alsa_mixer.c
+LOCAL_MODULE:= aplay
+LOCAL_SHARED_LIBRARIES:= libc libcutils
+LOCAL_MODULE_TAGS:= debug
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES:= arec.c alsa_pcm.c
+LOCAL_MODULE:= arec
+LOCAL_SHARED_LIBRARIES:= libc libcutils
+LOCAL_MODULE_TAGS:= debug
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES:= amix.c alsa_mixer.c
+LOCAL_MODULE:= amix
+LOCAL_SHARED_LIBRARIES := libc libcutils
+LOCAL_MODULE_TAGS:= debug
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES:= AudioHardware.cpp alsa_mixer.c alsa_pcm.c
+LOCAL_MODULE:= libaudio
+LOCAL_STATIC_LIBRARIES:= libaudiointerface
+LOCAL_SHARED_LIBRARIES:= libc libcutils libutils libmedia libhardware_legacy
+ifeq ($(BOARD_HAVE_BLUETOOTH),true)
+ LOCAL_SHARED_LIBRARIES += liba2dp
+endif
+
+ifeq ($(TARGET_SIMULATOR),true)
+ LOCAL_LDLIBS += -ldl
+else
+ LOCAL_SHARED_LIBRARIES += libdl
+endif
+
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES:= AudioPolicyManager.cpp
+LOCAL_MODULE:= libaudiopolicy
+LOCAL_STATIC_LIBRARIES:= libaudiopolicybase
+LOCAL_SHARED_LIBRARIES:= libc libcutils libutils libmedia
+ifeq ($(BOARD_HAVE_BLUETOOTH),true)
+ LOCAL_CFLAGS += -DWITH_A2DP
+endif
+include $(BUILD_SHARED_LIBRARY)
+
+endif
1,966 libaudio/AudioHardware.cpp
View
@@ -0,0 +1,1966 @@
+/*
+** 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.
+*/
+
+#include <math.h>
+
+#define LOG_NDEBUG 0
+
+#define LOG_TAG "AudioHardware"
+
+#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 <sys/resource.h>
+#include <dlfcn.h>
+#include <fcntl.h>
+
+#include "AudioHardware.h"
+#include <media/AudioRecord.h>
+#include <hardware_legacy/power.h>
+
+extern "C" {
+#include "alsa_audio.h"
+}
+
+
+namespace android {
+
+const uint32_t AudioHardware::inputSamplingRates[] = {
+ 8000, 11025, 16000, 22050, 44100
+};
+
+// trace driver operations for dump
+//
+//#define DRIVER_TRACE
+
+enum {
+ DRV_NONE,
+ DRV_PCM_OPEN,
+ DRV_PCM_CLOSE,
+ DRV_PCM_WRITE,
+ DRV_PCM_READ,
+ DRV_MIXER_OPEN,
+ DRV_MIXER_CLOSE,
+ DRV_MIXER_GET,
+ DRV_MIXER_SEL
+};
+
+#ifdef DRIVER_TRACE
+#define TRACE_DRIVER_IN(op) mDriverOp = op;
+#define TRACE_DRIVER_OUT mDriverOp = DRV_NONE;
+#else
+#define TRACE_DRIVER_IN(op)
+#define TRACE_DRIVER_OUT
+#endif
+
+// ----------------------------------------------------------------------------
+
+const char *AudioHardware::inputPathNameDefault = "Default";
+const char *AudioHardware::inputPathNameCamcorder = "Camcorder";
+const char *AudioHardware::inputPathNameVoiceRecognition = "Voice Recognition";
+const char *AudioHardware::inputPathNameVoiceCommunication = "Voice Communication";
+
+AudioHardware::AudioHardware() :
+ mInit(false),
+ mMicMute(false),
+ mPcm(NULL),
+ mMixer(NULL),
+ mPcmOpenCnt(0),
+ mMixerOpenCnt(0),
+ mInCallAudioMode(false),
+ mInputSource(AUDIO_SOURCE_DEFAULT),
+ mBluetoothNrec(true),
+ mTTYMode(TTY_MODE_OFF),
+ mDriverOp(DRV_NONE)
+{
+ mInit = true;
+}
+
+AudioHardware::~AudioHardware()
+{
+ for (size_t index = 0; index < mInputs.size(); index++) {
+ closeInputStream(mInputs[index].get());
+ }
+ mInputs.clear();
+ closeOutputStream((AudioStreamOut*)mOutput.get());
+
+ if (mMixer) {
+ TRACE_DRIVER_IN(DRV_MIXER_CLOSE)
+ mixer_close(mMixer);
+ TRACE_DRIVER_OUT
+ }
+ if (mPcm) {
+ TRACE_DRIVER_IN(DRV_PCM_CLOSE)
+ pcm_close(mPcm);
+ TRACE_DRIVER_OUT
+ }
+
+ mInit = false;
+}
+
+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)
+{
+ sp <AudioStreamOutALSA> out;
+ status_t rc;
+
+ { // scope for the lock
+ Mutex::Autolock lock(mLock);
+
+ // only one output stream allowed
+ if (mOutput != 0) {
+ if (status) {
+ *status = INVALID_OPERATION;
+ }
+ return NULL;
+ }
+
+ out = new AudioStreamOutALSA();
+
+ rc = out->set(this, devices, format, channels, sampleRate);
+ if (rc == NO_ERROR) {
+ mOutput = out;
+ }
+ }
+
+ if (rc != NO_ERROR) {
+ if (out != 0) {
+ out.clear();
+ }
+ }
+ if (status) {
+ *status = rc;
+ }
+
+ return out.get();
+}
+
+void AudioHardware::closeOutputStream(AudioStreamOut* out) {
+ sp <AudioStreamOutALSA> spOut;
+ {
+ Mutex::Autolock lock(mLock);
+ if (mOutput == 0 || mOutput.get() != out) {
+ LOGW("Attempt to close invalid output stream");
+ return;
+ }
+ spOut = mOutput;
+ mOutput.clear();
+ }
+ spOut.clear();
+}
+
+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)) {
+ if (status) {
+ *status = BAD_VALUE;
+ }
+ return NULL;
+ }
+
+ status_t rc = NO_ERROR;
+ sp <AudioStreamInALSA> in;
+
+ { // scope for the lock
+ Mutex::Autolock lock(mLock);
+
+ in = new AudioStreamInALSA();
+ rc = in->set(this, devices, format, channels, sampleRate, acoustic_flags);
+ if (rc == NO_ERROR) {
+ mInputs.add(in);
+ }
+ }
+
+ if (rc != NO_ERROR) {
+ if (in != 0) {
+ in.clear();
+ }
+ }
+ if (status) {
+ *status = rc;
+ }
+
+ LOGV("AudioHardware::openInputStream()%p", in.get());
+ return in.get();
+}
+
+void AudioHardware::closeInputStream(AudioStreamIn* in) {
+
+ sp<AudioStreamInALSA> spIn;
+ {
+ Mutex::Autolock lock(mLock);
+
+ ssize_t index = mInputs.indexOf((AudioStreamInALSA *)in);
+ if (index < 0) {
+ LOGW("Attempt to close invalid input stream");
+ return;
+ }
+ spIn = mInputs[index];
+ mInputs.removeAt(index);
+ }
+ LOGV("AudioHardware::closeInputStream()%p", in);
+ spIn.clear();
+}
+
+
+status_t AudioHardware::setMode(int mode)
+{
+ sp<AudioStreamOutALSA> spOut;
+ sp<AudioStreamInALSA> spIn;
+ status_t status;
+
+ // Mutex acquisition order is always out -> in -> hw
+ AutoMutex lock(mLock);
+
+ spOut = mOutput;
+ while (spOut != 0) {
+ if (!spOut->checkStandby()) {
+ int cnt = spOut->prepareLock();
+ mLock.unlock();
+ spOut->lock();
+ mLock.lock();
+ // make sure that another thread did not change output state while the
+ // mutex is released
+ if ((spOut == mOutput) && (cnt == spOut->standbyCnt())) {
+ break;
+ }
+ spOut->unlock();
+ spOut = mOutput;
+ } else {
+ spOut.clear();
+ }
+ }
+ // spOut is not 0 here only if the output is active
+
+ spIn = getActiveInput_l();
+ while (spIn != 0) {
+ int cnt = spIn->prepareLock();
+ mLock.unlock();
+ spIn->lock();
+ mLock.lock();
+ // make sure that another thread did not change input state while the
+ // mutex is released
+ if ((spIn == getActiveInput_l()) && (cnt == spIn->standbyCnt())) {
+ break;
+ }
+ spIn->unlock();
+ spIn = getActiveInput_l();
+ }
+ // spIn is not 0 here only if the input is active
+
+ int prevMode = mMode;
+ status = AudioHardwareBase::setMode(mode);
+ LOGV("setMode() : new %d, old %d", mMode, prevMode);
+ if (status == NO_ERROR) {
+ if (mMode == AudioSystem::MODE_IN_CALL && !mInCallAudioMode) {
+ if (spOut != 0) {
+ LOGV("setMode() in call force output standby");
+ spOut->doStandby_l();
+ }
+ if (spIn != 0) {
+ LOGV("setMode() in call force input standby");
+ spIn->doStandby_l();
+ }
+
+ LOGV("setMode() openPcmOut_l()");
+ openPcmOut_l();
+ openMixer_l();
+ setInputSource_l(AUDIO_SOURCE_DEFAULT);
+ mInCallAudioMode = true;
+ }
+ if (mMode == AudioSystem::MODE_NORMAL && mInCallAudioMode) {
+ setInputSource_l(mInputSource);
+ if (mMixer != NULL) {
+ TRACE_DRIVER_IN(DRV_MIXER_GET)
+ struct mixer_ctl *ctl= mixer_get_control(mMixer, "Playback Path", 0);
+ TRACE_DRIVER_OUT
+ if (ctl != NULL) {
+ LOGV("setMode() reset Playback Path to RCV");
+ TRACE_DRIVER_IN(DRV_MIXER_SEL)
+ mixer_ctl_select(ctl, "RCV");
+ TRACE_DRIVER_OUT
+ }
+ }
+ LOGV("setMode() closePcmOut_l()");
+ closeMixer_l();
+ closePcmOut_l();
+
+ if (spOut != 0) {
+ LOGV("setMode() off call force output standby");
+ spOut->doStandby_l();
+ }
+ if (spIn != 0) {
+ LOGV("setMode() off call force input standby");
+ spIn->doStandby_l();
+ }
+
+ mInCallAudioMode = false;
+ }
+ }
+
+ if (spIn != 0) {
+ spIn->unlock();
+ }
+ if (spOut != 0) {
+ spOut->unlock();
+ }
+
+ return status;
+}
+
+status_t AudioHardware::setMicMute(bool state)
+{
+ LOGV("setMicMute(%d) mMicMute %d", state, mMicMute);
+ sp<AudioStreamInALSA> spIn;
+ {
+ AutoMutex lock(mLock);
+ if (mMicMute != state) {
+ mMicMute = state;
+ spIn = getActiveInput_l();
+ }
+ }
+
+ if (spIn != 0) {
+ spIn->standby();
+ }
+
+ 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_NREC_VALUE_ON[] = "on";
+ const char TTY_MODE_KEY[] = "tty_mode";
+ const char TTY_MODE_VALUE_OFF[] = "tty_off";
+ const char TTY_MODE_VALUE_VCO[] = "tty_vco";
+ const char TTY_MODE_VALUE_HCO[] = "tty_hco";
+ const char TTY_MODE_VALUE_FULL[] = "tty_full";
+
+ key = String8(BT_NREC_KEY);
+ if (param.get(key, value) == NO_ERROR) {
+ if (value == BT_NREC_VALUE_ON) {
+ mBluetoothNrec = true;
+ } else {
+ mBluetoothNrec = false;
+ LOGD("Turning noise reduction and echo cancellation off for BT "
+ "headset");
+ }
+ param.remove(String8(BT_NREC_KEY));
+ }
+
+ key = String8(TTY_MODE_KEY);
+ if (param.get(key, value) == NO_ERROR) {
+ int ttyMode;
+ if (value == TTY_MODE_VALUE_OFF) {
+ ttyMode = TTY_MODE_OFF;
+ } else if (value == TTY_MODE_VALUE_VCO) {
+ ttyMode = TTY_MODE_VCO;
+ } else if (value == TTY_MODE_VALUE_HCO) {
+ ttyMode = TTY_MODE_HCO;
+ } else if (value == TTY_MODE_VALUE_FULL) {
+ ttyMode = TTY_MODE_FULL;
+ } else {
+ return BAD_VALUE;
+ }
+
+ if (ttyMode != mTTYMode) {
+ LOGV("new tty mode %d", ttyMode);
+ mTTYMode = ttyMode;
+ if (mOutput != 0 && mMode == AudioSystem::MODE_IN_CALL) {
+ setIncallPath_l(mOutput->device());
+ }
+ }
+ param.remove(String8(TTY_MODE_KEY));
+ }
+
+ return NO_ERROR;
+}
+
+String8 AudioHardware::getParameters(const String8& keys)
+{
+ AudioParameter request = AudioParameter(keys);
+ AudioParameter reply = AudioParameter();
+
+ LOGV("getParameters() %s", keys.string());
+
+ return reply.toString();
+}
+
+size_t AudioHardware::getInputBufferSize(uint32_t sampleRate, int format, int channelCount)
+{
+ 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;
+ }
+ if (sampleRate != 8000 && sampleRate != 11025 && sampleRate != 16000 &&
+ sampleRate != 22050 && sampleRate != 44100) {
+ LOGW("getInputBufferSize bad sample rate: %d", sampleRate);
+ return 0;
+ }
+
+ return AudioStreamInALSA::getBufferSize(sampleRate, channelCount);
+}
+
+
+status_t AudioHardware::setVoiceVolume(float volume)
+{
+ LOGD("### setVoiceVolume: %f", volume);
+
+ AutoMutex lock(mLock);
+
+ uint32_t device = AudioSystem::DEVICE_OUT_EARPIECE;
+ if (mOutput != 0) {
+ device = mOutput->device();
+ }
+ if (mMixer != NULL) {
+ struct mixer_ctl *ctl;
+ const char* name;
+ TRACE_DRIVER_IN(DRV_MIXER_GET)
+ switch (device) {
+ case AudioSystem::DEVICE_OUT_EARPIECE:
+ name = "Playback Volume";
+ break;
+ case AudioSystem::DEVICE_OUT_WIRED_HEADSET:
+ case AudioSystem::DEVICE_OUT_WIRED_HEADPHONE:
+ name = "Playback Headset Volume";
+ break;
+ default:
+ name = "Playback Spkr Volume";
+ break;
+ }
+ ctl= mixer_get_control(mMixer, name, 0);
+ TRACE_DRIVER_OUT
+
+ if (ctl != NULL) {
+ LOGV("setVoiceVolume() set %s to %f", name, volume);
+ TRACE_DRIVER_IN(DRV_MIXER_SET)
+ mixer_ctl_set(ctl, volume * 100);
+ TRACE_DRIVER_OUT
+ }
+ }
+
+ return NO_ERROR;
+}
+
+status_t AudioHardware::setMasterVolume(float volume)
+{
+ LOGV("Set master volume to %f.\n", volume);
+ // We return an error code here to let the audioflinger do in-software
+ // volume on top of the maximum volume that we set through the SND API.
+ // return error - software mixer will handle it
+
+ return -1;
+}
+
+static const int kDumpLockRetries = 50;
+static const int kDumpLockSleep = 20000;
+
+static bool tryLock(Mutex& mutex)
+{
+ bool locked = false;
+ for (int i = 0; i < kDumpLockRetries; ++i) {
+ if (mutex.tryLock() == NO_ERROR) {
+ locked = true;
+ break;
+ }
+ usleep(kDumpLockSleep);
+ }
+ return locked;
+}
+
+status_t AudioHardware::dump(int fd, const Vector<String16>& args)
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+
+ bool locked = tryLock(mLock);
+ if (!locked) {
+ snprintf(buffer, SIZE, "\n\tAudioHardware maybe deadlocked\n");
+ } else {
+ mLock.unlock();
+ }
+
+ snprintf(buffer, SIZE, "\tInit %s\n", (mInit) ? "OK" : "Failed");
+ result.append(buffer);
+ snprintf(buffer, SIZE, "\tMic Mute %s\n", (mMicMute) ? "ON" : "OFF");
+ result.append(buffer);
+ snprintf(buffer, SIZE, "\tmPcm: %p\n", mPcm);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "\tmPcmOpenCnt: %d\n", mPcmOpenCnt);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "\tmMixer: %p\n", mMixer);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "\tmMixerOpenCnt: %d\n", mMixerOpenCnt);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "\tIn Call Audio Mode %s\n",
+ (mInCallAudioMode) ? "ON" : "OFF");
+ result.append(buffer);
+ snprintf(buffer, SIZE, "\tInput source %d\n", mInputSource);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "\tmDriverOp: %d\n", mDriverOp);
+ result.append(buffer);
+
+ snprintf(buffer, SIZE, "\n\tmOutput %p dump:\n", mOutput.get());
+ result.append(buffer);
+ write(fd, result.string(), result.size());
+ if (mOutput != 0) {
+ mOutput->dump(fd, args);
+ }
+
+ snprintf(buffer, SIZE, "\n\t%d inputs opened:\n", mInputs.size());
+ write(fd, buffer, strlen(buffer));
+ for (size_t i = 0; i < mInputs.size(); i++) {
+ snprintf(buffer, SIZE, "\t- input %d dump:\n", i);
+ write(fd, buffer, strlen(buffer));
+ mInputs[i]->dump(fd, args);
+ }
+
+ return NO_ERROR;
+}
+
+status_t AudioHardware::setIncallPath_l(uint32_t device)
+{
+ LOGV("setIncallPath_l: device %x", device);
+
+ if (mMixer != NULL) {
+ TRACE_DRIVER_IN(DRV_MIXER_GET)
+ struct mixer_ctl *ctl= mixer_get_control(mMixer, "Voice Call Path", 0);
+ TRACE_DRIVER_OUT
+ LOGE_IF(ctl == NULL, "setIncallPath_l() could not get mixer ctl");
+ if (ctl != NULL) {
+ LOGV("setIncallPath_l() Voice Call Path, (%x)", device);
+ const char *router = getVoiceRouteFromDevice(device);
+ TRACE_DRIVER_IN(DRV_MIXER_SEL)
+ mixer_ctl_select(ctl,router);
+ TRACE_DRIVER_OUT
+ }
+ }
+ return NO_ERROR;
+}
+
+struct pcm *AudioHardware::openPcmOut_l()
+{
+ LOGD("openPcmOut_l() mPcmOpenCnt: %d", mPcmOpenCnt);
+ if (mPcmOpenCnt++ == 0) {
+ if (mPcm != NULL) {
+ LOGE("openPcmOut_l() mPcmOpenCnt == 0 and mPcm == %p\n", mPcm);
+ mPcmOpenCnt--;
+ return NULL;
+ }
+ unsigned flags = PCM_OUT;
+
+ flags |= (AUDIO_HW_OUT_PERIOD_MULT - 1) << PCM_PERIOD_SZ_SHIFT;
+ flags |= (AUDIO_HW_OUT_PERIOD_CNT - PCM_PERIOD_CNT_MIN) << PCM_PERIOD_CNT_SHIFT;
+
+ TRACE_DRIVER_IN(DRV_PCM_OPEN)
+ mPcm = pcm_open(flags);
+ TRACE_DRIVER_OUT
+ if (!pcm_ready(mPcm)) {
+ LOGE("openPcmOut_l() cannot open pcm_out driver: %s\n", pcm_error(mPcm));
+ TRACE_DRIVER_IN(DRV_PCM_CLOSE)
+ pcm_close(mPcm);
+ TRACE_DRIVER_OUT
+ mPcmOpenCnt--;
+ mPcm = NULL;
+ }
+ }
+ return mPcm;
+}
+
+void AudioHardware::closePcmOut_l()
+{
+ LOGD("closePcmOut_l() mPcmOpenCnt: %d", mPcmOpenCnt);
+ if (mPcmOpenCnt == 0) {
+ LOGE("closePcmOut_l() mPcmOpenCnt == 0");
+ return;
+ }
+
+ if (--mPcmOpenCnt == 0) {
+ TRACE_DRIVER_IN(DRV_PCM_CLOSE)
+ pcm_close(mPcm);
+ TRACE_DRIVER_OUT
+ mPcm = NULL;
+ }
+}
+
+struct mixer *AudioHardware::openMixer_l()
+{
+ LOGV("openMixer_l() mMixerOpenCnt: %d", mMixerOpenCnt);
+ if (mMixerOpenCnt++ == 0) {
+ if (mMixer != NULL) {
+ LOGE("openMixer_l() mMixerOpenCnt == 0 and mMixer == %p\n", mMixer);
+ mMixerOpenCnt--;
+ return NULL;
+ }
+ TRACE_DRIVER_IN(DRV_MIXER_OPEN)
+ mMixer = mixer_open();
+ TRACE_DRIVER_OUT
+ if (mMixer == NULL) {
+ LOGE("openMixer_l() cannot open mixer");
+ mMixerOpenCnt--;
+ return NULL;
+ }
+ }
+ return mMixer;
+}
+
+void AudioHardware::closeMixer_l()
+{
+ LOGV("closeMixer_l() mMixerOpenCnt: %d", mMixerOpenCnt);
+ if (mMixerOpenCnt == 0) {
+ LOGE("closeMixer_l() mMixerOpenCnt == 0");
+ return;
+ }
+
+ if (--mMixerOpenCnt == 0) {
+ TRACE_DRIVER_IN(DRV_MIXER_CLOSE)
+ mixer_close(mMixer);
+ TRACE_DRIVER_OUT
+ mMixer = NULL;
+ }
+}
+
+const char *AudioHardware::getOutputRouteFromDevice(uint32_t device)
+{
+ switch (device) {
+ case AudioSystem::DEVICE_OUT_EARPIECE:
+ return "RCV";
+ case AudioSystem::DEVICE_OUT_SPEAKER:
+ if (mMode == AudioSystem::MODE_RINGTONE) return "RING_SPK";
+ else return "SPK";
+ case AudioSystem::DEVICE_OUT_WIRED_HEADPHONE:
+ if (mMode == AudioSystem::MODE_RINGTONE) return "RING_NO_MIC";
+ else return "HP_NO_MIC";
+ case AudioSystem::DEVICE_OUT_WIRED_HEADSET:
+ if (mMode == AudioSystem::MODE_RINGTONE) return "RING_HP";
+ else return "HP";
+ case (AudioSystem::DEVICE_OUT_SPEAKER|AudioSystem::DEVICE_OUT_WIRED_HEADPHONE):
+ case (AudioSystem::DEVICE_OUT_SPEAKER|AudioSystem::DEVICE_OUT_WIRED_HEADSET):
+ if (mMode == AudioSystem::MODE_RINGTONE) return "RING_SPK_HP";
+ else return "SPK_HP";
+ case AudioSystem::DEVICE_OUT_BLUETOOTH_SCO:
+ case AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
+ case AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT:
+ return "BT";
+ default:
+ return "OFF";
+ }
+}
+
+const char *AudioHardware::getVoiceRouteFromDevice(uint32_t device)
+{
+ switch (device) {
+ case AudioSystem::DEVICE_OUT_EARPIECE:
+ return "RCV";
+ case AudioSystem::DEVICE_OUT_SPEAKER:
+ return "SPK";
+ case AudioSystem::DEVICE_OUT_WIRED_HEADPHONE:
+ case AudioSystem::DEVICE_OUT_WIRED_HEADSET:
+ switch (mTTYMode) {
+ case TTY_MODE_VCO:
+ return "TTY_VCO";
+ case TTY_MODE_HCO:
+ return "TTY_HCO";
+ case TTY_MODE_FULL:
+ return "TTY_FULL";
+ case TTY_MODE_OFF:
+ default:
+ if (device == AudioSystem::DEVICE_OUT_WIRED_HEADPHONE) {
+ return "HP_NO_MIC";
+ } else {
+ return "HP";
+ }
+ }
+ case AudioSystem::DEVICE_OUT_BLUETOOTH_SCO:
+ case AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
+ case AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT:
+ return "BT";
+ default:
+ return "OFF";
+ }
+}
+
+const char *AudioHardware::getInputRouteFromDevice(uint32_t device)
+{
+ if (mMicMute) {
+ return "MIC OFF";
+ }
+
+ switch (device) {
+ case AudioSystem::DEVICE_IN_BUILTIN_MIC:
+ return "Main Mic";
+ case AudioSystem::DEVICE_IN_WIRED_HEADSET:
+ return "Hands Free Mic";
+ case AudioSystem::DEVICE_IN_BLUETOOTH_SCO_HEADSET:
+ return "BT Sco Mic";
+ default:
+ return "MIC OFF";
+ }
+}
+
+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
+sp <AudioHardware::AudioStreamInALSA> AudioHardware::getActiveInput_l()
+{
+ sp< AudioHardware::AudioStreamInALSA> spIn;
+
+ 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]->checkStandby()) {
+ spIn = mInputs[i];
+ break;
+ }
+ }
+
+ return spIn;
+}
+
+status_t AudioHardware::setInputSource_l(audio_source source)
+{
+ LOGV("setInputSource_l(%d)", source);
+ if (source != mInputSource) {
+ if ((source == AUDIO_SOURCE_DEFAULT) || (mMode != AudioSystem::MODE_IN_CALL)) {
+ if (mMixer) {
+ TRACE_DRIVER_IN(DRV_MIXER_GET)
+ struct mixer_ctl *ctl= mixer_get_control(mMixer, "Input Source", 0);
+ TRACE_DRIVER_OUT
+ if (ctl == NULL) {
+ return NO_INIT;
+ }
+ const char* sourceName;
+ switch (source) {
+ case AUDIO_SOURCE_DEFAULT: // intended fall-through
+ case AUDIO_SOURCE_MIC:
+ sourceName = inputPathNameDefault;
+ break;
+ case AUDIO_SOURCE_VOICE_COMMUNICATION:
+ sourceName = inputPathNameVoiceCommunication;
+ break;
+ case AUDIO_SOURCE_CAMCORDER:
+ sourceName = inputPathNameCamcorder;
+ break;
+ case AUDIO_SOURCE_VOICE_RECOGNITION:
+ sourceName = inputPathNameVoiceRecognition;
+ break;
+ case AUDIO_SOURCE_VOICE_UPLINK: // intended fall-through
+ case AUDIO_SOURCE_VOICE_DOWNLINK: // intended fall-through
+ case AUDIO_SOURCE_VOICE_CALL: // intended fall-through
+ default:
+ return NO_INIT;
+ }
+ LOGV("mixer_ctl_select, Input Source, (%s)", sourceName);
+ TRACE_DRIVER_IN(DRV_MIXER_SEL)
+ mixer_ctl_select(ctl, sourceName);
+ TRACE_DRIVER_OUT
+ }
+ }
+ mInputSource = source;
+ }
+
+ return NO_ERROR;
+}
+
+
+//------------------------------------------------------------------------------
+// AudioStreamOutALSA
+//------------------------------------------------------------------------------
+
+AudioHardware::AudioStreamOutALSA::AudioStreamOutALSA() :
+ mHardware(0), mPcm(0), mMixer(0), mRouteCtl(0),
+ mStandby(true), mDevices(0), mChannels(AUDIO_HW_OUT_CHANNELS),
+ mSampleRate(AUDIO_HW_OUT_SAMPLERATE), mBufferSize(AUDIO_HW_OUT_PERIOD_BYTES),
+ mDriverOp(DRV_NONE), mStandbyCnt(0), mSleepReq(false)
+{
+}
+
+status_t AudioHardware::AudioStreamOutALSA::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;
+ mDevices = devices;
+
+ // 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;
+ }
+
+ if (pFormat) *pFormat = lFormat;
+ if (pChannels) *pChannels = lChannels;
+ if (pRate) *pRate = lRate;
+
+ mChannels = lChannels;
+ mSampleRate = lRate;
+ mBufferSize = AUDIO_HW_OUT_PERIOD_BYTES;
+
+ return NO_ERROR;
+}
+
+AudioHardware::AudioStreamOutALSA::~AudioStreamOutALSA()
+{
+ standby();
+}
+
+ssize_t AudioHardware::AudioStreamOutALSA::write(const void* buffer, size_t bytes)
+{
+ // LOGV("AudioStreamOutALSA::write(%p, %u)", buffer, bytes);
+ status_t status = NO_INIT;
+ const uint8_t* p = static_cast<const uint8_t*>(buffer);
+ int ret;
+
+ if (mHardware == NULL) return NO_INIT;
+
+ if (mSleepReq) {
+ // 10ms are always shorter than the time to reconfigure the audio path
+ // which is the only condition when mSleepReq would be true.
+ usleep(10000);
+ }
+
+ { // scope for the lock
+
+ AutoMutex lock(mLock);
+
+ if (mStandby) {
+ AutoMutex hwLock(mHardware->lock());
+
+ LOGD("AudioStreamOutALSA::write: AudioHardware pcm playback is exiting standby.");
+ acquire_wake_lock (PARTIAL_WAKE_LOCK, "AudioOutLock");
+
+ sp<AudioStreamInALSA> spIn = mHardware->getActiveInput_l();
+ while (spIn != 0) {
+ int cnt = spIn->prepareLock();
+ mHardware->lock().unlock();
+ // Mutex acquisition order is always out -> in -> hw
+ spIn->lock();
+ mHardware->lock().lock();
+ // make sure that another thread did not change input state
+ // while the mutex is released
+ if ((spIn == mHardware->getActiveInput_l()) &&
+ (cnt == spIn->standbyCnt())) {
+ LOGV("AudioStreamOutALSA::write() force input standby");
+ spIn->close_l();
+ break;
+ }
+ spIn->unlock();
+ spIn = mHardware->getActiveInput_l();
+ }
+ // spIn is not 0 here only if the input was active and has been
+ // closed above
+
+ // open output before input
+ open_l();
+
+ if (spIn != 0) {
+ if (spIn->open_l() != NO_ERROR) {
+ spIn->doStandby_l();
+ }
+ spIn->unlock();
+ }
+ if (mPcm == NULL) {
+ release_wake_lock("AudioOutLock");
+ goto Error;
+ }
+ mStandby = false;
+ }
+
+ TRACE_DRIVER_IN(DRV_PCM_WRITE)
+ ret = pcm_write(mPcm,(void*) p, bytes);
+ TRACE_DRIVER_OUT
+
+ if (ret == 0) {
+ return bytes;
+ }
+ LOGW("write error: %d", errno);
+ status = -errno;
+ }
+Error:
+
+ standby();
+
+ // Simulate audio output timing in case of error
+ usleep((((bytes * 1000) / frameSize()) * 1000) / sampleRate());
+
+ return status;
+}
+
+status_t AudioHardware::AudioStreamOutALSA::standby()
+{
+ if (mHardware == NULL) return NO_INIT;
+
+ AutoMutex lock(mLock);
+
+ { // scope for the AudioHardware lock
+ AutoMutex hwLock(mHardware->lock());
+
+ doStandby_l();
+ }
+
+ return NO_ERROR;
+}
+
+void AudioHardware::AudioStreamOutALSA::doStandby_l()
+{
+ mStandbyCnt++;
+
+ if (!mStandby) {
+ LOGD("AudioHardware pcm playback is going to standby.");
+ release_wake_lock("AudioOutLock");
+ mStandby = true;
+ }
+
+ close_l();
+}
+
+void AudioHardware::AudioStreamOutALSA::close_l()
+{
+ if (mMixer) {
+ mHardware->closeMixer_l();
+ mMixer = NULL;
+ mRouteCtl = NULL;
+ }
+ if (mPcm) {
+ mHardware->closePcmOut_l();
+ mPcm = NULL;
+ }
+}
+
+status_t AudioHardware::AudioStreamOutALSA::open_l()
+{
+ LOGV("open pcm_out driver");
+ mPcm = mHardware->openPcmOut_l();
+ if (mPcm == NULL) {
+ return NO_INIT;
+ }
+
+ mMixer = mHardware->openMixer_l();
+ if (mMixer) {
+ LOGV("open playback normal");
+ TRACE_DRIVER_IN(DRV_MIXER_GET)
+ mRouteCtl = mixer_get_control(mMixer, "Playback Path", 0);
+ TRACE_DRIVER_OUT
+ }
+ if (mHardware->mode() != AudioSystem::MODE_IN_CALL) {
+ const char *route = mHardware->getOutputRouteFromDevice(mDevices);
+ LOGV("write() wakeup setting route %s", route);
+ if (mRouteCtl) {
+ TRACE_DRIVER_IN(DRV_MIXER_SEL)
+ mixer_ctl_select(mRouteCtl, route);
+ TRACE_DRIVER_OUT
+ }
+ }
+ return NO_ERROR;
+}
+
+status_t AudioHardware::AudioStreamOutALSA::dump(int fd, const Vector<String16>& args)
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+
+ bool locked = tryLock(mLock);
+ if (!locked) {
+ snprintf(buffer, SIZE, "\n\t\tAudioStreamOutALSA maybe deadlocked\n");
+ } else {
+ mLock.unlock();
+ }
+
+ snprintf(buffer, SIZE, "\t\tmHardware: %p\n", mHardware);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "\t\tmPcm: %p\n", mPcm);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "\t\tmMixer: %p\n", mMixer);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "\t\tmRouteCtl: %p\n", mRouteCtl);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "\t\tStandby %s\n", (mStandby) ? "ON" : "OFF");
+ result.append(buffer);
+ snprintf(buffer, SIZE, "\t\tmDevices: 0x%08x\n", mDevices);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "\t\tmChannels: 0x%08x\n", mChannels);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "\t\tmSampleRate: %d\n", mSampleRate);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "\t\tmBufferSize: %d\n", mBufferSize);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "\t\tmDriverOp: %d\n", mDriverOp);
+ result.append(buffer);
+
+ ::write(fd, result.string(), result.size());
+
+ return NO_ERROR;
+}
+
+bool AudioHardware::AudioStreamOutALSA::checkStandby()
+{
+ return mStandby;
+}
+
+status_t AudioHardware::AudioStreamOutALSA::setParameters(const String8& keyValuePairs)
+{
+ AudioParameter param = AudioParameter(keyValuePairs);
+
+ status_t status = NO_ERROR;
+ int device;
+ LOGD("AudioStreamOutALSA::setParameters() %s", keyValuePairs.string());
+
+ if (mHardware == NULL) return NO_INIT;
+
+ {
+ AutoMutex lock(mLock);
+
+ if (param.getInt(String8(AudioParameter::keyRouting), device) == NO_ERROR)
+ {
+ if (device != 0) {
+ AutoMutex hwLock(mHardware->lock());
+
+ if (mDevices != (uint32_t)device) {
+ mDevices = (uint32_t)device;
+
+ if (mHardware->mode() != AudioSystem::MODE_IN_CALL) {
+ doStandby_l();
+ }
+ }
+ if (mHardware->mode() == AudioSystem::MODE_IN_CALL) {
+ mHardware->setIncallPath_l(device);
+ }
+ }
+ param.remove(String8(AudioParameter::keyRouting));
+ }
+ }
+
+ if (param.size()) {
+ status = BAD_VALUE;
+ }
+
+ return status;
+}
+
+String8 AudioHardware::AudioStreamOutALSA::getParameters(const String8& keys)
+{
+ AudioParameter param = AudioParameter(keys);
+ String8 value;
+ String8 key = String8(AudioParameter::keyRouting);
+
+ if (param.get(key, value) == NO_ERROR) {
+ param.addInt(key, (int)mDevices);
+ }
+
+ LOGV("AudioStreamOutALSA::getParameters() %s", param.toString().string());
+ return param.toString();
+}
+
+status_t AudioHardware::AudioStreamOutALSA::getRenderPosition(uint32_t *dspFrames)
+{
+ //TODO
+ return INVALID_OPERATION;
+}
+
+int AudioHardware::AudioStreamOutALSA::prepareLock()
+{
+ // request sleep next time write() is called so that caller can acquire
+ // mLock
+ mSleepReq = true;
+ return mStandbyCnt;
+}
+
+void AudioHardware::AudioStreamOutALSA::lock()
+{
+ mLock.lock();
+ mSleepReq = false;
+}
+
+void AudioHardware::AudioStreamOutALSA::unlock() {
+ mLock.unlock();
+}
+
+//------------------------------------------------------------------------------
+// AudioStreamInALSA
+//------------------------------------------------------------------------------
+
+AudioHardware::AudioStreamInALSA::AudioStreamInALSA() :
+ mHardware(0), mPcm(0), mMixer(0), mRouteCtl(0),
+ mStandby(true), mDevices(0), mChannels(AUDIO_HW_IN_CHANNELS), mChannelCount(1),
+ mSampleRate(AUDIO_HW_IN_SAMPLERATE), mBufferSize(AUDIO_HW_IN_PERIOD_BYTES),
+ mDownSampler(NULL), mReadStatus(NO_ERROR), mDriverOp(DRV_NONE),
+ mStandbyCnt(0), mSleepReq(false)
+{
+}
+
+status_t AudioHardware::AudioStreamInALSA::set(
+ AudioHardware* hw, uint32_t devices, int *pFormat,
+ uint32_t *pChannels, uint32_t *pRate, AudioSystem::audio_in_acoustics acoustics)
+{
+ if (pFormat == 0 || *pFormat != AUDIO_HW_IN_FORMAT) {
+ *pFormat = AUDIO_HW_IN_FORMAT;
+ return BAD_VALUE;
+ }
+ if (pRate == 0) {
+ return BAD_VALUE;
+ }
+ uint32_t rate = AudioHardware::getInputSampleRate(*pRate);
+ if (rate != *pRate) {
+ *pRate = rate;
+ return BAD_VALUE;
+ }
+
+ if (pChannels == 0 || (*pChannels != AudioSystem::CHANNEL_IN_MONO &&
+ *pChannels != AudioSystem::CHANNEL_IN_STEREO)) {
+ *pChannels = AUDIO_HW_IN_CHANNELS;
+ return BAD_VALUE;
+ }
+
+ mHardware = hw;
+
+ LOGV("AudioStreamInALSA::set(%d, %d, %u)", *pFormat, *pChannels, *pRate);
+
+ mBufferSize = getBufferSize(*pRate, AudioSystem::popCount(*pChannels));
+ mDevices = devices;
+ mChannels = *pChannels;
+ mChannelCount = AudioSystem::popCount(mChannels);
+ mSampleRate = rate;
+ if (mSampleRate != AUDIO_HW_OUT_SAMPLERATE) {
+ mDownSampler = new AudioHardware::DownSampler(mSampleRate,
+ mChannelCount,
+ AUDIO_HW_IN_PERIOD_SZ,
+ this);
+ status_t status = mDownSampler->initCheck();
+ if (status != NO_ERROR) {
+ delete mDownSampler;
+ LOGW("AudioStreamInALSA::set() downsampler init failed: %d", status);
+ return status;
+ }
+
+ mPcmIn = new int16_t[AUDIO_HW_IN_PERIOD_SZ * mChannelCount];
+ }
+ return NO_ERROR;
+}
+
+AudioHardware::AudioStreamInALSA::~AudioStreamInALSA()
+{
+ standby();
+ if (mDownSampler != NULL) {
+ delete mDownSampler;
+ if (mPcmIn != NULL) {
+ delete[] mPcmIn;
+ }
+ }
+}
+
+ssize_t AudioHardware::AudioStreamInALSA::read(void* buffer, ssize_t bytes)
+{
+ // LOGV("AudioStreamInALSA::read(%p, %u)", buffer, bytes);
+ status_t status = NO_INIT;
+ int ret;
+
+ if (mHardware == NULL) return NO_INIT;
+
+ if (mSleepReq) {
+ // 10ms are always shorter than the time to reconfigure the audio path
+ // which is the only condition when mSleepReq would be true.
+ usleep(10000);
+ }
+
+ { // scope for the lock
+ AutoMutex lock(mLock);
+
+ if (mStandby) {
+ AutoMutex hwLock(mHardware->lock());
+
+ LOGD("AudioHardware pcm capture is exiting standby.");
+ acquire_wake_lock (PARTIAL_WAKE_LOCK, "AudioInLock");
+
+ sp<AudioStreamOutALSA> spOut = mHardware->output();
+ while (spOut != 0) {
+ if (!spOut->checkStandby()) {
+ int cnt = spOut->prepareLock();
+ mHardware->lock().unlock();
+ mLock.unlock();
+ // Mutex acquisition order is always out -> in -> hw
+ spOut->lock();
+ mLock.lock();
+ mHardware->lock().lock();
+ // make sure that another thread did not change output state
+ // while the mutex is released
+ if ((spOut == mHardware->output()) && (cnt == spOut->standbyCnt())) {
+ LOGV("AudioStreamInALSA::read() force output standby");
+ spOut->close_l();
+ break;
+ }
+ spOut->unlock();
+ spOut = mHardware->output();
+ } else {
+ spOut.clear();
+ }
+ }
+ // spOut is not 0 here only if the output was active and has been
+ // closed above
+
+ // open output before input
+ if (spOut != 0) {
+ if (spOut->open_l() != NO_ERROR) {
+ spOut->doStandby_l();
+ }
+ spOut->unlock();
+ }
+
+ open_l();
+
+ if (mPcm == NULL) {
+ release_wake_lock("AudioInLock");
+ goto Error;
+ }
+ mStandby = false;
+ }
+
+
+ if (mDownSampler != NULL) {
+ size_t frames = bytes / frameSize();
+ size_t framesIn = 0;
+ mReadStatus = 0;
+ do {
+ size_t outframes = frames - framesIn;
+ mDownSampler->resample(
+ (int16_t *)buffer + (framesIn * mChannelCount),
+ &outframes);
+ framesIn += outframes;
+ } while ((framesIn < frames) && mReadStatus == 0);
+ ret = mReadStatus;
+ bytes = framesIn * frameSize();
+ } else {
+ TRACE_DRIVER_IN(DRV_PCM_READ)
+ ret = pcm_read(mPcm, buffer, bytes);
+ TRACE_DRIVER_OUT
+ }
+
+ if (ret == 0) {
+ return bytes;
+ }
+
+ LOGW("read error: %d", ret);
+ status = ret;
+ }
+
+Error:
+
+ standby();
+
+ // Simulate audio output timing in case of error
+ usleep((((bytes * 1000) / frameSize()) * 1000) / sampleRate());
+
+ return status;
+}
+
+status_t AudioHardware::AudioStreamInALSA::standby()
+{
+ if (mHardware == NULL) return NO_INIT;
+
+ AutoMutex lock(mLock);
+
+ { // scope for AudioHardware lock
+ AutoMutex hwLock(mHardware->lock());
+
+ doStandby_l();
+ }
+ return NO_ERROR;
+}
+
+void AudioHardware::AudioStreamInALSA::doStandby_l()
+{
+ mStandbyCnt++;
+
+ if (!mStandby) {
+ LOGD("AudioHardware pcm capture is going to standby.");
+ release_wake_lock("AudioInLock");
+ mStandby = true;
+ }
+ close_l();
+}
+
+void AudioHardware::AudioStreamInALSA::close_l()
+{
+ if (mMixer) {
+ mHardware->closeMixer_l();
+ mMixer = NULL;
+ mRouteCtl = NULL;
+ }
+
+ if (mPcm) {
+ TRACE_DRIVER_IN(DRV_PCM_CLOSE)
+ pcm_close(mPcm);
+ TRACE_DRIVER_OUT
+ mPcm = NULL;
+ }
+}
+
+status_t AudioHardware::AudioStreamInALSA::open_l()
+{
+ unsigned flags = PCM_IN;
+ if (mChannels == AudioSystem::CHANNEL_IN_MONO) {
+ flags |= PCM_MONO;
+ }
+ flags |= (AUDIO_HW_IN_PERIOD_MULT - 1) << PCM_PERIOD_SZ_SHIFT;
+ flags |= (AUDIO_HW_IN_PERIOD_CNT - PCM_PERIOD_CNT_MIN)
+ << PCM_PERIOD_CNT_SHIFT;
+
+ LOGV("open pcm_in driver");
+ TRACE_DRIVER_IN(DRV_PCM_OPEN)
+ mPcm = pcm_open(flags);
+ TRACE_DRIVER_OUT
+ if (!pcm_ready(mPcm)) {
+ LOGE("cannot open pcm_in driver: %s\n", pcm_error(mPcm));
+ TRACE_DRIVER_IN(DRV_PCM_CLOSE)
+ pcm_close(mPcm);
+ TRACE_DRIVER_OUT
+ mPcm = NULL;
+ return NO_INIT;
+ }
+
+ if (mDownSampler != NULL) {
+ mInPcmInBuf = 0;
+ mDownSampler->reset();
+ }
+
+ mMixer = mHardware->openMixer_l();
+ if (mMixer) {
+ TRACE_DRIVER_IN(DRV_MIXER_GET)
+ mRouteCtl = mixer_get_control(mMixer, "Capture MIC Path", 0);
+ TRACE_DRIVER_OUT
+ }
+
+ if (mHardware->mode() != AudioSystem::MODE_IN_CALL) {
+ const char *route = mHardware->getInputRouteFromDevice(mDevices);
+ LOGV("read() wakeup setting route %s", route);
+ if (mRouteCtl) {
+ TRACE_DRIVER_IN(DRV_MIXER_SEL)
+ mixer_ctl_select(mRouteCtl, route);
+ TRACE_DRIVER_OUT
+ }
+ }
+
+ return NO_ERROR;
+}
+
+status_t AudioHardware::AudioStreamInALSA::dump(int fd, const Vector<String16>& args)
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+
+ bool locked = tryLock(mLock);
+ if (!locked) {
+ snprintf(buffer, SIZE, "\n\t\tAudioStreamInALSA maybe deadlocked\n");
+ } else {
+ mLock.unlock();
+ }
+
+ snprintf(buffer, SIZE, "\t\tmHardware: %p\n", mHardware);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "\t\tmPcm: %p\n", mPcm);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "\t\tmMixer: %p\n", mMixer);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "\t\tStandby %s\n", (mStandby) ? "ON" : "OFF");
+ result.append(buffer);
+ snprintf(buffer, SIZE, "\t\tmDevices: 0x%08x\n", mDevices);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "\t\tmChannels: 0x%08x\n", mChannels);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "\t\tmSampleRate: %d\n", mSampleRate);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "\t\tmBufferSize: %d\n", mBufferSize);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "\t\tmDriverOp: %d\n", mDriverOp);
+ result.append(buffer);
+ write(fd, result.string(), result.size());
+
+ return NO_ERROR;
+}
+
+bool AudioHardware::AudioStreamInALSA::checkStandby()
+{
+ return mStandby;
+}
+
+status_t AudioHardware::AudioStreamInALSA::setParameters(const String8& keyValuePairs)
+{
+ AudioParameter param = AudioParameter(keyValuePairs);
+ status_t status = NO_ERROR;
+ int value;
+
+ LOGD("AudioStreamInALSA::setParameters() %s", keyValuePairs.string());
+
+ if (mHardware == NULL) return NO_INIT;
+
+ {
+ AutoMutex lock(mLock);
+
+ if (param.getInt(String8(AudioParameter::keyInputSource), value) == NO_ERROR) {
+ AutoMutex hwLock(mHardware->lock());
+
+ mHardware->openMixer_l();
+ mHardware->setInputSource_l((audio_source)value);
+ mHardware->closeMixer_l();
+
+ param.remove(String8(AudioParameter::keyInputSource));
+ }
+
+ if (param.getInt(String8(AudioParameter::keyRouting), value) == NO_ERROR)
+ {
+ if (value != 0) {
+ AutoMutex hwLock(mHardware->lock());
+
+ if (mDevices != (uint32_t)value) {
+ mDevices = (uint32_t)value;
+ if (mHardware->mode() != AudioSystem::MODE_IN_CALL) {
+ doStandby_l();
+ }
+ }
+ }
+ param.remove(String8(AudioParameter::keyRouting));
+ }
+ }
+
+
+ if (param.size()) {
+ status = BAD_VALUE;
+ }
+
+ return status;
+
+}
+
+String8 AudioHardware::AudioStreamInALSA::getParameters(const String8& keys)
+{
+ AudioParameter param = AudioParameter(keys);
+ String8 value;
+ String8 key = String8(AudioParameter::keyRouting);
+
+ if (param.get(key, value) == NO_ERROR) {
+ param.addInt(key, (int)mDevices);
+ }
+
+ LOGV("AudioStreamInALSA::getParameters() %s", param.toString().string());
+ return param.toString();
+}
+
+status_t AudioHardware::AudioStreamInALSA::getNextBuffer(AudioHardware::BufferProvider::Buffer* buffer)
+{
+ if (mPcm == NULL) {
+ buffer->raw = NULL;
+ buffer->frameCount = 0;
+ mReadStatus = NO_INIT;
+ return NO_INIT;
+ }
+
+ if (mInPcmInBuf == 0) {
+ TRACE_DRIVER_IN(DRV_PCM_READ)
+ mReadStatus = pcm_read(mPcm,(void*) mPcmIn, AUDIO_HW_IN_PERIOD_SZ * frameSize());
+ TRACE_DRIVER_OUT
+ if (mReadStatus != 0) {
+ buffer->raw = NULL;
+ buffer->frameCount = 0;
+ return mReadStatus;
+ }
+ mInPcmInBuf = AUDIO_HW_IN_PERIOD_SZ;
+ }
+
+ buffer->frameCount = (buffer->frameCount > mInPcmInBuf) ? mInPcmInBuf : buffer->frameCount;
+ buffer->i16 = mPcmIn + (AUDIO_HW_IN_PERIOD_SZ - mInPcmInBuf) * mChannelCount;
+
+ return mReadStatus;
+}
+
+void AudioHardware::AudioStreamInALSA::releaseBuffer(Buffer* buffer)
+{
+ mInPcmInBuf -= buffer->frameCount;
+}
+
+size_t AudioHardware::AudioStreamInALSA::getBufferSize(uint32_t sampleRate, int channelCount)
+{
+ size_t ratio;
+
+ switch (sampleRate) {
+ case 8000:
+ case 11025:
+ ratio = 4;
+ break;
+ case 16000:
+ case 22050:
+ ratio = 2;
+ break;
+ case 44100:
+ default:
+ ratio = 1;
+ break;
+ }
+
+ return (AUDIO_HW_IN_PERIOD_SZ*channelCount*sizeof(int16_t)) / ratio ;
+}
+
+int AudioHardware::AudioStreamInALSA::prepareLock()
+{
+ // request sleep next time read() is called so that caller can acquire
+ // mLock
+ mSleepReq = true;
+ return mStandbyCnt;
+}
+
+void AudioHardware::AudioStreamInALSA::lock()
+{
+ mLock.lock();
+ mSleepReq = false;
+}
+
+void AudioHardware::AudioStreamInALSA::unlock() {
+ mLock.unlock();
+}
+
+//------------------------------------------------------------------------------
+// DownSampler
+//------------------------------------------------------------------------------
+
+/*
+ * 2.30 fixed point FIR filter coefficients for conversion 44100 -> 22050.
+ * (Works equivalently for 22010 -> 11025 or any other halving, of course.)
+ *
+ * Transition band from about 18 kHz, passband ripple < 0.1 dB,
+ * stopband ripple at about -55 dB, linear phase.
+ *
+ * Design and display in MATLAB or Octave using:
+ *
+ * filter = fir1(19, 0.5); filter = round(filter * 2**30); freqz(filter * 2**-30);
+ */
+static const int32_t filter_22khz_coeff[] = {
+ 2089257, 2898328, -5820678, -10484531,
+ 19038724, 30542725, -50469415, -81505260,
+ 152544464, 478517512, 478517512, 152544464,
+ -81505260, -50469415, 30542725, 19038724,
+ -10484531, -5820678, 2898328, 2089257,
+};
+#define NUM_COEFF_22KHZ (sizeof(filter_22khz_coeff) / sizeof(filter_22khz_coeff[0]))
+#define OVERLAP_22KHZ (NUM_COEFF_22KHZ - 2)
+
+/*
+ * Convolution of signals A and reverse(B). (In our case, the filter response
+ * is symmetric, so the reversing doesn't matter.)
+ * A is taken to be in 0.16 fixed-point, and B is taken to be in 2.30 fixed-point.
+ * The answer will be in 16.16 fixed-point, unclipped.
+ *
+ * This function would probably be the prime candidate for SIMD conversion if
+ * you want more speed.
+ */
+int32_t fir_convolve(const int16_t* a, const int32_t* b, int num_samples)
+{
+ int32_t sum = 1 << 13;
+ for (int i = 0; i < num_samples; ++i) {
+ sum += a[i] * (b[i] >> 16);
+ }
+ return sum >> 14;
+}
+
+/* Clip from 16.16 fixed-point to 0.16 fixed-point. */
+int16_t clip(int32_t x)
+{
+ if (x < -32768) {
+ return -32768;
+ } else if (x > 32767) {
+ return 32767;
+ } else {
+ return x;
+ }
+}
+
+/*
+ * Convert a chunk from 44 kHz to 22 kHz. Will update num_samples_in and num_samples_out
+ * accordingly, since it may leave input samples in the buffer due to overlap.
+ *
+ * Input and output are taken to be in 0.16 fixed-point.
+ */
+void resample_2_1(int16_t* input, int16_t* output, int* num_samples_in, int* num_samples_out)
+{
+ if (*num_samples_in < (int)NUM_COEFF_22KHZ) {
+ *num_samples_out = 0;
+ return;
+ }
+
+ int odd_smp = *num_samples_in & 0x1;
+ int num_samples = *num_samples_in - odd_smp - OVERLAP_22KHZ;
+
+ for (int i = 0; i < num_samples; i += 2) {
+ output[i / 2] = clip(fir_convolve(input + i, filter_22khz_coeff, NUM_COEFF_22KHZ));
+ }
+
+ memmove(input, input + num_samples, (OVERLAP_22KHZ + odd_smp) * sizeof(*input));
+ *num_samples_out = num_samples / 2;
+ *num_samples_in = OVERLAP_22KHZ + odd_smp;
+}
+
+/*
+ * 2.30 fixed point FIR filter coefficients for conversion 22050 -> 16000,
+ * or 11025 -> 8000.
+ *
+ * Transition band from about 14 kHz, passband ripple < 0.1 dB,
+ * stopband ripple at about -50 dB, linear phase.
+ *
+ * Design and display in MATLAB or Octave using:
+ *
+ * filter = fir1(23, 16000 / 22050); filter = round(filter * 2**30); freqz(filter * 2**-30);
+ */
+static const int32_t filter_16khz_coeff[] = {
+ 2057290, -2973608, 1880478, 4362037,
+ -14639744, 18523609, -1609189, -38502470,
+ 78073125, -68353935, -59103896, 617555440,
+ 617555440, -59103896, -68353935, 78073125,
+ -38502470, -1609189, 18523609, -14639744,
+ 4362037, 1880478, -2973608, 2057290,
+};
+#define NUM_COEFF_16KHZ (sizeof(filter_16khz_coeff) / sizeof(filter_16khz_coeff[0]))
+#define OVERLAP_16KHZ (NUM_COEFF_16KHZ - 1)
+
+/*
+ * Convert a chunk from 22 kHz to 16 kHz. Will update num_samples_in and
+ * num_samples_out accordingly, since it may leave input samples in the buffer
+ * due to overlap.
+ *
+ * This implementation is rather ad-hoc; it first low-pass filters the data
+ * into a temporary buffer, and then converts chunks of 441 input samples at a
+ * time into 320 output samples by simple linear interpolation. A better
+ * implementation would use a polyphase filter bank to do these two operations
+ * in one step.
+ *
+ * Input and output are taken to be in 0.16 fixed-point.
+ */
+
+#define RESAMPLE_16KHZ_SAMPLES_IN 441
+#define RESAMPLE_16KHZ_SAMPLES_OUT 320
+
+void resample_441_320(int16_t* input, int16_t* output, int* num_samples_in, int* num_samples_out)
+{
+ const int num_blocks = (*num_samples_in - OVERLAP_16KHZ) / RESAMPLE_16KHZ_SAMPLES_IN;
+ if (num_blocks < 1) {
+ *num_samples_out = 0;
+ return;
+ }
+
+ for (int i = 0; i < num_blocks; ++i) {
+ uint32_t tmp[RESAMPLE_16KHZ_SAMPLES_IN];
+ for (int j = 0; j < RESAMPLE_16KHZ_SAMPLES_IN; ++j) {
+ tmp[j] = fir_convolve(input + i * RESAMPLE_16KHZ_SAMPLES_IN + j,
+ filter_16khz_coeff,
+ NUM_COEFF_16KHZ);
+ }
+
+ const float step_float = (float)RESAMPLE_16KHZ_SAMPLES_IN / (float)RESAMPLE_16KHZ_SAMPLES_OUT;
+ const uint32_t step = (uint32_t)(step_float * 32768.0f + 0.5f); // 17.15 fixed point
+
+ uint32_t in_sample_num = 0; // 17.15 fixed point
+ for (int j = 0; j < RESAMPLE_16KHZ_SAMPLES_OUT; ++j, in_sample_num += step) {
+ const uint32_t whole = in_sample_num >> 15;
+ const uint32_t frac = (in_sample_num & 0x7fff); // 0.15 fixed point
+ const int32_t s1 = tmp[whole];
+ const int32_t s2 = tmp[whole + 1];
+ *output++ = clip(s1 + (((s2 - s1) * (int32_t)frac) >> 15));
+ }
+
+ }
+
+ const int samples_consumed = num_blocks * RESAMPLE_16KHZ_SAMPLES_IN;
+ memmove(input, input + samples_consumed, (*num_samples_in - samples_consumed) * sizeof(*input));
+ *num_samples_in -= samples_consumed;
+ *num_samples_out = RESAMPLE_16KHZ_SAMPLES_OUT * num_blocks;
+}
+
+
+AudioHardware::DownSampler::DownSampler(uint32_t outSampleRate,
+ uint32_t channelCount,
+ uint32_t frameCount,
+ AudioHardware::BufferProvider* provider)
+ : mStatus(NO_INIT), mProvider(provider), mSampleRate(outSampleRate),
+ mChannelCount(channelCount), mFrameCount(frameCount),
+ mInLeft(NULL), mInRight(NULL), mTmpLeft(NULL), mTmpRight(NULL),
+ mTmp2Left(NULL), mTmp2Right(NULL), mOutLeft(NULL), mOutRight(NULL)
+
+{
+ LOGV("AudioHardware::DownSampler() cstor %p SR %d channels %d frames %d",
+ this, mSampleRate, mChannelCount, mFrameCount);
+
+ if (mSampleRate != 8000 && mSampleRate != 11025 && mSampleRate != 16000 &&
+ mSampleRate != 22050) {
+ LOGW("AudioHardware::DownSampler cstor: bad sampling rate: %d", mSampleRate);
+ return;
+ }
+
+ mInLeft = new int16_t[mFrameCount];
+ mInRight = new int16_t[mFrameCount];
+ mTmpLeft = new int16_t[mFrameCount];
+ mTmpRight = new int16_t[mFrameCount];
+ mTmp2Left = new int16_t[mFrameCount];
+ mTmp2Right = new int16_t[mFrameCount];
+ mOutLeft = new int16_t[mFrameCount];
+ mOutRight = new int16_t[mFrameCount];
+
+ mStatus = NO_ERROR;
+}
+
+AudioHardware::DownSampler::~DownSampler()
+{
+ if (mInLeft) delete[] mInLeft;
+ if (mInRight) delete[] mInRight;
+ if (mTmpLeft) delete[] mTmpLeft;
+ if (mTmpRight) delete[] mTmpRight;
+ if (mTmp2Left) delete[] mTmp2Left;
+ if (mTmp2Right) delete[] mTmp2Right;
+ if (mOutLeft) delete[] mOutLeft;
+ if (mOutRight) delete[] mOutRight;
+}
+
+void AudioHardware::DownSampler::reset()
+{
+ mInInBuf = 0;
+ mInTmpBuf = 0;
+ mInTmp2Buf = 0;
+ mOutBufPos = 0;
+ mInOutBuf = 0;
+}
+
+
+int AudioHardware::DownSampler::resample(int16_t* out, size_t *outFrameCount)
+{
+ if (mStatus != NO_ERROR) {
+ return mStatus;
+ }
+
+ if (out == NULL || outFrameCount == NULL) {
+ return BAD_VALUE;
+ }
+
+ int16_t *outLeft = mTmp2Left;
+ int16_t *outRight = mTmp2Left;
+ if (mSampleRate == 22050) {
+ outLeft = mTmpLeft;
+ outRight = mTmpRight;
+ } else if (mSampleRate == 8000){
+ outLeft = mOutLeft;
+ outRight = mOutRight;
+ }
+
+ int outFrames = 0;
+ int remaingFrames = *outFrameCount;
+
+ if (mInOutBuf) {
+ int frames = (remaingFrames > mInOutBuf) ? mInOutBuf : remaingFrames;
+
+ for (int i = 0; i < frames; ++i) {
+ out[i] = outLeft[mOutBufPos + i];
+ }
+ if (mChannelCount == 2) {
+ for (int i = 0; i < frames; ++i) {
+ out[i * 2] = outLeft[mOutBufPos + i];
+ out[i * 2 + 1] = outRight[mOutBufPos + i];
+ }
+ }
+ remaingFrames -= frames;
+ mInOutBuf -= frames;
+ mOutBufPos += frames;
+ outFrames += frames;
+ }
+
+ while (remaingFrames) {
+ LOGW_IF((mInOutBuf != 0), "mInOutBuf should be 0 here");
+
+ AudioHardware::BufferProvider::Buffer buf;
+ buf.frameCount = mFrameCount - mInInBuf;
+ int ret = mProvider->getNextBuffer(&buf);
+ if (buf.raw == NULL) {
+ *outFrameCount = outFrames;
+ return ret;
+ }
+
+ for (size_t i = 0; i < buf.frameCount; ++i) {
+ mInLeft[i + mInInBuf] = buf.i16[i];
+ }
+ if (mChannelCount == 2) {
+ for (size_t i = 0; i < buf.frameCount; ++i) {
+ mInLeft[i + mInInBuf] = buf.i16[i * 2];
+ mInRight[i + mInInBuf] = buf.i16[i * 2 + 1];
+ }
+ }
+ mInInBuf += buf.frameCount;
+ mProvider->releaseBuffer(&buf);
+
+ /* 44010 -> 22050 */
+ {
+ int samples_in_left = mInInBuf;
+ int samples_out_left;
+ resample_2_1(mInLeft, mTmpLeft + mInTmpBuf, &samples_in_left, &samples_out_left);
+
+ if (mChannelCount == 2) {
+ int samples_in_right = mInInBuf;
+ int samples_out_right;
+ resample_2_1(mInRight, mTmpRight + mInTmpBuf, &samples_in_right, &samples_out_right);
+ }
+
+ mInInBuf = samples_in_left;
+ mInTmpBuf += samples_out_left;
+ mInOutBuf = samples_out_left;
+ }
+
+ if (mSampleRate == 11025 || mSampleRate == 8000) {
+ /* 22050 - > 11025 */
+ int samples_in_left = mInTmpBuf;
+ int samples_out_left;
+ resample_2_1(mTmpLeft, mTmp2Left + mInTmp2Buf, &samples_in_left, &samples_out_left);
+
+ if (mChannelCount == 2) {
+ int samples_in_right = mInTmpBuf;
+ int samples_out_right;
+ resample_2_1(mTmpRight, mTmp2Right + mInTmp2Buf, &samples_in_right, &samples_out_right);
+ }
+
+
+ mInTmpBuf = samples_in_left;
+ mInTmp2Buf += samples_out_left;
+ mInOutBuf = samples_out_left;
+
+ if (mSampleRate == 8000) {
+ /* 11025 -> 8000*/
+ int samples_in_left = mInTmp2Buf;
+ int samples_out_left;
+ resample_441_320(mTmp2Left, mOutLeft, &samples_in_left, &samples_out_left);
+
+ if (mChannelCount == 2) {
+ int samples_in_right = mInTmp2Buf;
+ int samples_out_right;
+ resample_441_320(mTmp2Right, mOutRight, &samples_in_right, &samples_out_right);
+ }
+
+ mInTmp2Buf = samples_in_left;
+ mInOutBuf = samples_out_left;
+ } else {
+ mInTmp2Buf = 0;
+ }
+
+ } else if (mSampleRate == 16000) {
+ /* 22050 -> 16000*/
+ int samples_in_left = mInTmpBuf;
+ int samples_out_left;
+ resample_441_320(mTmpLeft, mTmp2Left, &samples_in_left, &samples_out_left);
+
+ if (mChannelCount == 2) {
+ int samples_in_right = mInTmpBuf;
+ int samples_out_right;
+ resample_441_320(mTmpRight, mTmp2Right, &samples_in_right, &samples_out_right);
+ }
+
+ mInTmpBuf = samples_in_left;
+ mInOutBuf = samples_out_left;
+ } else {
+ mInTmpBuf = 0;
+ }
+
+ int frames = (remaingFrames > mInOutBuf) ? mInOutBuf : remaingFrames;
+
+ for (int i = 0; i < frames; ++i) {
+ out[outFrames + i] = outLeft[i];
+ }
+ if (mChannelCount == 2) {
+ for (int i = 0; i < frames; ++i) {
+ out[(outFrames + i) * 2] = outLeft[i];
+ out[(outFrames + i) * 2 + 1] = outRight[i];
+ }
+ }
+ remaingFrames -= frames;
+ outFrames += frames;
+ mOutBufPos = frames;
+ mInOutBuf -= frames;
+ }
+
+ return 0;
+}
+
+
+
+
+
+
+
+//------------------------------------------------------------------------------
+// Factory
+//------------------------------------------------------------------------------
+
+extern "C" AudioHardwareInterface* createAudioHardware(void) {
+ return new AudioHardware();
+}
+
+}; // namespace android
351 libaudio/AudioHardware.h
View
@@ -0,0 +1,351 @@
+/*
+** Copyright 2008, 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>
+
+extern "C" {
+ struct pcm;
+ struct mixer;
+ struct mixer_ctl;
+};
+
+namespace android {
+
+// TODO: determine actual audio DSP and hardware latency
+// Additionnal latency introduced by audio DSP and hardware in ms
+#define AUDIO_HW_OUT_LATENCY_MS 0
+// Default audio output sample rate
+#define AUDIO_HW_OUT_SAMPLERATE 44100
+// Default audio output channel mask
+#define AUDIO_HW_OUT_CHANNELS (AudioSystem::CHANNEL_OUT_STEREO)
+// Default audio output sample format
+#define AUDIO_HW_OUT_FORMAT (AudioSystem::PCM_16_BIT)
+// Kernel pcm out buffer size in frames at 44.1kHz
+#define AUDIO_HW_OUT_PERIOD_MULT 8 // (8 * 128 = 1024 frames)
+#define AUDIO_HW_OUT_PERIOD_SZ (PCM_PERIOD_SZ_MIN * AUDIO_HW_OUT_PERIOD_MULT)
+#define AUDIO_HW_OUT_PERIOD_CNT 4
+// Default audio output buffer size in bytes
+#define AUDIO_HW_OUT_PERIOD_BYTES (AUDIO_HW_OUT_PERIOD_SZ * 2 * sizeof(int16_t))
+
+// Default audio input sample rate
+#define AUDIO_HW_IN_SAMPLERATE 8000
+// Default audio input channel mask
+#define AUDIO_HW_IN_CHANNELS (AudioSystem::CHANNEL_IN_MONO)
+// Default audio input sample format
+#define AUDIO_HW_IN_FORMAT (AudioSystem::PCM_16_BIT)
+// Number of buffers in audio driver for input
+#define AUDIO_HW_NUM_IN_BUF 2
+// Kernel pcm in buffer size in frames at 44.1kHz (before resampling)
+#define AUDIO_HW_IN_PERIOD_MULT 16 // (16 * 128 = 2048 frames)
+#define AUDIO_HW_IN_PERIOD_SZ (PCM_PERIOD_SZ_MIN * AUDIO_HW_IN_PERIOD_MULT)
+#define AUDIO_HW_IN_PERIOD_CNT 2
+// Default audio input buffer size in bytes (8kHz mono)
+#define AUDIO_HW_IN_PERIOD_BYTES ((AUDIO_HW_IN_PERIOD_SZ*sizeof(int16_t))/8)
+
+
+class AudioHardware : public AudioHardwareBase
+{
+ class AudioStreamOutALSA;
+ class AudioStreamInALSA;
+public:
+
+ // input path names used to translate from input sources to driver paths
+ static const char *inputPathNameDefault;
+ static const char *inputPathNameCamcorder;
+ static const char *inputPathNameVoiceRecognition;
+ static const char *inputPathNameVoiceCommunication;
+
+ 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);
+
+ 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);
+
+ 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);
+
+ int mode() { return mMode; }
+ const char *getOutputRouteFromDevice(uint32_t device);
+ const char *getInputRouteFromDevice(uint32_t device);
+ const char *getVoiceRouteFromDevice(uint32_t device);
+
+ status_t setIncallPath_l(uint32_t device);
+
+ status_t setInputSource_l(audio_source source);
+
+ static uint32_t getInputSampleRate(uint32_t sampleRate);
+ sp <AudioStreamInALSA> getActiveInput_l();
+
+ Mutex& lock() { return mLock; }
+
+ struct pcm *openPcmOut_l();
+ void closePcmOut_l();
+
+ struct mixer *openMixer_l();
+ void closeMixer_l();
+
+ sp <AudioStreamOutALSA> output() { return mOutput; }
+
+protected:
+ virtual status_t dump(int fd, const Vector<String16>& args);
+
+private:
+
+ enum tty_modes {
+ TTY_MODE_OFF,
+ TTY_MODE_VCO,
+ TTY_MODE_HCO,
+ TTY_MODE_FULL
+ };
+
+ bool mInit;
+ bool mMicMute;
+ sp <AudioStreamOutALSA> mOutput;
+ SortedVector < sp<AudioStreamInALSA> > mInputs;
+ Mutex mLock;
+ struct pcm* mPcm;
+ struct mixer* mMixer;
+ uint32_t mPcmOpenCnt;
+ uint32_t mMixerOpenCnt;
+ bool mInCallAudioMode;
+
+ audio_source mInputSource;
+ bool mBluetoothNrec;
+ int mTTYMode;
+
+ // trace driver operations for dump
+ int mDriverOp;
+
+ static uint32_t checkInputSampleRate(uint32_t sampleRate);
+ static const uint32_t inputSamplingRates[];
+
+ class AudioStreamOutALSA : public AudioStreamOut, public RefBase
+ {
+ public:
+ AudioStreamOutALSA();
+ virtual ~AudioStreamOutALSA();
+ status_t set(AudioHardware* mHardware,
+ uint32_t devices,
+ int *pFormat,
+ uint32_t *pChannels,
+ uint32_t *pRate);
+ virtual uint32_t sampleRate()
+ const { return mSampleRate; }
+ virtual size_t bufferSize()
+ const { return mBufferSize; }
+ virtual uint32_t channels()
+ const { return mChannels; }
+ virtual int format()
+ const { return AUDIO_HW_OUT_FORMAT; }
+ virtual uint32_t latency()
+ const { return (1000 * AUDIO_HW_OUT_PERIOD_CNT *
+ (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);
+ virtual status_t standby();
+ bool checkStandby();
+
+ virtual status_t dump(int fd, const Vector<String16>& args);
+ virtual status_t setParameters(const String8& keyValuePairs);
+ virtual String8 getParameters(const String8& keys);
+ uint32_t device() { return mDevices; }
+ virtual status_t getRenderPosition(uint32_t *dspFrames);
+
+ void doStandby_l();
+ void close_l();
+ status_t open_l();
+ int standbyCnt() { return mStandbyCnt; }
+
+ int prepareLock();
+ void lock();
+ void unlock();
+
+ private:
+
+ Mutex mLock;
+ AudioHardware* mHardware;
+ struct pcm *mPcm;
+ struct mixer *mMixer;
+ struct mixer_ctl *mRouteCtl;
+ const char *next_route;
+ bool mStandby;
+ uint32_t mDevices;
+ uint32_t mChannels;
+ uint32_t mSampleRate;
+ size_t mBufferSize;
+ // trace driver operations for dump
+ int mDriverOp;
+ int mStandbyCnt;
+ bool mSleepReq;
+ };
+
+ class DownSampler;
+
+ class BufferProvider
+ {
+ public:
+
+ struct Buffer {
+ union {
+ void* raw;
+ short* i16;
+ int8_t* i8;
+ };
+ size_t frameCount;
+ };
+
+ virtual ~BufferProvider() {}
+
+ virtual status_t getNextBuffer(Buffer* buffer) = 0;
+ virtual void releaseBuffer(Buffer* buffer) = 0;
+ };
+
+ class DownSampler {
+ public:
+ DownSampler(uint32_t outSampleRate,
+ uint32_t channelCount,
+ uint32_t frameCount,
+ BufferProvider* provider);
+
+ virtual ~DownSampler();
+
+ void reset();
+ status_t initCheck() { return mStatus; }
+ int resample(int16_t* out, size_t *outFrameCount);
+
+ private:
+ status_t mStatus;
+ BufferProvider* mProvider;
+ uint32_t mSampleRate;
+ uint32_t mChannelCount;
+ uint32_t mFrameCount;
+ int16_t *mInLeft;
+ int16_t *mInRight;
+ int16_t *mTmpLeft;
+ int16_t *mTmpRight;
+ int16_t *mTmp2Left;
+ int16_t *mTmp2Right;
+ int16_t *mOutLeft;
+ int16_t *mOutRight;
+ int mInInBuf;
+ int mInTmpBuf;
+ int mInTmp2Buf;
+ int mOutBufPos;
+ int mInOutBuf;
+ };
+
+
+ class AudioStreamInALSA : public AudioStreamIn, public BufferProvider, public RefBase
+ {
+
+ public:
+ AudioStreamInALSA();
+ virtual ~AudioStreamInALSA();
+ status_t set(AudioHardware* hw,
+ 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 AUDIO_HW_IN_FORMAT; }
+ 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();
+ bool checkStandby();
+ virtual status_t setParameters(const String8& keyValuePairs);
+ virtual String8 getParameters(const String8& keys);
+ virtual unsigned int getInputFramesLost() const { return 0; }
+ uint32_t device() { return mDevices; }
+ void doStandby_l();
+ void close_l();
+ status_t open_l();
+ int standbyCnt() { return mStandbyCnt; }
+
+ static size_t getBufferSize(uint32_t sampleRate, int channelCount);
+
+ // BufferProvider
+ virtual status_t getNextBuffer(BufferProvider::Buffer* buffer);
+ virtual void releaseBuffer(BufferProvider::Buffer* buffer);
+
+ int prepareLock();
+ void lock();
+ void unlock();
+
+ private:
+ Mutex mLock;
+ AudioHardware* mHardware;
+ struct pcm *mPcm;
+ struct mixer *mMixer;
+ struct mixer_ctl *mRouteCtl;
+ const char *next_route;
+ bool mStandby;
+ uint32_t mDevices;
+ uint32_t mChannels;
+ uint32_t mChannelCount;
+ uint32_t mSampleRate;
+ size_t mBufferSize;
+ DownSampler *mDownSampler;
+ status_t mReadStatus;
+ size_t mInPcmInBuf;
+ int16_t *mPcmIn;
+ // trace driver operations for dump
+ int mDriverOp;
+ int mStandbyCnt;
+ bool mSleepReq;
+ };
+
+};
+
+}; // namespace android
+
+#endif
46 libaudio/AudioPolicyManager.cpp
View
@@ -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.
+ */
+
+#define LOG_TAG "AudioPolicyManager"
+#define LOG_NDEBUG 0
+#include <utils/Log.h>
+#include "AudioPolicyManager.h"
+#include <media/mediarecorder.h>
+
+namespace android {
+
+
+
+// ----------------------------------------------------------------------------
+// AudioPolicyManager for crespo platform
+// 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
46 libaudio/AudioPolicyManager.h
View
@@ -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 duplication of notifications and ringtones over A2DP output
+ virtual bool a2dpUsedForSonification() const { return true; }
+#endif
+
+};
+};
77 libaudio/alsa_audio.h
View
@@ -0,0 +1,77 @@
+/*
+** 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 _AUDIO_H_
+#define _AUDIO_H_
+
+struct pcm;
+
+#define PCM_OUT 0x00000000
+#define PCM_IN 0x10000000
+
+#define PCM_STEREO 0x00000000
+#define PCM_MONO 0x01000000
+
+#define PCM_44100HZ 0x00000000
+#define PCM_48000HZ 0x00100000
+#define PCM_8000HZ 0x00200000
+#define PCM_RATE_MASK 0x00F00000
+
+#define PCM_PERIOD_CNT_MIN 2
+#define PCM_PERIOD_CNT_SHIFT 16
+#define PCM_PERIOD_CNT_MASK (0xF << PCM_PERIOD_CNT_SHIFT)
+#define PCM_PERIOD_SZ_MIN 128
+#define PCM_PERIOD_SZ_SHIFT 12
+#define PCM_PERIOD_SZ_MASK (0xF << PCM_PERIOD_SZ_SHIFT)
+
+/* Acquire/release a pcm channel.
+ * Returns non-zero on error
+ */
+struct pcm *pcm_open(unsigned flags);
+int pcm_close(struct pcm *pcm);
+int pcm_ready(struct pcm *pcm);
+
+/* Returns a human readable reason for the last error. */
+const char *pcm_error(struct pcm *pcm);
+
+/* Returns the buffer size (int bytes) that should be used for pcm_write.
+ * This will be 1/2 of the actual fifo size.
+ */
+unsigned pcm_buffer_size(struct pcm *pcm);
+
+/* Write data to the fifo.
+ * Will start playback on the first write or on a write that
+ * occurs after a fifo underrun.
+ */
+int pcm_write(struct pcm *pcm, void *data, unsigned count);
+int pcm_read(struct pcm *pcm, void *data, unsigned count);
+
+struct mixer;
+struct mixer_ctl;
+
+struct mixer *mixer_open(void);
+void mixer_close(struct mixer *mixer);
+void mixer_dump(struct mixer *mixer);
+
+struct mixer_ctl *mixer_get_control(struct mixer *mixer,
+ const char *name, unsigned index);
+struct mixer_ctl *mixer_get_nth_control(struct mixer *mixer, unsigned n);
+
+int mixer_ctl_set(struct mixer_ctl *ctl, unsigned percent);
+int mixer_ctl_select(struct mixer_ctl *ctl, const char *value);
+void mixer_ctl_print(struct mixer_ctl *ctl);
+
+#endif
371 libaudio/alsa_mixer.c
View
@@ -0,0 +1,371 @@
+/*
+** 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.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include <linux/ioctl.h>
+#define __force
+#define __bitwise
+#define __user
+#include "asound.h"
+
+#include "alsa_audio.h"
+
+static const char *elem_iface_name(snd_ctl_elem_iface_t n)
+{
+ switch (n) {
+ case SNDRV_CTL_ELEM_IFACE_CARD: return "CARD";
+ case SNDRV_CTL_ELEM_IFACE_HWDEP: return "HWDEP";
+ case SNDRV_CTL_ELEM_IFACE_MIXER: return "MIXER";
+ case SNDRV_CTL_ELEM_IFACE_PCM: return "PCM";
+ case SNDRV_CTL_ELEM_IFACE_RAWMIDI: return "MIDI";
+ case SNDRV_CTL_ELEM_IFACE_TIMER: return "TIMER";
+ case SNDRV_CTL_ELEM_IFACE_SEQUENCER: return "SEQ";
+ default: return "???";
+ }
+}
+
+static const char *elem_type_name(snd_ctl_elem_type_t n)
+{
+ switch (n) {
+ case SNDRV_CTL_ELEM_TYPE_NONE: return "NONE";
+ case SNDRV_CTL_ELEM_TYPE_BOOLEAN: return "BOOL";
+ case SNDRV_CTL_ELEM_TYPE_INTEGER: return "INT32";
+ case SNDRV_CTL_ELEM_TYPE_ENUMERATED: return "ENUM";
+ case SNDRV_CTL_ELEM_TYPE_BYTES: return "BYTES";
+ case SNDRV_CTL_ELEM_TYPE_IEC958: return "IEC958";
+ case SNDRV_CTL_ELEM_TYPE_INTEGER64: return "INT64";
+ default: return "???";
+ }
+}
+
+
+struct mixer_ctl {
+ struct mixer *mixer;
+ struct snd_ctl_elem_info *info;
+ char **ename;
+};
+
+struct mixer {
+ int fd;
+ struct snd_ctl_elem_info *info;
+ struct mixer_ctl *ctl;
+ unsigned count;
+};
+
+void mixer_close(struct mixer *mixer)
+{
+ unsigned n,m;
+
+ if (mixer->fd >= 0)
+ close(mixer->fd);
+
+ if (mixer->ctl) {
+ for (n = 0; n < mixer->count; n++) {
+ if (mixer->ctl[n].ename) {
+ unsigned max = mixer->ctl[n].info->value.enumerated.items;
+ for (m = 0; m < max; m++)
+ free(mixer->ctl[n].ename[m]);
+ free(mixer->ctl[n].ename);
+ }
+ }
+ free(mixer->ctl);
+ }
+
+ if (mixer->info)
+ free(mixer->info);
+
+ free(mixer);
+}
+
+struct mixer *mixer_open(void)
+{
+ struct snd_ctl_elem_list elist;
+ struct snd_ctl_elem_info tmp;
+ struct snd_ctl_elem_id *eid = NULL;
+ struct mixer *mixer = NULL;
+ unsigned n, m;
+ int fd;
+
+ fd = open("/dev/snd/controlC0", O_RDWR);
+ if (fd < 0)
+ return 0;
+
+ memset(&elist, 0, sizeof(elist));
+ if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)
+ goto fail;
+
+ mixer = calloc(1, sizeof(*mixer));
+ if (!mixer)
+ goto fail;
+
+ mixer->ctl = calloc(elist.count, sizeof(struct mixer_ctl));
+ mixer->info = calloc(elist.count, sizeof(struct snd_ctl_elem_info));
+ if (!mixer->ctl || !mixer->info)
+ goto fail;
+
+ eid = calloc(elist.count, sizeof(struct snd_ctl_elem_id));
+ if (!eid)
+ goto fail;
+
+ mixer->count = elist.count;
+ mixer->fd = fd;
+ elist.space = mixer->count;
+ elist.pids = eid;
+ if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)
+ goto fail;
+
+ for (n = 0; n < mixer->count; n++) {
+ struct snd_ctl_elem_info *ei = mixer->info + n;
+ ei->id.numid = eid[n].numid;
+ if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, ei) < 0)
+ goto fail;
+ mixer->ctl[n].info = ei;
+ mixer->ctl[n].mixer = mixer;
+ if (ei->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) {
+ char **enames = calloc(ei->value.enumerated.items, sizeof(char*));
+ if (!enames)
+ goto fail;
+ mixer->ctl[n].ename = enames;
+ for (m = 0; m < ei->value.enumerated.items; m++) {
+ memset(&tmp, 0, sizeof(tmp));
+ tmp.id.numid = ei->id.numid;
+ tmp.value.enumerated.item = m;
+ if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, &tmp) < 0)
+ goto fail;
+ enames[m] = strdup(tmp.value.enumerated.name);
+ if (!enames[m])
+ goto fail;
+ }
+ }
+ }
+
+ free(eid);
+ return mixer;
+
+fail:
+ if (eid)
+ free(eid);
+ if (mixer)
+ mixer_close(mixer);
+ else if (fd >= 0)