/* ** Copyright 2010, The Android Open-Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. ** You may obtain a copy of the License at ** ** http://www.apache.org/licenses/LICENSE-2.0 ** ** Unless required by applicable law or agreed to in writing, software ** distributed under the License is distributed on an "AS IS" BASIS, ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ** See the License for the specific language governing permissions and ** limitations under the License. */ #include <math.h> //#define LOG_NDEBUG 0 #define LOG_TAG "AudioHardware" #include <utils/Log.h> #include <utils/String8.h> #include <stdio.h> #include <unistd.h> #include <sys/ioctl.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/resource.h> #include <dlfcn.h> #include <fcntl.h> #include "AudioHardware.h" #include <audio_effects/effect_aec.h> extern "C" { #include <tinyalsa/asoundlib.h> } namespace android_audio_legacy { const uint32_t AudioHardware::inputConfigTable[][AudioHardware::INPUT_CONFIG_CNT] = { {8000, 4}, {11025, 4}, {16000, 2}, {22050, 2}, {32000, 1}, {44100, 1} }; // trace driver operations for dump // #define DRIVER_TRACE enum { DRV_NONE, DRV_PCM_OPEN, DRV_PCM_CLOSE, DRV_PCM_WRITE, DRV_PCM_READ, DRV_MIXER_OPEN, DRV_MIXER_CLOSE, DRV_MIXER_GET, DRV_MIXER_SEL }; #ifdef DRIVER_TRACE #define TRACE_DRIVER_IN(op) mDriverOp = op; #define TRACE_DRIVER_OUT mDriverOp = DRV_NONE; #else #define TRACE_DRIVER_IN(op) #define TRACE_DRIVER_OUT #endif // ---------------------------------------------------------------------------- const char *AudioHardware::inputPathNameDefault = "Default"; const char *AudioHardware::inputPathNameCamcorder = "Camcorder"; const char *AudioHardware::inputPathNameVoiceRecognition = "Voice Recognition"; AudioHardware::AudioHardware() : mInit(false), mMicMute(false), mPcm(NULL), mMixer(NULL), mPcmOpenCnt(0), mMixerOpenCnt(0), mInCallAudioMode(false), mVoiceVol(1.0f), mInputSource(AUDIO_SOURCE_DEFAULT), mBluetoothNrec(true), mTTYMode(TTY_MODE_OFF), mSecRilLibHandle(NULL), mRilClient(0), mActivatedCP(false), mEchoReference(NULL), mDriverOp(DRV_NONE) { loadRILD(); mInit = true; } AudioHardware::~AudioHardware() { for (size_t index = 0; index < mInputs.size(); index++) { closeInputStream(mInputs[index].get()); } mInputs.clear(); closeOutputStream((AudioStreamOut*)mOutput.get()); if (mMixer) { TRACE_DRIVER_IN(DRV_MIXER_CLOSE) mixer_close(mMixer); TRACE_DRIVER_OUT } if (mPcm) { TRACE_DRIVER_IN(DRV_PCM_CLOSE) pcm_close(mPcm); TRACE_DRIVER_OUT } if (mSecRilLibHandle) { if (disconnectRILD(mRilClient) != RIL_CLIENT_ERR_SUCCESS) ALOGE("Disconnect_RILD() error"); if (closeClientRILD(mRilClient) != RIL_CLIENT_ERR_SUCCESS) ALOGE("CloseClient_RILD() error"); mRilClient = 0; dlclose(mSecRilLibHandle); mSecRilLibHandle = NULL; } mInit = false; } status_t AudioHardware::initCheck() { return mInit ? NO_ERROR : NO_INIT; } void AudioHardware::loadRILD(void) { mSecRilLibHandle = dlopen("libsecril-client.so", RTLD_NOW); if (mSecRilLibHandle) { ALOGV("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) { ALOGE("Can't load all functions from libsecril-client.so"); dlclose(mSecRilLibHandle); mSecRilLibHandle = NULL; } else { mRilClient = openClientRILD(); if (!mRilClient) { ALOGE("OpenClient_RILD() error"); dlclose(mSecRilLibHandle); mSecRilLibHandle = NULL; } } } else { ALOGE("Can't load libsecril-client.so"); } } status_t AudioHardware::connectRILDIfRequired(void) { if (!mSecRilLibHandle) { ALOGE("connectIfRequired() lib is not loaded"); return INVALID_OPERATION; } if (isConnectedRILD(mRilClient)) { return OK; } if (connectRILD(mRilClient) != RIL_CLIENT_ERR_SUCCESS) { ALOGE("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) { sp <AudioStreamOutALSA> out; status_t rc; { // scope for the lock Mutex::Autolock lock(mLock); // only one output stream allowed if (mOutput != 0) { if (status) { *status = INVALID_OPERATION; } return NULL; } out = new AudioStreamOutALSA(); rc = out->set(this, devices, format, channels, sampleRate); if (rc == NO_ERROR) { mOutput = out; } } if (rc != NO_ERROR) { if (out != 0) { out.clear(); } } if (status) { *status = rc; } return out.get(); } void AudioHardware::closeOutputStream(AudioStreamOut* out) { sp <AudioStreamOutALSA> spOut; sp<AudioStreamInALSA> spIn; { Mutex::Autolock lock(mLock); if (mOutput == 0 || mOutput.get() != out) { ALOGW("Attempt to close invalid output stream"); return; } spOut = mOutput; mOutput.clear(); if (mEchoReference != NULL) { spIn = getActiveInput_l(); } } if (spIn != 0) { // this will safely release the echo reference by calling releaseEchoReference() // after placing the active input in standby spIn->standby(); } spOut.clear(); } AudioStreamIn* AudioHardware::openInputStream( uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status, AudioSystem::audio_in_acoustics acoustic_flags) { // check for valid input source if (!AudioSystem::isInputDevice((AudioSystem::audio_devices)devices)) { if (status) { *status = BAD_VALUE; } return NULL; } status_t rc = NO_ERROR; sp <AudioStreamInALSA> in; { // scope for the lock Mutex::Autolock lock(mLock); in = new AudioStreamInALSA(); rc = in->set(this, devices, format, channels, sampleRate, acoustic_flags); if (rc == NO_ERROR) { mInputs.add(in); } } if (rc != NO_ERROR) { if (in != 0) { in.clear(); } } if (status) { *status = rc; } ALOGV("AudioHardware::openInputStream()%p", in.get()); return in.get(); } void AudioHardware::closeInputStream(AudioStreamIn* in) { sp<AudioStreamInALSA> spIn; { Mutex::Autolock lock(mLock); ssize_t index = mInputs.indexOf((AudioStreamInALSA *)in); if (index < 0) { ALOGW("Attempt to close invalid input stream"); return; } spIn = mInputs[index]; mInputs.removeAt(index); } ALOGV("AudioHardware::closeInputStream()%p", in); spIn.clear(); } status_t AudioHardware::setMode(int mode) { sp<AudioStreamOutALSA> spOut; sp<AudioStreamInALSA> spIn; status_t status; // Mutex acquisition order is always out -> in -> hw AutoMutex lock(mLock); spOut = mOutput; while (spOut != 0) { if (!spOut->checkStandby()) { int cnt = spOut->prepareLock(); mLock.unlock(); spOut->lock(); mLock.lock(); // make sure that another thread did not change output state while the // mutex is released if ((spOut == mOutput) && (cnt == spOut->standbyCnt())) { break; } spOut->unlock(); spOut = mOutput; } else { spOut.clear(); } } // spOut is not 0 here only if the output is active spIn = getActiveInput_l(); while (spIn != 0) { int cnt = spIn->prepareLock(); mLock.unlock(); spIn->lock(); mLock.lock(); // make sure that another thread did not change input state while the // mutex is released if ((spIn == getActiveInput_l()) && (cnt == spIn->standbyCnt())) { break; } spIn->unlock(); spIn = getActiveInput_l(); } // spIn is not 0 here only if the input is active int prevMode = mMode; status = AudioHardwareBase::setMode(mode); ALOGV("setMode() : new %d, old %d", mMode, prevMode); if (status == NO_ERROR) { bool modeNeedsCPActive = mMode == AudioSystem::MODE_IN_CALL || mMode == AudioSystem::MODE_RINGTONE; // activate call clock in radio when entering in call or ringtone mode if (modeNeedsCPActive) { if ((!mActivatedCP) && (mSecRilLibHandle) && (connectRILDIfRequired() == OK)) { setCallClockSync(mRilClient, SOUND_CLOCK_START); mActivatedCP = true; } } if (mMode == AudioSystem::MODE_IN_CALL && !mInCallAudioMode) { if (spOut != 0) { ALOGV("setMode() in call force output standby"); spOut->doStandby_l(); } if (spIn != 0) { ALOGV("setMode() in call force input standby"); spIn->doStandby_l(); } ALOGV("setMode() openPcmOut_l()"); openPcmOut_l(); openMixer_l(); setInputSource_l(AUDIO_SOURCE_DEFAULT); setVoiceVolume_l(mVoiceVol); mInCallAudioMode = true; } if (mMode != AudioSystem::MODE_IN_CALL && mInCallAudioMode) { setInputSource_l(mInputSource); if (mMixer != NULL) { TRACE_DRIVER_IN(DRV_MIXER_GET) struct mixer_ctl *ctl= mixer_get_ctl_by_name(mMixer, "Playback Path"); TRACE_DRIVER_OUT if (ctl != NULL) { ALOGV("setMode() reset Playback Path to RCV"); TRACE_DRIVER_IN(DRV_MIXER_SEL) mixer_ctl_set_enum_by_string(ctl, "RCV"); TRACE_DRIVER_OUT } } ALOGV("setMode() closePcmOut_l()"); closeMixer_l(); closePcmOut_l(); if (spOut != 0) { ALOGV("setMode() off call force output standby"); spOut->doStandby_l(); } if (spIn != 0) { ALOGV("setMode() off call force input standby"); spIn->doStandby_l(); } mInCallAudioMode = false; } if (!modeNeedsCPActive) { if(mActivatedCP) mActivatedCP = false; } } if (spIn != 0) { spIn->unlock(); } if (spOut != 0) { spOut->unlock(); } return status; } status_t AudioHardware::setMicMute(bool state) { ALOGV("setMicMute(%d) mMicMute %d", state, mMicMute); sp<AudioStreamInALSA> spIn; { AutoMutex lock(mLock); if (mMicMute != state) { mMicMute = state; // in call mute is handled by RIL if (mMode != AudioSystem::MODE_IN_CALL) { spIn = getActiveInput_l(); } } } if (spIn != 0) { spIn->standby(); } return NO_ERROR; } status_t AudioHardware::getMicMute(bool* state) { *state = mMicMute; return NO_ERROR; } status_t AudioHardware::setParameters(const String8& keyValuePairs) { AudioParameter param = AudioParameter(keyValuePairs); String8 value; String8 key; const char BT_NREC_KEY[] = "bt_headset_nrec"; const char BT_NREC_VALUE_ON[] = "on"; const char TTY_MODE_KEY[] = "tty_mode"; const char TTY_MODE_VALUE_OFF[] = "tty_off"; const char TTY_MODE_VALUE_VCO[] = "tty_vco"; const char TTY_MODE_VALUE_HCO[] = "tty_hco"; const char TTY_MODE_VALUE_FULL[] = "tty_full"; key = String8(BT_NREC_KEY); if (param.get(key, value) == NO_ERROR) { if (value == BT_NREC_VALUE_ON) { mBluetoothNrec = true; } else { mBluetoothNrec = false; ALOGD("Turning noise reduction and echo cancellation off for BT " "headset"); } param.remove(String8(BT_NREC_KEY)); } key = String8(TTY_MODE_KEY); if (param.get(key, value) == NO_ERROR) { int ttyMode; if (value == TTY_MODE_VALUE_OFF) { ttyMode = TTY_MODE_OFF; } else if (value == TTY_MODE_VALUE_VCO) { ttyMode = TTY_MODE_VCO; } else if (value == TTY_MODE_VALUE_HCO) { ttyMode = TTY_MODE_HCO; } else if (value == TTY_MODE_VALUE_FULL) { ttyMode = TTY_MODE_FULL; } else { return BAD_VALUE; } if (ttyMode != mTTYMode) { ALOGV("new tty mode %d", ttyMode); mTTYMode = ttyMode; if (mOutput != 0 && mMode == AudioSystem::MODE_IN_CALL) { setIncallPath_l(mOutput->device()); } } param.remove(String8(TTY_MODE_KEY)); } return NO_ERROR; } String8 AudioHardware::getParameters(const String8& keys) { AudioParameter request = AudioParameter(keys); AudioParameter reply = AudioParameter(); ALOGV("getParameters() %s", keys.string()); return reply.toString(); } size_t AudioHardware::getInputBufferSize(uint32_t sampleRate, int format, int channelCount) { if (format != AudioSystem::PCM_16_BIT) { ALOGW("getInputBufferSize bad format: %d", format); return 0; } if (channelCount < 1 || channelCount > 2) { ALOGW("getInputBufferSize bad channel count: %d", channelCount); return 0; } if (sampleRate != getInputSampleRate(sampleRate)) { ALOGW("getInputBufferSize bad sample rate: %d", sampleRate); return 0; } return AudioStreamInALSA::getBufferSize(sampleRate, channelCount); } status_t AudioHardware::setVoiceVolume(float volume) { AutoMutex lock(mLock); setVoiceVolume_l(volume); return NO_ERROR; } void AudioHardware::setVoiceVolume_l(float volume) { ALOGD("### setVoiceVolume_l"); mVoiceVol = volume; if ( (AudioSystem::MODE_IN_CALL == mMode) && (mSecRilLibHandle) && (connectRILDIfRequired() == OK) ) { uint32_t device = AudioSystem::DEVICE_OUT_EARPIECE; if (mOutput != 0) { device = mOutput->device(); } int int_volume = (int)(volume * 5); SoundType type; ALOGD("### route(%d) call volume(%f)", device, volume); switch (device) { case AudioSystem::DEVICE_OUT_EARPIECE: ALOGD("### earpiece call volume"); type = SOUND_TYPE_VOICE; break; case AudioSystem::DEVICE_OUT_SPEAKER: ALOGD("### 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: ALOGD("### 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. ALOGD("### headset call volume"); type = SOUND_TYPE_HEADSET; break; default: ALOGW("### Call volume setting error!!!0x%08x \n", device); type = SOUND_TYPE_VOICE; break; } setCallVolume(mRilClient, type, int_volume); } } status_t AudioHardware::setMasterVolume(float volume) { ALOGV("Set master volume to %f.\n", volume); // We return an error code here to let the audioflinger do in-software // volume on top of the maximum volume that we set through the SND API. // return error - software mixer will handle it return -1; } static const int kDumpLockRetries = 50; static const int kDumpLockSleep = 20000; static bool tryLock(Mutex& mutex) { bool locked = false; for (int i = 0; i < kDumpLockRetries; ++i) { if (mutex.tryLock() == NO_ERROR) { locked = true; break; } usleep(kDumpLockSleep); } return locked; } status_t AudioHardware::dump(int fd, const Vector<String16>& args) { const size_t SIZE = 256; char buffer[SIZE]; String8 result; bool locked = tryLock(mLock); if (!locked) { snprintf(buffer, SIZE, "\n\tAudioHardware maybe deadlocked\n"); } else { mLock.unlock(); } snprintf(buffer, SIZE, "\tInit %s\n", (mInit) ? "OK" : "Failed"); result.append(buffer); snprintf(buffer, SIZE, "\tMic Mute %s\n", (mMicMute) ? "ON" : "OFF"); result.append(buffer); snprintf(buffer, SIZE, "\tmPcm: %p\n", mPcm); result.append(buffer); snprintf(buffer, SIZE, "\tmPcmOpenCnt: %d\n", mPcmOpenCnt); result.append(buffer); snprintf(buffer, SIZE, "\tmMixer: %p\n", mMixer); result.append(buffer); snprintf(buffer, SIZE, "\tmMixerOpenCnt: %d\n", mMixerOpenCnt); result.append(buffer); snprintf(buffer, SIZE, "\tIn Call Audio Mode %s\n", (mInCallAudioMode) ? "ON" : "OFF"); result.append(buffer); snprintf(buffer, SIZE, "\tInput source %d\n", mInputSource); result.append(buffer); snprintf(buffer, SIZE, "\tmSecRilLibHandle: %p\n", mSecRilLibHandle); result.append(buffer); snprintf(buffer, SIZE, "\tmRilClient: %p\n", mRilClient); result.append(buffer); snprintf(buffer, SIZE, "\tCP %s\n", (mActivatedCP) ? "Activated" : "Deactivated"); result.append(buffer); snprintf(buffer, SIZE, "\tmDriverOp: %d\n", mDriverOp); result.append(buffer); snprintf(buffer, SIZE, "\n\tmOutput %p dump:\n", mOutput.get()); result.append(buffer); write(fd, result.string(), result.size()); if (mOutput != 0) { mOutput->dump(fd, args); } snprintf(buffer, SIZE, "\n\t%d inputs opened:\n", mInputs.size()); write(fd, buffer, strlen(buffer)); for (size_t i = 0; i < mInputs.size(); i++) { snprintf(buffer, SIZE, "\t- input %d dump:\n", i); write(fd, buffer, strlen(buffer)); mInputs[i]->dump(fd, args); } return NO_ERROR; } status_t AudioHardware::setIncallPath_l(uint32_t device) { ALOGV("setIncallPath_l: device %x", device); // Setup sound path for CP clocking if ((mSecRilLibHandle) && (connectRILDIfRequired() == OK)) { if (mMode == AudioSystem::MODE_IN_CALL) { ALOGD("### incall mode route (%d)", device); AudioPath path; switch(device){ case AudioSystem::DEVICE_OUT_EARPIECE: ALOGD("### incall mode earpiece route"); path = SOUND_AUDIO_PATH_HANDSET; break; case AudioSystem::DEVICE_OUT_SPEAKER: ALOGD("### 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: ALOGD("### 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_HEADPHONE : ALOGD("### incall mode headphone route"); path = SOUND_AUDIO_PATH_HEADPHONE; break; case AudioSystem::DEVICE_OUT_WIRED_HEADSET : ALOGD("### incall mode headset route"); path = SOUND_AUDIO_PATH_HEADSET; break; default: ALOGW("### incall mode Error!! route = [%d]", device); path = SOUND_AUDIO_PATH_HANDSET; break; } setCallAudioPath(mRilClient, path); if (mMixer != NULL) { TRACE_DRIVER_IN(DRV_MIXER_GET) struct mixer_ctl *ctl= mixer_get_ctl_by_name(mMixer, "Voice Call Path"); TRACE_DRIVER_OUT ALOGE_IF(ctl == NULL, "setIncallPath_l() could not get mixer ctl"); if (ctl != NULL) { ALOGV("setIncallPath_l() Voice Call Path, (%x)", device); TRACE_DRIVER_IN(DRV_MIXER_SEL) mixer_ctl_set_enum_by_string(ctl, getVoiceRouteFromDevice(device)); TRACE_DRIVER_OUT } } } } return NO_ERROR; } struct pcm *AudioHardware::openPcmOut_l() { ALOGD("openPcmOut_l() mPcmOpenCnt: %d", mPcmOpenCnt); if (mPcmOpenCnt++ == 0) { if (mPcm != NULL) { ALOGE("openPcmOut_l() mPcmOpenCnt == 0 and mPcm == %p\n", mPcm); mPcmOpenCnt--; return NULL; } unsigned flags = PCM_OUT; struct pcm_config config = { channels : 2, rate : AUDIO_HW_OUT_SAMPLERATE, period_size : AUDIO_HW_OUT_PERIOD_SZ, period_count : AUDIO_HW_OUT_PERIOD_CNT, format : PCM_FORMAT_S16_LE, start_threshold : 0, stop_threshold : 0, silence_threshold : 0, avail_min : 0, }; TRACE_DRIVER_IN(DRV_PCM_OPEN) mPcm = pcm_open(0, 0, flags, &config); TRACE_DRIVER_OUT if (!pcm_is_ready(mPcm)) { ALOGE("openPcmOut_l() cannot open pcm_out driver: %s\n", pcm_get_error(mPcm)); TRACE_DRIVER_IN(DRV_PCM_CLOSE) pcm_close(mPcm); TRACE_DRIVER_OUT mPcmOpenCnt--; mPcm = NULL; } } return mPcm; } void AudioHardware::closePcmOut_l() { ALOGD("closePcmOut_l() mPcmOpenCnt: %d", mPcmOpenCnt); if (mPcmOpenCnt == 0) { ALOGE("closePcmOut_l() mPcmOpenCnt == 0"); return; } if (--mPcmOpenCnt == 0) { TRACE_DRIVER_IN(DRV_PCM_CLOSE) pcm_close(mPcm); TRACE_DRIVER_OUT mPcm = NULL; } } struct mixer *AudioHardware::openMixer_l() { ALOGV("openMixer_l() mMixerOpenCnt: %d", mMixerOpenCnt); if (mMixerOpenCnt++ == 0) { if (mMixer != NULL) { ALOGE("openMixer_l() mMixerOpenCnt == 0 and mMixer == %p\n", mMixer); mMixerOpenCnt--; return NULL; } TRACE_DRIVER_IN(DRV_MIXER_OPEN) mMixer = mixer_open(0); TRACE_DRIVER_OUT if (mMixer == NULL) { ALOGE("openMixer_l() cannot open mixer"); mMixerOpenCnt--; return NULL; } } return mMixer; } void AudioHardware::closeMixer_l() { ALOGV("closeMixer_l() mMixerOpenCnt: %d", mMixerOpenCnt); if (mMixerOpenCnt == 0) { ALOGE("closeMixer_l() mMixerOpenCnt == 0"); return; } if (--mMixerOpenCnt == 0) { TRACE_DRIVER_IN(DRV_MIXER_CLOSE) mixer_close(mMixer); TRACE_DRIVER_OUT mMixer = NULL; } } const char *AudioHardware::getOutputRouteFromDevice(uint32_t device) { switch (device) { case AudioSystem::DEVICE_OUT_EARPIECE: return "RCV"; case AudioSystem::DEVICE_OUT_SPEAKER: if (mMode == AudioSystem::MODE_RINGTONE) return "RING_SPK"; else return "SPK"; case AudioSystem::DEVICE_OUT_WIRED_HEADPHONE: if (mMode == AudioSystem::MODE_RINGTONE) return "RING_NO_MIC"; else return "HP_NO_MIC"; case AudioSystem::DEVICE_OUT_WIRED_HEADSET: if (mMode == AudioSystem::MODE_RINGTONE) return "RING_HP"; else return "HP"; case (AudioSystem::DEVICE_OUT_SPEAKER|AudioSystem::DEVICE_OUT_WIRED_HEADPHONE): case (AudioSystem::DEVICE_OUT_SPEAKER|AudioSystem::DEVICE_OUT_WIRED_HEADSET): if (mMode == AudioSystem::MODE_RINGTONE) return "RING_SPK_HP"; else return "SPK_HP"; case AudioSystem::DEVICE_OUT_BLUETOOTH_SCO: case AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET: case AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT: return "BT"; default: return "OFF"; } } const char *AudioHardware::getVoiceRouteFromDevice(uint32_t device) { switch (device) { case AudioSystem::DEVICE_OUT_EARPIECE: return "RCV"; case AudioSystem::DEVICE_OUT_SPEAKER: return "SPK"; case AudioSystem::DEVICE_OUT_WIRED_HEADPHONE: case AudioSystem::DEVICE_OUT_WIRED_HEADSET: switch (mTTYMode) { case TTY_MODE_VCO: return "TTY_VCO"; case TTY_MODE_HCO: return "TTY_HCO"; case TTY_MODE_FULL: return "TTY_FULL"; case TTY_MODE_OFF: default: if (device == AudioSystem::DEVICE_OUT_WIRED_HEADPHONE) { return "HP_NO_MIC"; } else { return "HP"; } } case AudioSystem::DEVICE_OUT_BLUETOOTH_SCO: case AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET: case AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT: return "BT"; default: return "OFF"; } } const char *AudioHardware::getInputRouteFromDevice(uint32_t device) { if (mMicMute) { return "MIC OFF"; } switch (device) { case AudioSystem::DEVICE_IN_BUILTIN_MIC: return "Main Mic"; case AudioSystem::DEVICE_IN_WIRED_HEADSET: return "Hands Free Mic"; case AudioSystem::DEVICE_IN_BLUETOOTH_SCO_HEADSET: return "BT Sco Mic"; default: return "MIC OFF"; } } uint32_t AudioHardware::getInputSampleRate(uint32_t sampleRate) { size_t i; uint32_t prevDelta; uint32_t delta; size_t size = sizeof(inputConfigTable)/sizeof(uint32_t)/INPUT_CONFIG_CNT; for (i = 0, prevDelta = 0xFFFFFFFF; i < size; i++, prevDelta = delta) { delta = abs(sampleRate - inputConfigTable[i][INPUT_CONFIG_SAMPLE_RATE]); if (delta > prevDelta) break; } // i is always > 0 here return inputConfigTable[i-1][INPUT_CONFIG_SAMPLE_RATE]; } // getActiveInput_l() must be called with mLock held sp <AudioHardware::AudioStreamInALSA> AudioHardware::getActiveInput_l() { sp< AudioHardware::AudioStreamInALSA> spIn; for (size_t i = 0; i < mInputs.size(); i++) { // return first input found not being in standby mode // as only one input can be in this state if (!mInputs[i]->checkStandby()) { spIn = mInputs[i]; break; } } return spIn; } status_t AudioHardware::setInputSource_l(audio_source source) { ALOGV("setInputSource_l(%d)", source); if (source != mInputSource) { if ((source == AUDIO_SOURCE_DEFAULT) || (mMode != AudioSystem::MODE_IN_CALL)) { if (mMixer) { TRACE_DRIVER_IN(DRV_MIXER_GET) struct mixer_ctl *ctl= mixer_get_ctl_by_name(mMixer, "Input Source"); TRACE_DRIVER_OUT if (ctl == NULL) { return NO_INIT; } const char* sourceName; switch (source) { case AUDIO_SOURCE_DEFAULT: // intended fall-through case AUDIO_SOURCE_MIC: // intended fall-through case AUDIO_SOURCE_VOICE_COMMUNICATION: sourceName = inputPathNameDefault; break; case AUDIO_SOURCE_CAMCORDER: sourceName = inputPathNameCamcorder; break; case AUDIO_SOURCE_VOICE_RECOGNITION: sourceName = inputPathNameVoiceRecognition; break; case AUDIO_SOURCE_VOICE_UPLINK: // intended fall-through case AUDIO_SOURCE_VOICE_DOWNLINK: // intended fall-through case AUDIO_SOURCE_VOICE_CALL: // intended fall-through default: return NO_INIT; } ALOGV("mixer_ctl_set_enum_by_string, Input Source, (%s)", sourceName); TRACE_DRIVER_IN(DRV_MIXER_SEL) mixer_ctl_set_enum_by_string(ctl, sourceName); TRACE_DRIVER_OUT } } mInputSource = source; } return NO_ERROR; } struct echo_reference_itfe *AudioHardware::getEchoReference(audio_format_t format, uint32_t channelCount, uint32_t samplingRate) { ALOGV("AudioHardware::getEchoReference %p", mEchoReference); releaseEchoReference(mEchoReference); if (mOutput != NULL) { uint32_t wrChannelCount = popcount(mOutput->channels()); uint32_t wrSampleRate = mOutput->sampleRate(); int status = create_echo_reference(AUDIO_FORMAT_PCM_16_BIT, channelCount, samplingRate, AUDIO_FORMAT_PCM_16_BIT, wrChannelCount, wrSampleRate, &mEchoReference); if (status == 0) { mOutput->addEchoReference(mEchoReference); } } return mEchoReference; } void AudioHardware::releaseEchoReference(struct echo_reference_itfe *reference) { ALOGV("AudioHardware::releaseEchoReference %p", mEchoReference); if (mEchoReference != NULL && reference == mEchoReference) { if (mOutput != NULL) { mOutput->removeEchoReference(reference); } release_echo_reference(mEchoReference); mEchoReference = NULL; } } //------------------------------------------------------------------------------ // AudioStreamOutALSA //------------------------------------------------------------------------------ AudioHardware::AudioStreamOutALSA::AudioStreamOutALSA() : mHardware(0), mPcm(0), mMixer(0), mRouteCtl(0), mStandby(true), mDevices(0), mChannels(AUDIO_HW_OUT_CHANNELS), mSampleRate(AUDIO_HW_OUT_SAMPLERATE), mBufferSize(AUDIO_HW_OUT_PERIOD_BYTES), mDriverOp(DRV_NONE), mStandbyCnt(0), mSleepReq(false), mEchoReference(NULL) { } status_t AudioHardware::AudioStreamOutALSA::set( AudioHardware* hw, uint32_t devices, int *pFormat, uint32_t *pChannels, uint32_t *pRate) { int lFormat = pFormat ? *pFormat : 0; uint32_t lChannels = pChannels ? *pChannels : 0; uint32_t lRate = pRate ? *pRate : 0; mHardware = hw; mDevices = devices; // fix up defaults if (lFormat == 0) lFormat = format(); if (lChannels == 0) lChannels = channels(); if (lRate == 0) lRate = sampleRate(); // check values if ((lFormat != format()) || (lChannels != channels()) || (lRate != sampleRate())) { if (pFormat) *pFormat = format(); if (pChannels) *pChannels = channels(); if (pRate) *pRate = sampleRate(); return BAD_VALUE; } if (pFormat) *pFormat = lFormat; if (pChannels) *pChannels = lChannels; if (pRate) *pRate = lRate; mChannels = lChannels; mSampleRate = lRate; mBufferSize = AUDIO_HW_OUT_PERIOD_BYTES; return NO_ERROR; } AudioHardware::AudioStreamOutALSA::~AudioStreamOutALSA() { standby(); } int AudioHardware::AudioStreamOutALSA::getPlaybackDelay(size_t frames, struct echo_reference_buffer *buffer) { size_t kernelFr; int rc = pcm_get_htimestamp(mPcm, &kernelFr, &buffer->time_stamp); if (rc < 0) { buffer->time_stamp.tv_sec = 0; buffer->time_stamp.tv_nsec = 0; buffer->delay_ns = 0; ALOGV("getPlaybackDelay(): pcm_get_htimestamp error, setting playbackTimestamp to 0"); return rc; } kernelFr = pcm_get_buffer_size(mPcm) - kernelFr; // adjust render time stamp with delay added by current driver buffer. // Add the duration of current frame as we want the render time of the last // sample being written. long delayNs = (long)(((int64_t)(kernelFr + frames)* 1000000000) /AUDIO_HW_OUT_SAMPLERATE); ALOGV("AudioStreamOutALSA::getPlaybackDelay delayNs: [%ld], "\ "kernelFr:[%d], frames:[%d], buffSize:[%d], time_stamp:[%ld].[%ld]", delayNs, (int)kernelFr, (int)frames, pcm_get_buffer_size(mPcm), (long)buffer->time_stamp.tv_sec, buffer->time_stamp.tv_nsec); buffer->delay_ns = delayNs; return 0; } ssize_t AudioHardware::AudioStreamOutALSA::write(const void* buffer, size_t bytes) { ALOGV("-----AudioStreamInALSA::write(%p, %d) START", buffer, (int)bytes); status_t status = NO_INIT; const uint8_t* p = static_cast<const uint8_t*>(buffer); int ret; if (mHardware == NULL) return NO_INIT; if (mSleepReq) { // 10ms are always shorter than the time to reconfigure the audio path // which is the only condition when mSleepReq would be true. usleep(10000); } { // scope for the lock AutoMutex lock(mLock); if (mStandby) { AutoMutex hwLock(mHardware->lock()); ALOGD("AudioHardware pcm playback is exiting standby."); sp<AudioStreamInALSA> spIn = mHardware->getActiveInput_l(); while (spIn != 0) { int cnt = spIn->prepareLock(); mHardware->lock().unlock(); // Mutex acquisition order is always out -> in -> hw spIn->lock(); mHardware->lock().lock(); // make sure that another thread did not change input state // while the mutex is released if ((spIn == mHardware->getActiveInput_l()) && (cnt == spIn->standbyCnt())) { ALOGV("AudioStreamOutALSA::write() force input standby"); spIn->close_l(); break; } spIn->unlock(); spIn = mHardware->getActiveInput_l(); } // spIn is not 0 here only if the input was active and has been // closed above // open output before input open_l(); if (spIn != 0) { if (spIn->open_l() != NO_ERROR) { spIn->doStandby_l(); } spIn->unlock(); } if (mPcm == NULL) { goto Error; } mStandby = false; } if (mEchoReference != NULL) { struct echo_reference_buffer b; b.raw = (void *)buffer; b.frame_count = bytes / frameSize(); getPlaybackDelay(bytes / frameSize(), &b); mEchoReference->write(mEchoReference, &b); } TRACE_DRIVER_IN(DRV_PCM_WRITE) ret = pcm_write(mPcm,(void*) p, bytes); TRACE_DRIVER_OUT if (ret == 0) { ALOGV("-----AudioStreamInALSA::write(%p, %d) END", buffer, (int)bytes); return bytes; } ALOGW("write error: %d", errno); status = -errno; } Error: standby(); // Simulate audio output timing in case of error usleep((((bytes * 1000) / frameSize()) * 1000) / sampleRate()); ALOGE("AudioStreamOutALSA::write END WITH ERROR !!!!!!!!!(%p, %u)", buffer, bytes); return status; } status_t AudioHardware::AudioStreamOutALSA::standby() { if (mHardware == NULL) return NO_INIT; mSleepReq = true; { AutoMutex lock(mLock); mSleepReq = false; { // scope for the AudioHardware lock AutoMutex hwLock(mHardware->lock()); doStandby_l(); } } return NO_ERROR; } void AudioHardware::AudioStreamOutALSA::doStandby_l() { mStandbyCnt++; if (!mStandby) { ALOGD("AudioHardware pcm playback is going to standby."); // stop echo reference capture if (mEchoReference != NULL) { mEchoReference->write(mEchoReference, NULL); } mStandby = true; } close_l(); } void AudioHardware::AudioStreamOutALSA::close_l() { if (mMixer) { mHardware->closeMixer_l(); mMixer = NULL; mRouteCtl = NULL; } if (mPcm) { mHardware->closePcmOut_l(); mPcm = NULL; } } status_t AudioHardware::AudioStreamOutALSA::open_l() { ALOGV("open pcm_out driver"); mPcm = mHardware->openPcmOut_l(); if (mPcm == NULL) { return NO_INIT; } mMixer = mHardware->openMixer_l(); if (mMixer) { ALOGV("open playback normal"); TRACE_DRIVER_IN(DRV_MIXER_GET) mRouteCtl = mixer_get_ctl_by_name(mMixer, "Playback Path"); TRACE_DRIVER_OUT } if (mHardware->mode() != AudioSystem::MODE_IN_CALL) { const char *route = mHardware->getOutputRouteFromDevice(mDevices); ALOGV("write() wakeup setting route %s", route); if (mRouteCtl) { TRACE_DRIVER_IN(DRV_MIXER_SEL) mixer_ctl_set_enum_by_string(mRouteCtl, route); TRACE_DRIVER_OUT } } return NO_ERROR; } status_t AudioHardware::AudioStreamOutALSA::dump(int fd, const Vector<String16>& args) { const size_t SIZE = 256; char buffer[SIZE]; String8 result; bool locked = tryLock(mLock); if (!locked) { snprintf(buffer, SIZE, "\n\t\tAudioStreamOutALSA maybe deadlocked\n"); } else { mLock.unlock(); } snprintf(buffer, SIZE, "\t\tmHardware: %p\n", mHardware); result.append(buffer); snprintf(buffer, SIZE, "\t\tmPcm: %p\n", mPcm); result.append(buffer); snprintf(buffer, SIZE, "\t\tmMixer: %p\n", mMixer); result.append(buffer); snprintf(buffer, SIZE, "\t\tmRouteCtl: %p\n", mRouteCtl); result.append(buffer); snprintf(buffer, SIZE, "\t\tStandby %s\n", (mStandby) ? "ON" : "OFF"); result.append(buffer); snprintf(buffer, SIZE, "\t\tmDevices: 0x%08x\n", mDevices); result.append(buffer); snprintf(buffer, SIZE, "\t\tmChannels: 0x%08x\n", mChannels); result.append(buffer); snprintf(buffer, SIZE, "\t\tmSampleRate: %d\n", mSampleRate); result.append(buffer); snprintf(buffer, SIZE, "\t\tmBufferSize: %d\n", mBufferSize); result.append(buffer); snprintf(buffer, SIZE, "\t\tmDriverOp: %d\n", mDriverOp); result.append(buffer); ::write(fd, result.string(), result.size()); return NO_ERROR; } bool AudioHardware::AudioStreamOutALSA::checkStandby() { return mStandby; } status_t AudioHardware::AudioStreamOutALSA::setParameters(const String8& keyValuePairs) { AudioParameter param = AudioParameter(keyValuePairs); status_t status = NO_ERROR; int device; ALOGD("AudioStreamOutALSA::setParameters() %s", keyValuePairs.string()); if (mHardware == NULL) return NO_INIT; mSleepReq = true; { AutoMutex lock(mLock); mSleepReq = false; if (param.getInt(String8(AudioParameter::keyRouting), device) == NO_ERROR) { if (device != 0) { AutoMutex hwLock(mHardware->lock()); if (mDevices != (uint32_t)device) { mDevices = (uint32_t)device; if (mHardware->mode() != AudioSystem::MODE_IN_CALL) { doStandby_l(); } } if (mHardware->mode() == AudioSystem::MODE_IN_CALL) { mHardware->setIncallPath_l(device); } } param.remove(String8(AudioParameter::keyRouting)); } } if (param.size()) { status = BAD_VALUE; } return status; } String8 AudioHardware::AudioStreamOutALSA::getParameters(const String8& keys) { AudioParameter param = AudioParameter(keys); String8 value; String8 key = String8(AudioParameter::keyRouting); if (param.get(key, value) == NO_ERROR) { param.addInt(key, (int)mDevices); } ALOGV("AudioStreamOutALSA::getParameters() %s", param.toString().string()); return param.toString(); } status_t AudioHardware::AudioStreamOutALSA::getRenderPosition(uint32_t *dspFrames) { //TODO return INVALID_OPERATION; } int AudioHardware::AudioStreamOutALSA::prepareLock() { // request sleep next time write() is called so that caller can acquire // mLock mSleepReq = true; return mStandbyCnt; } void AudioHardware::AudioStreamOutALSA::lock() { mLock.lock(); mSleepReq = false; } void AudioHardware::AudioStreamOutALSA::unlock() { mLock.unlock(); } void AudioHardware::AudioStreamOutALSA::addEchoReference(struct echo_reference_itfe *reference) { ALOGV("AudioStreamOutALSA::addEchoReference %p", mEchoReference); if (mEchoReference == NULL) { mEchoReference = reference; } } void AudioHardware::AudioStreamOutALSA::removeEchoReference(struct echo_reference_itfe *reference) { ALOGV("AudioStreamOutALSA::removeEchoReference %p", mEchoReference); if (mEchoReference == reference) { mEchoReference->write(mEchoReference, NULL); mEchoReference = NULL; } } //------------------------------------------------------------------------------ // AudioStreamInALSA //------------------------------------------------------------------------------ AudioHardware::AudioStreamInALSA::AudioStreamInALSA() : mHardware(0), mPcm(0), mMixer(0), mRouteCtl(0), mStandby(true), mDevices(0), mChannels(AUDIO_HW_IN_CHANNELS), mChannelCount(1), mSampleRate(AUDIO_HW_IN_SAMPLERATE), mBufferSize(AUDIO_HW_IN_PERIOD_BYTES), mDownSampler(NULL), mReadStatus(NO_ERROR), mInputBuf(NULL), mDriverOp(DRV_NONE), mStandbyCnt(0), mSleepReq(false), mProcBuf(NULL), mProcBufSize(0), mRefBuf(NULL), mRefBufSize(0), mEchoReference(NULL), mNeedEchoReference(false) { } status_t AudioHardware::AudioStreamInALSA::set( AudioHardware* hw, uint32_t devices, int *pFormat, uint32_t *pChannels, uint32_t *pRate, AudioSystem::audio_in_acoustics acoustics) { if (pFormat == 0 || *pFormat != AUDIO_HW_IN_FORMAT) { *pFormat = AUDIO_HW_IN_FORMAT; return BAD_VALUE; } if (pRate == 0) { return BAD_VALUE; } uint32_t rate = AudioHardware::getInputSampleRate(*pRate); if (rate != *pRate) { *pRate = rate; return BAD_VALUE; } if (pChannels == 0 || (*pChannels != AudioSystem::CHANNEL_IN_MONO && *pChannels != AudioSystem::CHANNEL_IN_STEREO)) { *pChannels = AUDIO_HW_IN_CHANNELS; return BAD_VALUE; } mHardware = hw; ALOGV("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) { mBufferProvider.mProvider.get_next_buffer = getNextBufferStatic; mBufferProvider.mProvider.release_buffer = releaseBufferStatic; mBufferProvider.mInputStream = this; int status = create_resampler(AUDIO_HW_OUT_SAMPLERATE, mSampleRate, mChannelCount, RESAMPLER_QUALITY_VOIP, &mBufferProvider.mProvider, &mDownSampler); if (status != 0) { ALOGW("AudioStreamInALSA::set() downsampler init failed: %d", status); mDownSampler = NULL; return status; } } mInputBuf = new int16_t[AUDIO_HW_IN_PERIOD_SZ * mChannelCount]; return NO_ERROR; } AudioHardware::AudioStreamInALSA::~AudioStreamInALSA() { standby(); if (mDownSampler != NULL) { release_resampler(mDownSampler); } delete[] mInputBuf; delete[] mProcBuf; } // readFrames() reads frames from kernel driver, down samples to capture rate if necessary // and output the number of frames requested to the buffer specified ssize_t AudioHardware::AudioStreamInALSA::readFrames(void* buffer, ssize_t frames) { ssize_t framesWr = 0; while (framesWr < frames) { size_t framesRd = frames - framesWr; if (mDownSampler != NULL) { mDownSampler->resample_from_provider(mDownSampler, (int16_t *)((char *)buffer + framesWr * frameSize()), &framesRd); } else { struct resampler_buffer buf = { { raw : NULL, }, frame_count : framesRd, }; getNextBuffer(&buf); if (buf.raw != NULL) { memcpy((char *)buffer + framesWr * frameSize(), buf.raw, buf.frame_count * frameSize()); framesRd = buf.frame_count; } releaseBuffer(&buf); } // mReadStatus is updated by getNextBuffer() also called by // mDownSampler->resample_from_provider() if (mReadStatus != 0) { return mReadStatus; } framesWr += framesRd; } return framesWr; } // processFrames() reads frames from kernel driver (via readFrames()), calls the active // audio pre processings and output the number of frames requested to the buffer specified ssize_t AudioHardware::AudioStreamInALSA::processFrames(void* buffer, ssize_t frames) { ssize_t framesWr = 0; while (framesWr < frames) { // first reload enough frames at the end of process input buffer if (mProcFramesIn < (size_t)frames) { // expand process input buffer if necessary if (mProcBufSize < (size_t)frames) { mProcBufSize = (size_t)frames; mProcBuf = (int16_t *)realloc(mProcBuf, mProcBufSize * mChannelCount * sizeof(int16_t)); ALOGV("processFrames(): mProcBuf %p size extended to %d frames", mProcBuf, mProcBufSize); } ssize_t framesRd = readFrames(mProcBuf + mProcFramesIn * mChannelCount, frames - mProcFramesIn); if (framesRd < 0) { framesWr = framesRd; break; } mProcFramesIn += framesRd; } if (mEchoReference != NULL) { pushEchoReference(mProcFramesIn); } //inBuf.frameCount and outBuf.frameCount indicate respectively the maximum number of frames //to be consumed and produced by process() audio_buffer_t inBuf = { mProcFramesIn, {mProcBuf} }; audio_buffer_t outBuf = { frames - framesWr, {(int16_t *)buffer + framesWr * mChannelCount} }; for (size_t i = 0; i < mPreprocessors.size(); i++) { (*mPreprocessors[i])->process(mPreprocessors[i], &inBuf, &outBuf); } // process() has updated the number of frames consumed and produced in // inBuf.frameCount and outBuf.frameCount respectively // move remaining frames to the beginning of mProcBuf mProcFramesIn -= inBuf.frameCount; if (mProcFramesIn) { memcpy(mProcBuf, mProcBuf + inBuf.frameCount * mChannelCount, mProcFramesIn * mChannelCount * sizeof(int16_t)); } // if not enough frames were passed to process(), read more and retry. if (outBuf.frameCount == 0) { continue; } framesWr += outBuf.frameCount; } return framesWr; } int32_t AudioHardware::AudioStreamInALSA::updateEchoReference(size_t frames) { struct echo_reference_buffer b; b.delay_ns = 0; ALOGV("updateEchoReference1 START, frames = [%d], mRefFramesIn = [%d], b.frame_count = [%d]", frames, mRefFramesIn, frames - mRefFramesIn); if (mRefFramesIn < frames) { if (mRefBufSize < frames) { mRefBufSize = frames; mRefBuf = (int16_t *)realloc(mRefBuf, mRefBufSize * mChannelCount * sizeof(int16_t)); } b.frame_count = frames - mRefFramesIn; b.raw = (void *)(mRefBuf + mRefFramesIn * mChannelCount); getCaptureDelay(frames, &b); if (mEchoReference->read(mEchoReference, &b) == NO_ERROR) { mRefFramesIn += b.frame_count; ALOGV("updateEchoReference2: mRefFramesIn:[%d], mRefBufSize:[%d], "\ "frames:[%d], b.frame_count:[%d]", mRefFramesIn, mRefBufSize,frames,b.frame_count); } }else{ ALOGV("updateEchoReference3: NOT enough frames to read ref buffer"); } return b.delay_ns; } void AudioHardware::AudioStreamInALSA::pushEchoReference(size_t frames) { // read frames from echo reference buffer and update echo delay // mRefFramesIn is updated with frames available in mRefBuf int32_t delayUs = (int32_t)(updateEchoReference(frames)/1000); if (mRefFramesIn < frames) { frames = mRefFramesIn; } audio_buffer_t refBuf = { frames, {mRefBuf} }; for (size_t i = 0; i < mPreprocessors.size(); i++) { if ((*mPreprocessors[i])->process_reverse == NULL) { continue; } (*mPreprocessors[i])->process_reverse(mPreprocessors[i], &refBuf, NULL); setPreProcessorEchoDelay(mPreprocessors[i], delayUs); } mRefFramesIn -= refBuf.frameCount; if (mRefFramesIn) { ALOGV("pushEchoReference5: shifting mRefBuf down by = %d frames", mRefFramesIn); memcpy(mRefBuf, mRefBuf + refBuf.frameCount * mChannelCount, mRefFramesIn * mChannelCount * sizeof(int16_t)); } } status_t AudioHardware::AudioStreamInALSA::setPreProcessorEchoDelay(effect_handle_t handle, int32_t delayUs) { uint32_t buf[sizeof(effect_param_t) / sizeof(uint32_t) + 2]; effect_param_t *param = (effect_param_t *)buf; param->psize = sizeof(uint32_t); param->vsize = sizeof(uint32_t); *(uint32_t *)param->data = AEC_PARAM_ECHO_DELAY; *((int32_t *)param->data + 1) = delayUs; ALOGV("setPreProcessorEchoDelay: %d us", delayUs); return setPreprocessorParam(handle, param); } status_t AudioHardware::AudioStreamInALSA::setPreprocessorParam(effect_handle_t handle, effect_param_t *param) { uint32_t size = sizeof(int); uint32_t psize = ((param->psize - 1) / sizeof(int) + 1) * sizeof(int) + param->vsize; status_t status = (*handle)->command(handle, EFFECT_CMD_SET_PARAM, sizeof (effect_param_t) + psize, param, &size, ¶m->status); if (status == NO_ERROR) { status = param->status; } return status; } void AudioHardware::AudioStreamInALSA::getCaptureDelay(size_t frames, struct echo_reference_buffer *buffer) { // read frames available in kernel driver buffer size_t kernelFr; struct timespec tstamp; if (pcm_get_htimestamp(mPcm, &kernelFr, &tstamp) < 0) { buffer->time_stamp.tv_sec = 0; buffer->time_stamp.tv_nsec = 0; buffer->delay_ns = 0; ALOGW("read getCaptureDelay(): pcm_htimestamp error"); return; } // read frames available in audio HAL input buffer // add number of frames being read as we want the capture time of first sample in current // buffer long bufDelay = (long)(((int64_t)(mInputFramesIn + mProcFramesIn) * 1000000000) / AUDIO_HW_IN_SAMPLERATE); // add delay introduced by resampler long rsmpDelay = 0; if (mDownSampler) { rsmpDelay = mDownSampler->delay_ns(mDownSampler); } long kernelDelay = (long)(((int64_t)kernelFr * 1000000000) / AUDIO_HW_IN_SAMPLERATE); // correct capture time stamp long delayNs = kernelDelay + bufDelay + rsmpDelay; buffer->time_stamp = tstamp; buffer->delay_ns = delayNs; ALOGV("AudioStreamInALSA::getCaptureDelay TimeStamp = [%ld].[%ld], delayCaptureNs: [%d],"\ " kernelDelay:[%ld], bufDelay:[%ld], rsmpDelay:[%ld], kernelFr:[%d], "\ "mInputFramesIn:[%d], mProcFramesIn:[%d], frames:[%d]", buffer->time_stamp.tv_sec , buffer->time_stamp.tv_nsec, buffer->delay_ns, kernelDelay, bufDelay, rsmpDelay, kernelFr, mInputFramesIn, mProcFramesIn, frames); } ssize_t AudioHardware::AudioStreamInALSA::read(void* buffer, ssize_t bytes) { ALOGV("-----AudioStreamInALSA::read(%p, %d) START", buffer, (int)bytes); status_t status = NO_INIT; if (mHardware == NULL) return NO_INIT; if (mSleepReq) { // 10ms are always shorter than the time to reconfigure the audio path // which is the only condition when mSleepReq would be true. usleep(10000); } { // scope for the lock AutoMutex lock(mLock); if (mStandby) { AutoMutex hwLock(mHardware->lock()); ALOGD("AudioHardware pcm capture is exiting standby."); sp<AudioStreamOutALSA> spOut = mHardware->output(); while (spOut != 0) { spOut->prepareLock(); mHardware->lock().unlock(); mLock.unlock(); // Mutex acquisition order is always out -> in -> hw spOut->lock(); mLock.lock(); mHardware->lock().lock(); // make sure that another thread did not change output state // while the mutex is released if (spOut == mHardware->output()) { break; } spOut->unlock(); spOut = mHardware->output(); } // open output before input if (spOut != 0) { if (!spOut->checkStandby()) { ALOGV("AudioStreamInALSA::read() force output standby"); spOut->close_l(); if (spOut->open_l() != NO_ERROR) { spOut->doStandby_l(); } } ALOGV("AudioStreamInALSA exit standby mNeedEchoReference %d mEchoReference %p", mNeedEchoReference, mEchoReference); if (mNeedEchoReference && mEchoReference == NULL) { mEchoReference = mHardware->getEchoReference(AUDIO_FORMAT_PCM_16_BIT, mChannelCount, mSampleRate); } spOut->unlock(); } open_l(); if (mPcm == NULL) { goto Error; } mStandby = false; } size_t framesRq = bytes / mChannelCount/sizeof(int16_t); ssize_t framesRd; if (mPreprocessors.size() == 0) { framesRd = readFrames(buffer, framesRq); } else { framesRd = processFrames(buffer, framesRq); } if (framesRd >= 0) { ALOGV("-----AudioStreamInALSA::read(%p, %d) END", buffer, (int)bytes); return framesRd * mChannelCount * sizeof(int16_t); } ALOGW("read error: %d", (int)framesRd); status = framesRd; } Error: standby(); // Simulate audio output timing in case of error usleep((((bytes * 1000) / frameSize()) * 1000) / sampleRate()); ALOGE("-----AudioStreamInALSA::read(%p, %d) END ERROR", buffer, (int)bytes); return status; } status_t AudioHardware::AudioStreamInALSA::standby() { if (mHardware == NULL) return NO_INIT; mSleepReq = true; { AutoMutex lock(mLock); mSleepReq = false; { // scope for AudioHardware lock AutoMutex hwLock(mHardware->lock()); doStandby_l(); } } return NO_ERROR; } void AudioHardware::AudioStreamInALSA::doStandby_l() { mStandbyCnt++; if (!mStandby) { ALOGD("AudioHardware pcm capture is going to standby."); if (mEchoReference != NULL) { // stop reading from echo reference mEchoReference->read(mEchoReference, NULL); // Mutex acquisition order is always out -> in -> hw sp<AudioStreamOutALSA> spOut = mHardware->output(); if (spOut != 0) { spOut->prepareLock(); mHardware->lock().unlock(); mLock.unlock(); spOut->lock(); mLock.lock(); mHardware->lock().lock(); mHardware->releaseEchoReference(mEchoReference); spOut->unlock(); } mEchoReference = NULL; } mStandby = true; } close_l(); } void AudioHardware::AudioStreamInALSA::close_l() { if (mMixer) { mHardware->closeMixer_l(); mMixer = NULL; mRouteCtl = NULL; } if (mPcm) { TRACE_DRIVER_IN(DRV_PCM_CLOSE) pcm_close(mPcm); TRACE_DRIVER_OUT mPcm = NULL; } delete[] mProcBuf; mProcBuf = NULL; mProcBufSize = 0; delete[] mRefBuf; mRefBuf = NULL; mRefBufSize = 0; } status_t AudioHardware::AudioStreamInALSA::open_l() { unsigned flags = PCM_IN; struct pcm_config config = { channels : mChannelCount, rate : AUDIO_HW_IN_SAMPLERATE, period_size : AUDIO_HW_IN_PERIOD_SZ, period_count : AUDIO_HW_IN_PERIOD_CNT, format : PCM_FORMAT_S16_LE, start_threshold : 0, stop_threshold : 0, silence_threshold : 0, avail_min : 0, }; ALOGV("open pcm_in driver"); TRACE_DRIVER_IN(DRV_PCM_OPEN) mPcm = pcm_open(0, 0, flags, &config); TRACE_DRIVER_OUT if (!pcm_is_ready(mPcm)) { ALOGE("cannot open pcm_in driver: %s\n", pcm_get_error(mPcm)); TRACE_DRIVER_IN(DRV_PCM_CLOSE) pcm_close(mPcm); TRACE_DRIVER_OUT mPcm = NULL; return NO_INIT; } if (mDownSampler != NULL) { mDownSampler->reset(mDownSampler); } mInputFramesIn = 0; mProcBufSize = 0; mProcFramesIn = 0; mRefBufSize = 0; mRefFramesIn = 0; mMixer = mHardware->openMixer_l(); if (mMixer) { TRACE_DRIVER_IN(DRV_MIXER_GET) mRouteCtl = mixer_get_ctl_by_name(mMixer, "Capture MIC Path"); TRACE_DRIVER_OUT } if (mHardware->mode() != AudioSystem::MODE_IN_CALL) { const char *route = mHardware->getInputRouteFromDevice(mDevices); ALOGV("read() wakeup setting route %s", route); if (mRouteCtl) { TRACE_DRIVER_IN(DRV_MIXER_SEL) mixer_ctl_set_enum_by_string(mRouteCtl, route); TRACE_DRIVER_OUT } } return NO_ERROR; } status_t AudioHardware::AudioStreamInALSA::dump(int fd, const Vector<String16>& args) { const size_t SIZE = 256; char buffer[SIZE]; String8 result; bool locked = tryLock(mLock); if (!locked) { snprintf(buffer, SIZE, "\n\t\tAudioStreamInALSA maybe deadlocked\n"); } else { mLock.unlock(); } snprintf(buffer, SIZE, "\t\tmHardware: %p\n", mHardware); result.append(buffer); snprintf(buffer, SIZE, "\t\tmPcm: %p\n", mPcm); result.append(buffer); snprintf(buffer, SIZE, "\t\tmMixer: %p\n", mMixer); result.append(buffer); snprintf(buffer, SIZE, "\t\tStandby %s\n", (mStandby) ? "ON" : "OFF"); result.append(buffer); snprintf(buffer, SIZE, "\t\tmDevices: 0x%08x\n", mDevices); result.append(buffer); snprintf(buffer, SIZE, "\t\tmChannels: 0x%08x\n", mChannels); result.append(buffer); snprintf(buffer, SIZE, "\t\tmSampleRate: %d\n", mSampleRate); result.append(buffer); snprintf(buffer, SIZE, "\t\tmBufferSize: %d\n", mBufferSize); result.append(buffer); snprintf(buffer, SIZE, "\t\tmDriverOp: %d\n", mDriverOp); result.append(buffer); write(fd, result.string(), result.size()); return NO_ERROR; } bool AudioHardware::AudioStreamInALSA::checkStandby() { return mStandby; } status_t AudioHardware::AudioStreamInALSA::setParameters(const String8& keyValuePairs) { AudioParameter param = AudioParameter(keyValuePairs); status_t status = NO_ERROR; int value; ALOGD("AudioStreamInALSA::setParameters() %s", keyValuePairs.string()); if (mHardware == NULL) return NO_INIT; mSleepReq = true; { AutoMutex lock(mLock); mSleepReq = false; if (param.getInt(String8(AudioParameter::keyInputSource), value) == NO_ERROR) { AutoMutex hwLock(mHardware->lock()); mHardware->openMixer_l(); mHardware->setInputSource_l((audio_source)value); mHardware->closeMixer_l(); param.remove(String8(AudioParameter::keyInputSource)); } if (param.getInt(String8(AudioParameter::keyRouting), value) == NO_ERROR) { if (value != 0) { AutoMutex hwLock(mHardware->lock()); if (mDevices != (uint32_t)value) { mDevices = (uint32_t)value; if (mHardware->mode() != AudioSystem::MODE_IN_CALL) { doStandby_l(); } } } param.remove(String8(AudioParameter::keyRouting)); } } if (param.size()) { status = BAD_VALUE; } return status; } String8 AudioHardware::AudioStreamInALSA::getParameters(const String8& keys) { AudioParameter param = AudioParameter(keys); String8 value; String8 key = String8(AudioParameter::keyRouting); if (param.get(key, value) == NO_ERROR) { param.addInt(key, (int)mDevices); } ALOGV("AudioStreamInALSA::getParameters() %s", param.toString().string()); return param.toString(); } status_t AudioHardware::AudioStreamInALSA::addAudioEffect(effect_handle_t effect) { ALOGV("AudioStreamInALSA::addAudioEffect() %p", effect); effect_descriptor_t desc; status_t status = (*effect)->get_descriptor(effect, &desc); if (status == 0) { if (memcmp(&desc.type, FX_IID_AEC, sizeof(effect_uuid_t)) == 0) { ALOGV("AudioStreamInALSA::addAudioEffect() mNeedEchoReference true"); mNeedEchoReference = true; standby(); } ALOGV("AudioStreamInALSA::addAudioEffect() name %s", desc.name); } else { ALOGV("AudioStreamInALSA::addAudioEffect() get_descriptor() error"); } AutoMutex lock(mLock); mPreprocessors.add(effect); return NO_ERROR; } status_t AudioHardware::AudioStreamInALSA::removeAudioEffect(effect_handle_t effect) { status_t status = INVALID_OPERATION; ALOGV("AudioStreamInALSA::removeAudioEffect() %p", effect); { AutoMutex lock(mLock); for (size_t i = 0; i < mPreprocessors.size(); i++) { if (mPreprocessors[i] == effect) { mPreprocessors.removeAt(i); status = NO_ERROR; break; } } } if (status == NO_ERROR) { effect_descriptor_t desc; if ((*effect)->get_descriptor(effect, &desc) == 0) { if (memcmp(&desc.type, FX_IID_AEC, sizeof(effect_uuid_t)) == 0) { ALOGV("AudioStreamInALSA::removeAudioEffect() mNeedEchoReference false"); mNeedEchoReference = false; standby(); } } } return status; } extern "C" { int AudioHardware::AudioStreamInALSA::getNextBufferStatic( struct resampler_buffer_provider *provider, struct resampler_buffer* buffer) { ResamplerBufferProvider *bufferProvider = (ResamplerBufferProvider *)provider; return bufferProvider->mInputStream->getNextBuffer(buffer); } void AudioHardware::AudioStreamInALSA::releaseBufferStatic( struct resampler_buffer_provider *provider, struct resampler_buffer* buffer) { ResamplerBufferProvider *bufferProvider = (ResamplerBufferProvider *)provider; return bufferProvider->mInputStream->releaseBuffer(buffer); } }; // extern "C" status_t AudioHardware::AudioStreamInALSA::getNextBuffer(struct resampler_buffer *buffer) { if (mPcm == NULL) { buffer->raw = NULL; buffer->frame_count = 0; mReadStatus = NO_INIT; return NO_INIT; } if (mInputFramesIn == 0) { TRACE_DRIVER_IN(DRV_PCM_READ) mReadStatus = pcm_read(mPcm,(void*) mInputBuf, AUDIO_HW_IN_PERIOD_SZ * frameSize()); TRACE_DRIVER_OUT if (mReadStatus != 0) { buffer->raw = NULL; buffer->frame_count = 0; return mReadStatus; } mInputFramesIn = AUDIO_HW_IN_PERIOD_SZ; } buffer->frame_count = (buffer->frame_count > mInputFramesIn) ? mInputFramesIn:buffer->frame_count; buffer->i16 = mInputBuf + (AUDIO_HW_IN_PERIOD_SZ - mInputFramesIn) * mChannelCount; return mReadStatus; } void AudioHardware::AudioStreamInALSA::releaseBuffer(struct resampler_buffer *buffer) { mInputFramesIn -= buffer->frame_count; } size_t AudioHardware::AudioStreamInALSA::getBufferSize(uint32_t sampleRate, int channelCount) { size_t i; size_t size = sizeof(inputConfigTable)/sizeof(uint32_t)/INPUT_CONFIG_CNT; for (i = 0; i < size; i++) { if (sampleRate == inputConfigTable[i][INPUT_CONFIG_SAMPLE_RATE]) { return (AUDIO_HW_IN_PERIOD_SZ*channelCount*sizeof(int16_t)) / inputConfigTable[i][INPUT_CONFIG_BUFFER_RATIO]; } } // this should never happen as getBufferSize() is always called after getInputSampleRate() // that checks for valid sampling rates. ALOGE("AudioStreamInALSA::getBufferSize() invalid sampling rate %d", sampleRate); return 0; } int AudioHardware::AudioStreamInALSA::prepareLock() { // request sleep next time read() is called so that caller can acquire // mLock mSleepReq = true; return mStandbyCnt; } void AudioHardware::AudioStreamInALSA::lock() { mLock.lock(); mSleepReq = false; } void AudioHardware::AudioStreamInALSA::unlock() { mLock.unlock(); } //------------------------------------------------------------------------------ // Factory //------------------------------------------------------------------------------ extern "C" AudioHardwareInterface* createAudioHardware(void) { return new AudioHardware(); } }; // namespace android