diff options
author | Eric Laurent <elaurent@google.com> | 2010-10-11 09:02:58 -0700 |
---|---|---|
committer | Eric Laurent <elaurent@google.com> | 2010-10-21 19:30:39 -0700 |
commit | 84f3736063154f07de2602ad22992f7f4e24dd8c (patch) | |
tree | d039551d0e603e113232b594aa64aa405a317d02 /libaudio2 | |
parent | e46ae2fa1195d5de9bb5c6789501d1e09173c2da (diff) | |
download | device_samsung_crespo-84f3736063154f07de2602ad22992f7f4e24dd8c.zip device_samsung_crespo-84f3736063154f07de2602ad22992f7f4e24dd8c.tar.gz device_samsung_crespo-84f3736063154f07de2602ad22992f7f4e24dd8c.tar.bz2 |
Fix issue 3122180.
Disable use of user space ALSA library.
This change contains updates to the new audio HAL that does not rely
on ALSA user space library. It also activates this new version
of libaudio for the build by default.
Change-Id: Id68c05714a19820e12a48cbe07a161e4e9a189d8
Diffstat (limited to 'libaudio2')
-rw-r--r-- | libaudio2/Android.mk | 15 | ||||
-rw-r--r-- | libaudio2/AudioHardware.cpp | 1398 | ||||
-rw-r--r-- | libaudio2/AudioHardware.h | 214 | ||||
-rw-r--r-- | libaudio2/alsa_audio.h | 7 | ||||
-rw-r--r-- | libaudio2/alsa_pcm.c | 38 |
5 files changed, 1581 insertions, 91 deletions
diff --git a/libaudio2/Android.mk b/libaudio2/Android.mk index 53979f3..cdabff3 100644 --- a/libaudio2/Android.mk +++ b/libaudio2/Android.mk @@ -5,21 +5,21 @@ ifeq ($(TARGET_DEVICE),crespo) include $(CLEAR_VARS) LOCAL_SRC_FILES:= aplay.c alsa_pcm.c alsa_mixer.c LOCAL_MODULE:= aplay -LOCAL_SHARED_LIBRARIES:= libc +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 +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 +LOCAL_SHARED_LIBRARIES := libc libcutils LOCAL_MODULE_TAGS:= debug include $(BUILD_EXECUTABLE) @@ -29,10 +29,17 @@ 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 +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) diff --git a/libaudio2/AudioHardware.cpp b/libaudio2/AudioHardware.cpp index c0d20da..1a8975f 100644 --- a/libaudio2/AudioHardware.cpp +++ b/libaudio2/AudioHardware.cpp @@ -16,6 +16,8 @@ #include <math.h> +//#define LOG_NDEBUG 0 + #define LOG_TAG "AudioHardware" #include <utils/Log.h> @@ -31,6 +33,7 @@ #include "AudioHardware.h" #include <media/AudioRecord.h> +#include <hardware_legacy/power.h> extern "C" { #include "alsa_audio.h" @@ -38,17 +41,59 @@ extern "C" { namespace android { + +const uint32_t AudioHardware::inputSamplingRates[] = { + 8000, 11025, 16000, 22050, 44100 +}; + // ---------------------------------------------------------------------------- AudioHardware::AudioHardware() : - mInit(false), mMicMute(true), mOutput(0) + mInit(false), + mMicMute(false), + mOutput(NULL), + mPcm(NULL), + mMixer(NULL), + mPcmOpenCnt(0), + mMixerOpenCnt(0), + mVrModeEnabled(false), + mBluetoothNrec(true), + mSecRilLibHandle(NULL), + mRilClient(0), + mActivatedCP(false) { + loadRILD(); mInit = true; } AudioHardware::~AudioHardware() { + for (size_t index = 0; index < mInputs.size(); index++) { + closeInputStream((AudioStreamIn*)mInputs[index]); + } + mInputs.clear(); closeOutputStream((AudioStreamOut*)mOutput); + + if (mMixer) { + mixer_close(mMixer); + } + if (mPcm) { + pcm_close(mPcm); + } + + if (mSecRilLibHandle) { + if (disconnectRILD(mRilClient) != RIL_CLIENT_ERR_SUCCESS) + LOGE("Disconnect_RILD() error"); + + if (closeClientRILD(mRilClient) != RIL_CLIENT_ERR_SUCCESS) + LOGE("CloseClient_RILD() error"); + + mRilClient = 0; + + dlclose(mSecRilLibHandle); + mSecRilLibHandle = NULL; + } + mInit = false; } @@ -57,10 +102,77 @@ status_t AudioHardware::initCheck() return mInit ? NO_ERROR : NO_INIT; } +void AudioHardware::loadRILD(void) +{ + mSecRilLibHandle = dlopen("libsecril-client.so", RTLD_NOW); + + if (mSecRilLibHandle) { + LOGV("libsecril-client.so is loaded"); + + openClientRILD = (HRilClient (*)(void)) + dlsym(mSecRilLibHandle, "OpenClient_RILD"); + disconnectRILD = (int (*)(HRilClient)) + dlsym(mSecRilLibHandle, "Disconnect_RILD"); + closeClientRILD = (int (*)(HRilClient)) + dlsym(mSecRilLibHandle, "CloseClient_RILD"); + isConnectedRILD = (int (*)(HRilClient)) + dlsym(mSecRilLibHandle, "isConnected_RILD"); + connectRILD = (int (*)(HRilClient)) + dlsym(mSecRilLibHandle, "Connect_RILD"); + setCallVolume = (int (*)(HRilClient, SoundType, int)) + dlsym(mSecRilLibHandle, "SetCallVolume"); + setCallAudioPath = (int (*)(HRilClient, AudioPath)) + dlsym(mSecRilLibHandle, "SetCallAudioPath"); + setCallClockSync = (int (*)(HRilClient, SoundClockCondition)) + dlsym(mSecRilLibHandle, "SetCallClockSync"); + + if (!openClientRILD || !disconnectRILD || !closeClientRILD || + !isConnectedRILD || !connectRILD || + !setCallVolume || !setCallAudioPath || !setCallClockSync) { + LOGE("Can't load all functions from libsecril-client.so"); + + dlclose(mSecRilLibHandle); + mSecRilLibHandle = NULL; + } else { + mRilClient = openClientRILD(); + if (!mRilClient) { + LOGE("OpenClient_RILD() error"); + + dlclose(mSecRilLibHandle); + mSecRilLibHandle = NULL; + } + } + } else { + LOGE("Can't load libsecril-client.so"); + } +} + +status_t AudioHardware::connectRILDIfRequired(void) +{ + if (!mSecRilLibHandle) { + LOGE("connectIfRequired() lib is not loaded"); + return INVALID_OPERATION; + } + + if (isConnectedRILD(mRilClient)) { + return OK; + } + + if (connectRILD(mRilClient) != RIL_CLIENT_ERR_SUCCESS) { + LOGE("Connect_RILD() error"); + return INVALID_OPERATION; + } + + return OK; +} + AudioStreamOut* AudioHardware::openOutputStream( uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status) { + AudioStreamOutALSA* out = NULL; + status_t rc; + { // scope for the lock Mutex::Autolock lock(mLock); @@ -69,32 +181,40 @@ AudioStreamOut* AudioHardware::openOutputStream( if (status) { *status = INVALID_OPERATION; } - return 0; + return NULL; } - AudioStreamOutALSA* out = new AudioStreamOutALSA(); + out = new AudioStreamOutALSA(); - status_t rc = out->set(this, devices, format, channels, sampleRate); - if (rc) { - *status = rc; - } + rc = out->set(this, devices, format, channels, sampleRate); if (rc == NO_ERROR) { mOutput = out; - } else { + } + } + + if (rc != NO_ERROR) { + if (out) { delete out; } + out = NULL; + } + if (status) { + *status = rc; } - return mOutput; + + return out; } void AudioHardware::closeOutputStream(AudioStreamOut* out) { - Mutex::Autolock lock(mLock); - if (mOutput == 0 || mOutput != out) { - LOGW("Attempt to close invalid output stream"); - } else { - delete mOutput; + { + Mutex::Autolock lock(mLock); + if (mOutput == 0 || mOutput != out) { + LOGW("Attempt to close invalid output stream"); + return; + } mOutput = 0; } + delete out; } AudioStreamIn* AudioHardware::openInputStream( @@ -102,19 +222,118 @@ AudioStreamIn* AudioHardware::openInputStream( uint32_t *sampleRate, status_t *status, AudioSystem::audio_in_acoustics acoustic_flags) { - return 0; + // check for valid input source + if (!AudioSystem::isInputDevice((AudioSystem::audio_devices)devices)) { + if (status) { + *status = BAD_VALUE; + } + return NULL; + } + + status_t rc = NO_ERROR; + AudioStreamInALSA* in = NULL;; + + { // 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) { + delete in; + } + in = NULL; + } + if (status) { + *status = rc; + } + + LOGV("AudioHardware::openInputStream()%p", in); + return in; } void AudioHardware::closeInputStream(AudioStreamIn* in) { + { + Mutex::Autolock lock(mLock); + + ssize_t index = mInputs.indexOf((AudioStreamInALSA *)in); + if (index < 0) { + LOGW("Attempt to close invalid input stream"); + return; + } + mInputs.removeAt(index); + } + LOGV("AudioHardware::closeInputStream()%p", in); + delete in; } + status_t AudioHardware::setMode(int mode) { - return NO_ERROR; + AutoMutex lock(mLock); + int prevMode = mMode; + status_t status = AudioHardwareBase::setMode(mode); + LOGV("setMode() : new %d, old %d", mMode, prevMode); + if (status == NO_ERROR) { + // make sure that doAudioRouteOrMute() is called by doRouting() + // when entering or exiting in call mode even if the new device + // selected is the same as current one. + if ((mMode == AudioSystem::MODE_RINGTONE) || (mMode == AudioSystem::MODE_IN_CALL)) + { + if ((!mActivatedCP) && (mSecRilLibHandle) && (connectRILDIfRequired() == OK)) { + setCallClockSync(mRilClient, SOUND_CLOCK_START); + mActivatedCP = true; + } + } + + if (((prevMode != AudioSystem::MODE_IN_CALL) && (mMode == AudioSystem::MODE_IN_CALL)) || + ((prevMode == AudioSystem::MODE_IN_CALL) && (mMode != AudioSystem::MODE_IN_CALL))) { + if (mMode == AudioSystem::MODE_IN_CALL) { + openPcmOut_l(); + openMixer_l(); + setVoiceRecognition_l(false); + } + setIncallPath_l(mOutput->device()); + if (mMode != AudioSystem::MODE_IN_CALL) { + setVoiceRecognition_l(mVrModeEnabled); + closeMixer_l(); + mOutput->setNextRoute(getOutputRouteFromDevice(mOutput->device())); + closePcmOut_l(); + AudioStreamInALSA *input = getActiveInput_l(); + if (input != NULL) { + input->setNextRoute(getInputRouteFromDevice(input->device())); + } + } + } + + if (mMode == AudioSystem::MODE_NORMAL) { + if(mActivatedCP) + mActivatedCP = false; + } + } + + return status; } status_t AudioHardware::setMicMute(bool state) { + LOGV("setMicMute(%d) mMicMute %d", state, mMicMute); + AutoMutex lock(mLock); + if (mMicMute != state) { + mMicMute = state; + if (mMode != AudioSystem::MODE_IN_CALL) { + AudioStreamInALSA *input = getActiveInput_l(); + if (input != NULL) { + input->setNextRoute(getInputRouteFromDevice(input->device())); + } + } + } + return NO_ERROR; } @@ -126,6 +345,23 @@ status_t AudioHardware::getMicMute(bool* state) 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"; + + key = String8(BT_NREC_KEY); + if (param.get(key, value) == NO_ERROR) { + if (value == BT_NREC_VALUE_ON) { + mBluetoothNrec = true; + } else { + mBluetoothNrec = false; + LOGI("Turning noise reduction and echo cancellation off for BT " + "headset"); + } + } + return NO_ERROR; } @@ -141,17 +377,78 @@ String8 AudioHardware::getParameters(const String8& keys) size_t AudioHardware::getInputBufferSize(uint32_t sampleRate, int format, int channelCount) { - return 4096; + 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 v) + +status_t AudioHardware::setVoiceVolume(float volume) { + LOGI("### setVoiceVolume"); + + AutoMutex lock(mLock); + if ( (AudioSystem::MODE_IN_CALL == mMode) && (mSecRilLibHandle) && + (connectRILDIfRequired() == OK) ) { + + uint32_t device = AudioSystem::DEVICE_OUT_EARPIECE; + if (mOutput != NULL) { + device = mOutput->device(); + } + int int_volume = (int)(volume * 5); + SoundType type; + + LOGI("### route(%d) call volume(%f)", device, volume); + switch (device) { + case AudioSystem::DEVICE_OUT_EARPIECE: + LOGI("### earpiece call volume"); + type = SOUND_TYPE_VOICE; + break; + + case AudioSystem::DEVICE_OUT_SPEAKER: + LOGI("### speaker call volume"); + type = SOUND_TYPE_SPEAKER; + break; + + case AudioSystem::DEVICE_OUT_BLUETOOTH_SCO: + case AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET: + case AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT: + LOGI("### bluetooth call volume"); + type = SOUND_TYPE_BTVOICE; + break; + + case AudioSystem::DEVICE_OUT_WIRED_HEADSET: + case AudioSystem::DEVICE_OUT_WIRED_HEADPHONE: // Use receive path with 3 pole headset. + LOGI("### headset call volume"); + type = SOUND_TYPE_HEADSET; + break; + + default: + LOGW("### Call volume setting error!!!0x%08x \n", device); + type = SOUND_TYPE_VOICE; + break; + } + setCallVolume(mRilClient, type, int_volume); + } + return NO_ERROR; } -status_t AudioHardware::setMasterVolume(float v) +status_t AudioHardware::setMasterVolume(float volume) { - LOGI("Set master volume to %f.\n", v); + 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 @@ -163,10 +460,296 @@ status_t AudioHardware::dump(int fd, const Vector<String16>& args) return NO_ERROR; } +status_t AudioHardware::setIncallPath(uint32_t device) { + Mutex::Autolock lock(mLock); + return AudioHardware::setIncallPath_l(device); +} + +status_t AudioHardware::setIncallPath_l(uint32_t device) +{ + LOGV("setIncallPath: device %x", device); + + // Setup sound path for CP clocking + if ((mSecRilLibHandle) && + (connectRILDIfRequired() == OK)) { + + if (mMode == AudioSystem::MODE_IN_CALL) { + LOGI("### incall mode route (%d)", device); + AudioPath path; + switch(device){ + case AudioSystem::DEVICE_OUT_EARPIECE: + LOGI("### incall mode earpiece route"); + path = SOUND_AUDIO_PATH_HANDSET; + break; + + case AudioSystem::DEVICE_OUT_SPEAKER: + LOGI("### incall mode speaker route"); + path = SOUND_AUDIO_PATH_SPEAKER; + break; + + case AudioSystem::DEVICE_OUT_BLUETOOTH_SCO: + case AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET: + case AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT: + LOGI("### incall mode bluetooth route %s NR", mBluetoothNrec ? "" : "NO"); + if (mBluetoothNrec) { + path = SOUND_AUDIO_PATH_BLUETOOTH; + } else { + path = SOUND_AUDIO_PATH_BLUETOOTH_NO_NR; + } + break; + + case AudioSystem::DEVICE_OUT_WIRED_HEADSET : + case AudioSystem::DEVICE_OUT_WIRED_HEADPHONE : + LOGI("### incall mode headset route"); + path = SOUND_AUDIO_PATH_HEADSET; + break; + + default: + LOGW("### incall mode Error!! route = [%d]", device); + path = SOUND_AUDIO_PATH_HANDSET; + break; + } + + setCallAudioPath(mRilClient, path); + + if (mMixer != NULL) { + struct mixer_ctl *ctl= mixer_get_control(mMixer, "Voice Call Path", 0); + LOGE_IF(ctl == NULL, "setIncallPath_l() could not get mixer ctl"); + if (ctl != NULL) { + LOGV("setIncallPath_l() Voice Call Path, (%x)", device); + mixer_ctl_select(ctl, getVoiceRouteFromDevice(device)); + } + } + } + } + return NO_ERROR; +} + +struct pcm *AudioHardware::openPcmOut() +{ + AutoMutex lock(mLock); + return openPcmOut_l(); +} + +struct pcm *AudioHardware::openPcmOut_l() +{ + LOGI("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; + + mPcm = pcm_open(flags); + if (!pcm_ready(mPcm)) { + LOGE("openPcmOut_l() cannot open pcm_out driver: %s\n", pcm_error(mPcm)); + pcm_close(mPcm); + mPcm = 0; + } + } + return mPcm; +} + +void AudioHardware::closePcmOut() +{ + AutoMutex lock(mLock); + closePcmOut_l(); +} + +void AudioHardware::closePcmOut_l() +{ + LOGI("closePcmOut_l() mPcmOpenCnt: %d", mPcmOpenCnt); + if (mPcmOpenCnt == 0) { + LOGE("closePcmOut_l() mPcmOpenCnt == 0"); + return; + } + + if (--mPcmOpenCnt == 0) { + pcm_close(mPcm); + mPcm = NULL; + } +} + +struct mixer *AudioHardware::openMixer() +{ + AutoMutex lock(mLock); + return openMixer_l(); +} + +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", mPcm); + mMixerOpenCnt--; + return NULL; + } + mMixer = mixer_open(); + LOGE_IF(mMixer == NULL, "openMixer_l() cannot open mixer"); + } + return mMixer; +} + +void AudioHardware::closeMixer() +{ + AutoMutex lock(mLock); + closeMixer_l(); +} + +void AudioHardware::closeMixer_l() +{ + LOGV("closeMixer_l() mMixerOpenCnt: %d", mMixerOpenCnt); + if (mMixerOpenCnt == 0) { + LOGE("closeMixer_l() mMixerOpenCnt == 0"); + return; + } + + if (--mMixerOpenCnt == 0) { + mixer_close(mMixer); + 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: +//uncomment when kernel change is submitted +// if (mMode == AudioSystem::MODE_RINGTONE) return "RING_HP_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: +// return "HP_NO_MIC"; + case AudioSystem::DEVICE_OUT_WIRED_HEADSET: + 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 +AudioHardware::AudioStreamInALSA *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]->checkStandby()) { + return mInputs[i]; + } + } + + return NULL; +} + + +status_t AudioHardware::setVoiceRecognition(bool enable) +{ + AutoMutex lock(mLock); + return setVoiceRecognition_l(enable); +} + +status_t AudioHardware::setVoiceRecognition_l(bool enable) +{ + LOGV("setVoiceRecognition_l(%d)", enable); + if (enable != mVrModeEnabled) { + if (!(enable && (mMode == AudioSystem::MODE_IN_CALL))) { + openMixer_l(); + if (mMixer) { + struct mixer_ctl *ctl= mixer_get_control(mMixer, "Recognition Control", 0); + if (ctl == NULL) { + closeMixer_l(); + return NO_INIT; + } + const char *mode = enable ? "RECOGNITION_ON" : "RECOGNITION_OFF"; + LOGV("mixer_ctl_select, Recognition Control, (%s)", mode); + mixer_ctl_select(ctl, mode); + } + closeMixer_l(); + } + mVrModeEnabled = enable; + } + + return NO_ERROR; +} + + +//------------------------------------------------------------------------------ +// AudioStreamOutALSA +//------------------------------------------------------------------------------ + AudioHardware::AudioStreamOutALSA::AudioStreamOutALSA() : - mHardware(0), mPcm(0), mMixer(0), mRouteCtl(0), mStartCount(0), mRetryCount(0), + mHardware(0), mPcm(0), mMixer(0), mRouteCtl(0), mStartCount(0), mStandby(true), mDevices(0), mChannels(AUDIO_HW_OUT_CHANNELS), - mSampleRate(AUDIO_HW_OUT_SAMPLERATE), mBufferSize(AUDIO_HW_OUT_BUFSZ) + mSampleRate(AUDIO_HW_OUT_SAMPLERATE), mBufferSize(AUDIO_HW_OUT_PERIOD_BYTES) { } @@ -202,7 +785,7 @@ status_t AudioHardware::AudioStreamOutALSA::set( mChannels = lChannels; mSampleRate = lRate; - mBufferSize = 4096; + mBufferSize = AUDIO_HW_OUT_PERIOD_BYTES; return NO_ERROR; } @@ -214,51 +797,41 @@ AudioHardware::AudioStreamOutALSA::~AudioStreamOutALSA() ssize_t AudioHardware::AudioStreamOutALSA::write(const void* buffer, size_t bytes) { - // LOGD("AudioStreamOutALSA::write(%p, %u)", buffer, 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; + AutoMutex lock(mLock); if (mStandby) { LOGV("open pcm_out driver"); - next_route = "SPK"; - - mPcm = pcm_open(PCM_OUT); + mPcm = mHardware->openPcmOut(); if (!pcm_ready(mPcm)) { LOGE("cannot open pcm_out driver: %s\n", pcm_error(mPcm)); - pcm_close(mPcm); + mHardware->closePcmOut(); mPcm = 0; goto Error; } mMixer = mixer_open(); - if (mMixer) + if (mMixer) { + LOGV("open playback normal"); mRouteCtl = mixer_get_control(mMixer, "Playback Path", 0); - -#if 0 - LOGV("set pcm_out config"); - config.channel_count = AudioSystem::popCount(channels()); - config.sample_rate = mSampleRate; - config.buffer_size = mBufferSize; - config.buffer_count = AUDIO_HW_NUM_OUT_BUF; -// config.codec_type = CODEC_TYPE_PCM; - status = ioctl(mFd, AUDIO_SET_CONFIG, &config); - if (status < 0) { - LOGE("Cannot set config"); - goto Error; } - - LOGV("buffer_size: %u", config.buffer_size); - LOGV("buffer_count: %u", config.buffer_count); - LOGV("channel_count: %u", config.channel_count); - LOGV("sample_rate: %u", config.sample_rate); -#endif - + if (mHardware->mode() == AudioSystem::MODE_IN_CALL) { + next_route = 0; + } else { + next_route = mHardware->getOutputRouteFromDevice(mDevices); + } + acquire_wake_lock (PARTIAL_WAKE_LOCK, "AudioOutLock"); mStandby = false; } ret = pcm_write(mPcm,(void*) p, bytes); + + // FIXME:: this is done here as setting the route doesn't seem to work before reading + // first buffer if (ret == 0) { if (next_route && mRouteCtl) { mixer_ctl_select(mRouteCtl, next_route); @@ -280,10 +853,17 @@ Error: status_t AudioHardware::AudioStreamOutALSA::standby() { - if (mPcm) { - pcm_close(mPcm); - mPcm = 0; + AutoMutex lock(mLock); + if (!mStandby) { + if (next_route && mRouteCtl) { + mixer_ctl_select(mRouteCtl, next_route); + next_route = 0; + } + release_wake_lock("AudioOutLock"); + mStandby = true; + LOGV("AudioHardware pcm playback is going to standby."); } + if (mMixer) { mixer_close(mMixer); mMixer = 0; @@ -291,8 +871,10 @@ status_t AudioHardware::AudioStreamOutALSA::standby() mRouteCtl = 0; } } - mStandby = true; - LOGI("AudioHardware pcm playback is going to standby."); + if (mPcm) { + mHardware->closePcmOut(); + mPcm = 0; + } return NO_ERROR; } @@ -308,21 +890,729 @@ bool AudioHardware::AudioStreamOutALSA::checkStandby() status_t AudioHardware::AudioStreamOutALSA::setParameters(const String8& keyValuePairs) { - return NO_ERROR; + AudioParameter param = AudioParameter(keyValuePairs); + status_t status = NO_ERROR; + int device; + LOGD("AudioStreamOutALSA::setParameters() %s", keyValuePairs.string()); + + if (mHardware == NULL) return NO_INIT; + + if (param.getInt(String8(AudioParameter::keyRouting), device) == NO_ERROR) + { + if (mDevices != (uint32_t)device) { + mDevices = (uint32_t)device; + if (mHardware->mode() != AudioSystem::MODE_IN_CALL) { + next_route = mHardware->getOutputRouteFromDevice(device); + } + } + if (mHardware->mode() == AudioSystem::MODE_IN_CALL) { + mHardware->setIncallPath(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; } +//------------------------------------------------------------------------------ +// AudioStreamOutALSA +//------------------------------------------------------------------------------ + +AudioHardware::AudioStreamInALSA::AudioStreamInALSA() : + mHardware(0), mPcm(0), mMixer(0), mRouteCtl(0), mStartCount(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) +{ +} + +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; + + AutoMutex lock(mLock); + + if (mStandby) { + LOGV("open pcm_in driver"); + + 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; + + mPcm = pcm_open(flags); + if (!pcm_ready(mPcm)) { + LOGE("cannot open pcm_out driver: %s\n", pcm_error(mPcm)); + pcm_close(mPcm); + mPcm = 0; + goto Error; + } + + mMixer = mixer_open(); + if (mMixer) { + mRouteCtl = mixer_get_control(mMixer, "Capture MIC Path", 0); + mMicCtl = mixer_get_control(mMixer, "Mic Status", 0); + if (mMicCtl) { + LOGV("turn mic ON"); + mixer_ctl_select(mRouteCtl, "MIC_USE"); + } + } + + if (mDownSampler != NULL) { + mInPcmInBuf = 0; + mDownSampler->reset(); + } + if (mHardware->mode() != AudioSystem::MODE_IN_CALL) { + next_route = mHardware->getInputRouteFromDevice(mDevices); + } + acquire_wake_lock (PARTIAL_WAKE_LOCK, "AudioInLock"); + 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 { + ret = pcm_read(mPcm, buffer, bytes); + } + + // FIXME:: this is done here as setting the route doesn't seem to work before reading + // first buffer + if (ret == 0) { + if (next_route && mRouteCtl) { + LOGV("set route : %s ", next_route); + mixer_ctl_select(mRouteCtl, next_route); + next_route = 0; + } + return bytes; + } + + LOGW("read error: %d", ret); + status = ret; + +Error: + standby(); + + // Simulate audio output timing in case of error + usleep(bytes * 1000000 / frameSize() / sampleRate()); + + return status; +} + +status_t AudioHardware::AudioStreamInALSA::standby() +{ + AutoMutex lock(mLock); + + if (!mStandby) { + if (next_route && mRouteCtl) { + mixer_ctl_select(mRouteCtl, next_route); + next_route = 0; + } + release_wake_lock("AudioInLock"); + mStandby = true; + LOGI("AudioHardware pcm playback is going to standby."); + } + + if (mPcm) { + pcm_close(mPcm); + mPcm = 0; + } + if (mMixer) { + if (mMicCtl) { + LOGV("turn mic OFF"); + mixer_ctl_select(mMicCtl, "MIC_NO_USE"); + mMicCtl = 0; + } + mixer_close(mMixer); + mMixer = 0; + if (mRouteCtl) { + mRouteCtl = 0; + } + + } + return NO_ERROR; +} + +status_t AudioHardware::AudioStreamInALSA::dump(int fd, const Vector<String16>& args) +{ + 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; + + if (param.getInt(String8(VOICE_REC_MODE_KEY), value) == NO_ERROR) { + mHardware->setVoiceRecognition((value != 0)); + param.remove(String8(VOICE_REC_MODE_KEY)); + } + + if (param.getInt(String8(AudioParameter::keyRouting), value) == NO_ERROR) + { + if (value != 0) { + if (mDevices != (uint32_t)value) { + if (mHardware->mode() != AudioSystem::MODE_IN_CALL) { + next_route = mHardware->getInputRouteFromDevice((uint32_t)value); + } + mDevices = (uint32_t)value; + } + } + 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; + return NO_INIT; + } + + if (mInPcmInBuf == 0) { + mReadStatus = pcm_read(mPcm,(void*) mPcmIn, AUDIO_HW_IN_PERIOD_SZ * frameSize()); + 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 ; +} + +//------------------------------------------------------------------------------ +// 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; + + uint32_t in_sample_num = 0; // 16.16 fixed point + const uint32_t step = (uint32_t)(step_float * 65536.0f + 0.5f); // 16.16 fixed point + for (int j = 0; j < RESAMPLE_16KHZ_SAMPLES_OUT; ++j, in_sample_num += step) { + const uint32_t whole = in_sample_num >> 16; + const uint32_t frac = (in_sample_num & 0xffff); // 0.16 fixed point + const int32_t s1 = tmp[whole]; + const int32_t s2 = tmp[whole + 1]; + *output++ = clip(s1 + (((s2 - s1) * (int32_t)frac) >> 16)); + } + } + + 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(); } diff --git a/libaudio2/AudioHardware.h b/libaudio2/AudioHardware.h index 9bdfe2f..ab81d9c 100644 --- a/libaudio2/AudioHardware.h +++ b/libaudio2/AudioHardware.h @@ -25,6 +25,8 @@ #include <hardware_legacy/AudioHardwareBase.h> +#include "secril-client.h" + extern "C" { struct pcm; struct mixer; @@ -33,11 +35,6 @@ extern "C" { namespace android { -#define CODEC_TYPE_PCM 0 -#define PCM_FILL_BUFFER_COUNT 1 -// Number of buffers in audio driver for output -#define AUDIO_HW_NUM_OUT_BUF 2 - // 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 @@ -47,27 +44,36 @@ namespace android { #define AUDIO_HW_OUT_CHANNELS (AudioSystem::CHANNEL_OUT_STEREO) // Default audio output sample format #define AUDIO_HW_OUT_FORMAT (AudioSystem::PCM_16_BIT) -// Default audio output buffer size -#define AUDIO_HW_OUT_BUFSZ 4096 +// 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)) -#if 0 // 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) -// Default audio input buffer size -#define AUDIO_HW_IN_BUFSZ 256 +// 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) -// Maximum voice volume -#define VOICE_VOLUME_MAX 5 -#endif +#define VOICE_REC_MODE_KEY "vr_mode" class AudioHardware : public AudioHardwareBase { class AudioStreamOutALSA; + class AudioStreamInALSA; public: + AudioHardware(); virtual ~AudioHardware(); virtual status_t initCheck(); @@ -98,17 +104,66 @@ public: virtual size_t getInputBufferSize( uint32_t sampleRate, int format, int channelCount); - void clearCurDevice() { } + 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(uint32_t device); + status_t setIncallPath_l(uint32_t device); + + status_t setVoiceRecognition(bool enable); + status_t setVoiceRecognition_l(bool enable); + + static uint32_t getInputSampleRate(uint32_t sampleRate); + AudioStreamInALSA* getActiveInput_l(); + + void lock() { mLock.lock(); } + void unlock() { mLock.unlock(); } + + struct pcm *openPcmOut(); + struct pcm *openPcmOut_l(); + void closePcmOut(); + void closePcmOut_l(); + + struct mixer *openMixer(); + struct mixer *openMixer_l(); + void closeMixer(); + void closeMixer_l(); protected: virtual status_t dump(int fd, const Vector<String16>& args); private: - bool mInit; - bool mMicMute; - AudioStreamOutALSA *mOutput; - Mutex mLock; - struct mixer *mMixer; + + bool mInit; + bool mMicMute; + AudioStreamOutALSA* mOutput; + SortedVector <AudioStreamInALSA*> mInputs; + Mutex mLock; + struct pcm* mPcm; + struct mixer* mMixer; + uint32_t mPcmOpenCnt; + uint32_t mMixerOpenCnt; + + bool mVrModeEnabled; + bool mBluetoothNrec; + void* mSecRilLibHandle; + HRilClient mRilClient; + bool mActivatedCP; + HRilClient (*openClientRILD) (void); + int (*disconnectRILD) (HRilClient); + int (*closeClientRILD) (HRilClient); + int (*isConnectedRILD) (HRilClient); + int (*connectRILD) (HRilClient); + int (*setCallVolume) (HRilClient, SoundType, int); + int (*setCallAudioPath)(HRilClient, AudioPath); + int (*setCallClockSync)(HRilClient, SoundClockCondition); + void loadRILD(void); + status_t connectRILDIfRequired(void); + + static uint32_t checkInputSampleRate(uint32_t sampleRate); + static const uint32_t inputSamplingRates[]; class AudioStreamOutALSA : public AudioStreamOut { @@ -129,7 +184,7 @@ private: virtual int format() const { return AUDIO_HW_OUT_FORMAT; } virtual uint32_t latency() - const { return (1000 * AUDIO_HW_NUM_OUT_BUF * + const { return (1000 * AUDIO_HW_OUT_PERIOD_CNT * (bufferSize()/frameSize()))/sampleRate() + AUDIO_HW_OUT_LATENCY_MS; } virtual status_t setVolume(float left, float right) @@ -140,11 +195,14 @@ private: bool checkStandby(); virtual status_t setParameters(const String8& keyValuePairs); virtual String8 getParameters(const String8& keys); - uint32_t devices() - { return mDevices; } + uint32_t device() { return mDevices; } virtual status_t getRenderPosition(uint32_t *dspFrames); + void setNextRoute(const char *route) { next_route = route; } + private: + + Mutex mLock; AudioHardware* mHardware; struct pcm *mPcm; struct mixer *mMixer; @@ -158,6 +216,118 @@ private: uint32_t mSampleRate; size_t mBufferSize; }; + + 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: + 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 setNextRoute(const char *route) { next_route = route; } + + static size_t getBufferSize(uint32_t sampleRate, int channelCount); + + // BufferProvider + virtual status_t getNextBuffer(BufferProvider::Buffer* buffer); + virtual void releaseBuffer(BufferProvider::Buffer* buffer); + + private: + Mutex mLock; + AudioHardware* mHardware; + struct pcm *mPcm; + struct mixer *mMixer; + struct mixer_ctl *mRouteCtl; + struct mixer_ctl *mMicCtl; + const char *next_route; + int mStartCount; + int mRetryCount; + 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; + }; + }; }; // namespace android diff --git a/libaudio2/alsa_audio.h b/libaudio2/alsa_audio.h index ecc78a9..3cb86d9 100644 --- a/libaudio2/alsa_audio.h +++ b/libaudio2/alsa_audio.h @@ -30,6 +30,13 @@ struct pcm; #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 */ diff --git a/libaudio2/alsa_pcm.c b/libaudio2/alsa_pcm.c index ad47b1a..c16a17e 100644 --- a/libaudio2/alsa_pcm.c +++ b/libaudio2/alsa_pcm.c @@ -14,6 +14,11 @@ ** limitations under the License. */ +#define LOG_TAG "alsa_pcm" +//#define LOG_NDEBUG 0 +#include <cutils/log.h> +#include <cutils/config_utils.h> + #include <stdio.h> #include <stdlib.h> #include <fcntl.h> @@ -236,7 +241,6 @@ int pcm_write(struct pcm *pcm, void *data, unsigned count) return oops(pcm, errno, "cannot prepare channel"); if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x)) return oops(pcm, errno, "cannot write initial data"); - fprintf(stderr,"RUNNING\n"); pcm->running = 1; return 0; } @@ -249,7 +253,6 @@ int pcm_write(struct pcm *pcm, void *data, unsigned count) } return oops(pcm, errno, "cannot write stream data"); } - fprintf(stderr,"."); return 0; } } @@ -264,13 +267,13 @@ int pcm_read(struct pcm *pcm, void *data, unsigned count) x.buf = data; x.frames = (pcm->flags & PCM_MONO) ? (count / 2) : (count / 4); +// LOGV("read() %d frames", x.frames); for (;;) { if (!pcm->running) { if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_PREPARE)) return oops(pcm, errno, "cannot prepare channel"); if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_START)) return oops(pcm, errno, "cannot start channel"); - fprintf(stderr,"RUNNING\n"); pcm->running = 1; return 0; } @@ -283,7 +286,7 @@ int pcm_read(struct pcm *pcm, void *data, unsigned count) } return oops(pcm, errno, "cannot read stream data"); } - fprintf(stderr,"."); +// LOGV("read() got %d frames", x.frames); return 0; } } @@ -312,7 +315,10 @@ struct pcm *pcm_open(unsigned flags) struct snd_pcm_info info; struct snd_pcm_hw_params params; struct snd_pcm_sw_params sparams; - unsigned bufsz = 8192; + unsigned period_sz; + unsigned period_cnt; + + LOGV("pcm_open(0x%08x)",flags); pcm = calloc(1, sizeof(struct pcm)); if (!pcm) @@ -324,6 +330,13 @@ struct pcm *pcm_open(unsigned flags) dname = "/dev/snd/pcmC0D0p"; } + LOGV("pcm_open() period sz multiplier %d", + ((flags & PCM_PERIOD_SZ_MASK) >> PCM_PERIOD_SZ_SHIFT) + 1); + period_sz = 128 * (((flags & PCM_PERIOD_SZ_MASK) >> PCM_PERIOD_SZ_SHIFT) + 1); + LOGV("pcm_open() period cnt %d", + ((flags & PCM_PERIOD_CNT_MASK) >> PCM_PERIOD_CNT_SHIFT) + PCM_PERIOD_CNT_MIN); + period_cnt = ((flags & PCM_PERIOD_CNT_MASK) >> PCM_PERIOD_CNT_SHIFT) + PCM_PERIOD_CNT_MIN; + pcm->flags = flags; pcm->fd = open(dname, O_RDWR); if (pcm->fd < 0) { @@ -337,6 +350,9 @@ struct pcm *pcm_open(unsigned flags) } info_dump(&info); + LOGV("pcm_open() period_cnt %d period_sz %d channels %d", + period_cnt, period_sz, (flags & PCM_MONO) ? 1 : 2); + param_init(¶ms); param_set_mask(¶ms, SNDRV_PCM_HW_PARAM_ACCESS, SNDRV_PCM_ACCESS_RW_INTERLEAVED); @@ -344,13 +360,13 @@ struct pcm *pcm_open(unsigned flags) SNDRV_PCM_FORMAT_S16_LE); param_set_mask(¶ms, SNDRV_PCM_HW_PARAM_SUBFORMAT, SNDRV_PCM_SUBFORMAT_STD); - param_set_min(¶ms, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, bufsz); + param_set_min(¶ms, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, period_sz); param_set_int(¶ms, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, 16); param_set_int(¶ms, SNDRV_PCM_HW_PARAM_FRAME_BITS, (flags & PCM_MONO) ? 16 : 32); param_set_int(¶ms, SNDRV_PCM_HW_PARAM_CHANNELS, (flags & PCM_MONO) ? 1 : 2); - param_set_int(¶ms, SNDRV_PCM_HW_PARAM_PERIODS, 2); + param_set_int(¶ms, SNDRV_PCM_HW_PARAM_PERIODS, period_cnt); param_set_int(¶ms, SNDRV_PCM_HW_PARAM_RATE, 44100); if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_HW_PARAMS, ¶ms)) { @@ -363,9 +379,9 @@ struct pcm *pcm_open(unsigned flags) sparams.tstamp_mode = SNDRV_PCM_TSTAMP_NONE; sparams.period_step = 1; sparams.avail_min = 1; - sparams.start_threshold = bufsz / 4; - sparams.stop_threshold = bufsz / 4; - sparams.xfer_align = bufsz / 8; /* needed for old kernels */ + sparams.start_threshold = period_cnt * period_sz; + sparams.stop_threshold = period_cnt * period_sz; + sparams.xfer_align = period_sz / 2; /* needed for old kernels */ sparams.silence_size = 0; sparams.silence_threshold = 0; @@ -374,7 +390,7 @@ struct pcm *pcm_open(unsigned flags) goto fail; } - pcm->buffer_size = bufsz / 2; + pcm->buffer_size = period_cnt * period_sz; pcm->underruns = 0; return pcm; |