| /* |
| ** 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 |