diff options
-rwxr-xr-x | BoardConfig.mk | 4 | ||||
-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 |
6 files changed, 1583 insertions, 93 deletions
diff --git a/BoardConfig.mk b/BoardConfig.mk index 881cd52..c5253df 100755 --- a/BoardConfig.mk +++ b/BoardConfig.mk @@ -49,8 +49,8 @@ TARGET_ARCH_VARIANT := armv7-a-neon ARCH_ARM_HAVE_TLS_REGISTER := true ifeq ($(BOARD_USES_GENERIC_AUDIO),false) -BOARD_USES_ALSA_AUDIO := true -BUILD_WITH_ALSA_UTILS := true +BOARD_USES_ALSA_AUDIO := false +BUILD_WITH_ALSA_UTILS := false endif USE_CAMERA_STUB := false 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; |