| /* |
| ** Copyright 2008, The Android Open-Source Project |
| ** |
| ** Licensed under the Apache License, Version 2.0 (the "License"); |
| ** you may not use this file except in compliance with the License. |
| ** You may obtain a copy of the License at |
| ** |
| ** http://www.apache.org/licenses/LICENSE-2.0 |
| ** |
| ** Unless required by applicable law or agreed to in writing, software |
| ** distributed under the License is distributed on an "AS IS" BASIS, |
| ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| ** See the License for the specific language governing permissions and |
| ** limitations under the License. |
| */ |
| |
| #include <math.h> |
| |
| //#define LOG_NDEBUG 0 |
| #define LOG_TAG "AudioHardwareMSM72XX" |
| #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 <dlfcn.h> |
| #include <fcntl.h> |
| |
| // hardware specific functions |
| |
| #include "AudioHardware.h" |
| #include <media/AudioRecord.h> |
| |
| #define LOG_SND_RPC 0 // Set to 1 to log sound RPC's |
| |
| namespace android { |
| static int audpre_index, tx_iir_index; |
| static void * acoustic; |
| const uint32_t AudioHardware::inputSamplingRates[] = { |
| 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000 |
| }; |
| // ---------------------------------------------------------------------------- |
| |
| AudioHardware::AudioHardware() : |
| mInit(false), mMicMute(true), mBluetoothNrec(true), mBluetoothId(0), |
| mOutput(0), mSndEndpoints(NULL), mCurSndDevice(-1), |
| SND_DEVICE_CURRENT(-1), |
| SND_DEVICE_HANDSET(-1), |
| SND_DEVICE_SPEAKER(-1), |
| SND_DEVICE_HEADSET(-1), |
| SND_DEVICE_BT(-1), |
| SND_DEVICE_CARKIT(-1), |
| SND_DEVICE_TTY_FULL(-1), |
| SND_DEVICE_TTY_VCO(-1), |
| SND_DEVICE_TTY_HCO(-1), |
| SND_DEVICE_NO_MIC_HEADSET(-1), |
| SND_DEVICE_FM_HEADSET(-1), |
| SND_DEVICE_HEADSET_AND_SPEAKER(-1), |
| SND_DEVICE_FM_SPEAKER(-1), |
| SND_DEVICE_BT_EC_OFF(-1) |
| { |
| |
| int (*snd_get_num)(); |
| int (*snd_get_endpoint)(int, msm_snd_endpoint *); |
| int (*set_acoustic_parameters)(); |
| |
| struct msm_snd_endpoint *ept; |
| |
| acoustic = ::dlopen("/system/lib/libhtc_acoustic.so", RTLD_NOW); |
| if (acoustic == NULL ) { |
| ALOGE("Could not open libhtc_acoustic.so"); |
| /* this is not really an error on non-htc devices... */ |
| mNumSndEndpoints = 0; |
| mInit = true; |
| return; |
| } |
| |
| set_acoustic_parameters = (int (*)(void))::dlsym(acoustic, "set_acoustic_parameters"); |
| if ((*set_acoustic_parameters) == 0 ) { |
| ALOGE("Could not open set_acoustic_parameters()"); |
| return; |
| } |
| |
| int rc = set_acoustic_parameters(); |
| if (rc < 0) { |
| ALOGE("Could not set acoustic parameters to share memory: %d", rc); |
| // return; |
| } |
| |
| snd_get_num = (int (*)(void))::dlsym(acoustic, "snd_get_num_endpoints"); |
| if ((*snd_get_num) == 0 ) { |
| ALOGE("Could not open snd_get_num()"); |
| // return; |
| } |
| |
| mNumSndEndpoints = snd_get_num(); |
| ALOGD("mNumSndEndpoints = %d", mNumSndEndpoints); |
| mSndEndpoints = new msm_snd_endpoint[mNumSndEndpoints]; |
| mInit = true; |
| ALOGV("constructed %d SND endpoints)", mNumSndEndpoints); |
| ept = mSndEndpoints; |
| snd_get_endpoint = (int (*)(int, msm_snd_endpoint *))::dlsym(acoustic, "snd_get_endpoint"); |
| if ((*snd_get_endpoint) == 0 ) { |
| ALOGE("Could not open snd_get_endpoint()"); |
| return; |
| } |
| |
| for (int cnt = 0; cnt < mNumSndEndpoints; cnt++, ept++) { |
| ept->id = cnt; |
| snd_get_endpoint(cnt, ept); |
| #define CHECK_FOR(desc) \ |
| if (!strcmp(ept->name, #desc)) { \ |
| SND_DEVICE_##desc = ept->id; \ |
| ALOGD("BT MATCH " #desc); \ |
| } else |
| CHECK_FOR(CURRENT) |
| CHECK_FOR(HANDSET) |
| CHECK_FOR(SPEAKER) |
| CHECK_FOR(BT) |
| CHECK_FOR(BT_EC_OFF) |
| CHECK_FOR(HEADSET) |
| CHECK_FOR(CARKIT) |
| CHECK_FOR(TTY_FULL) |
| CHECK_FOR(TTY_VCO) |
| CHECK_FOR(TTY_HCO) |
| CHECK_FOR(NO_MIC_HEADSET) |
| CHECK_FOR(FM_HEADSET) |
| CHECK_FOR(FM_SPEAKER) |
| CHECK_FOR(HEADSET_AND_SPEAKER) {} |
| #undef CHECK_FOR |
| } |
| } |
| |
| AudioHardware::~AudioHardware() |
| { |
| for (size_t index = 0; index < mInputs.size(); index++) { |
| closeInputStream((AudioStreamIn*)mInputs[index]); |
| } |
| mInputs.clear(); |
| closeOutputStream((AudioStreamOut*)mOutput); |
| delete [] mSndEndpoints; |
| if (acoustic) { |
| ::dlclose(acoustic); |
| acoustic = 0; |
| } |
| mInit = false; |
| } |
| |
| status_t AudioHardware::initCheck() |
| { |
| return mInit ? NO_ERROR : NO_INIT; |
| } |
| |
| AudioStreamOut* AudioHardware::openOutputStream( |
| uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status) |
| { |
| { // scope for the lock |
| Mutex::Autolock lock(mLock); |
| |
| // only one output stream allowed |
| if (mOutput) { |
| if (status) { |
| *status = INVALID_OPERATION; |
| } |
| return 0; |
| } |
| |
| // create new output stream |
| AudioStreamOutMSM72xx* out = new AudioStreamOutMSM72xx(); |
| status_t lStatus = out->set(this, devices, format, channels, sampleRate); |
| if (status) { |
| *status = lStatus; |
| } |
| if (lStatus == NO_ERROR) { |
| mOutput = out; |
| } else { |
| delete out; |
| } |
| } |
| return mOutput; |
| } |
| |
| void AudioHardware::closeOutputStream(AudioStreamOut* out) { |
| Mutex::Autolock lock(mLock); |
| if (mOutput == 0 || mOutput != out) { |
| ALOGW("Attempt to close invalid output stream"); |
| } |
| else { |
| delete mOutput; |
| mOutput = 0; |
| } |
| } |
| |
| 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)) { |
| return 0; |
| } |
| |
| mLock.lock(); |
| |
| AudioStreamInMSM72xx* in = new AudioStreamInMSM72xx(); |
| status_t lStatus = in->set(this, devices, format, channels, sampleRate, acoustic_flags); |
| if (status) { |
| *status = lStatus; |
| } |
| if (lStatus != NO_ERROR) { |
| mLock.unlock(); |
| delete in; |
| return 0; |
| } |
| |
| mInputs.add(in); |
| mLock.unlock(); |
| |
| return in; |
| } |
| |
| void AudioHardware::closeInputStream(AudioStreamIn* in) { |
| Mutex::Autolock lock(mLock); |
| |
| ssize_t index = mInputs.indexOf((AudioStreamInMSM72xx *)in); |
| if (index < 0) { |
| ALOGW("Attempt to close invalid input stream"); |
| } else { |
| mLock.unlock(); |
| delete mInputs[index]; |
| mLock.lock(); |
| mInputs.removeAt(index); |
| } |
| } |
| |
| status_t AudioHardware::setMode(int mode) |
| { |
| status_t status = AudioHardwareBase::setMode(mode); |
| if (status == NO_ERROR) { |
| // make sure that doAudioRouteOrMute() is called by doRouting() |
| // even if the new device selected is the same as current one. |
| clearCurDevice(); |
| } |
| return status; |
| } |
| |
| bool AudioHardware::checkOutputStandby() |
| { |
| if (mOutput) |
| if (!mOutput->checkStandby()) |
| return false; |
| |
| return true; |
| } |
| |
| status_t AudioHardware::setMicMute(bool state) |
| { |
| Mutex::Autolock lock(mLock); |
| return setMicMute_nosync(state); |
| } |
| |
| // always call with mutex held |
| status_t AudioHardware::setMicMute_nosync(bool state) |
| { |
| if (mMicMute != state) { |
| mMicMute = state; |
| return doAudioRouteOrMute(SND_DEVICE_CURRENT); |
| } |
| 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_NAME_KEY[] = "bt_headset_name"; |
| const char BT_NREC_VALUE_ON[] = "on"; |
| |
| |
| ALOGV("setParameters() %s", keyValuePairs.string()); |
| |
| if (keyValuePairs.length() == 0) return BAD_VALUE; |
| |
| key = String8(BT_NREC_KEY); |
| if (param.get(key, value) == NO_ERROR) { |
| if (value == BT_NREC_VALUE_ON) { |
| mBluetoothNrec = true; |
| } else { |
| mBluetoothNrec = false; |
| ALOGI("Turning noise reduction and echo cancellation off for BT " |
| "headset"); |
| } |
| } |
| key = String8(BT_NAME_KEY); |
| if (param.get(key, value) == NO_ERROR) { |
| mBluetoothId = 0; |
| for (int i = 0; i < mNumSndEndpoints; i++) { |
| if (!strcasecmp(value.string(), mSndEndpoints[i].name)) { |
| mBluetoothId = mSndEndpoints[i].id; |
| ALOGI("Using custom acoustic parameters for %s", value.string()); |
| break; |
| } |
| } |
| if (mBluetoothId == 0) { |
| ALOGI("Using default acoustic parameters " |
| "(%s not in acoustic database)", value.string()); |
| doRouting(); |
| } |
| } |
| return NO_ERROR; |
| } |
| |
| String8 AudioHardware::getParameters(const String8& keys) |
| { |
| AudioParameter param = AudioParameter(keys); |
| return param.toString(); |
| } |
| |
| |
| static unsigned calculate_audpre_table_index(unsigned index) |
| { |
| switch (index) { |
| case 48000: return SAMP_RATE_INDX_48000; |
| case 44100: return SAMP_RATE_INDX_44100; |
| case 32000: return SAMP_RATE_INDX_32000; |
| case 24000: return SAMP_RATE_INDX_24000; |
| case 22050: return SAMP_RATE_INDX_22050; |
| case 16000: return SAMP_RATE_INDX_16000; |
| case 12000: return SAMP_RATE_INDX_12000; |
| case 11025: return SAMP_RATE_INDX_11025; |
| case 8000: return SAMP_RATE_INDX_8000; |
| default: return -1; |
| } |
| } |
| 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; |
| } |
| |
| return 2048*channelCount; |
| } |
| |
| static status_t set_volume_rpc(uint32_t device, |
| uint32_t method, |
| uint32_t volume) |
| { |
| int fd; |
| #if LOG_SND_RPC |
| ALOGD("rpc_snd_set_volume(%d, %d, %d)\n", device, method, volume); |
| #endif |
| |
| if (device == -1UL) return NO_ERROR; |
| |
| fd = open("/dev/msm_snd", O_RDWR); |
| if (fd < 0) { |
| ALOGE("Can not open snd device"); |
| return -EPERM; |
| } |
| /* rpc_snd_set_volume( |
| * device, # Any hardware device enum, including |
| * # SND_DEVICE_CURRENT |
| * method, # must be SND_METHOD_VOICE to do anything useful |
| * volume, # integer volume level, in range [0,5]. |
| * # note that 0 is audible (not quite muted) |
| * ) |
| * rpc_snd_set_volume only works for in-call sound volume. |
| */ |
| struct msm_snd_volume_config args; |
| args.device = device; |
| args.method = method; |
| args.volume = volume; |
| |
| if (ioctl(fd, SND_SET_VOLUME, &args) < 0) { |
| ALOGE("snd_set_volume error."); |
| close(fd); |
| return -EIO; |
| } |
| close(fd); |
| return NO_ERROR; |
| } |
| |
| status_t AudioHardware::setVoiceVolume(float v) |
| { |
| if (v < 0.0) { |
| ALOGW("setVoiceVolume(%f) under 0.0, assuming 0.0\n", v); |
| v = 0.0; |
| } else if (v > 1.0) { |
| ALOGW("setVoiceVolume(%f) over 1.0, assuming 1.0\n", v); |
| v = 1.0; |
| } |
| |
| int vol = lrint(v * 5.0); |
| ALOGD("setVoiceVolume(%f)\n", v); |
| ALOGI("Setting in-call volume to %d (available range is 0 to 5)\n", vol); |
| |
| Mutex::Autolock lock(mLock); |
| set_volume_rpc(SND_DEVICE_CURRENT, SND_METHOD_VOICE, vol); |
| return NO_ERROR; |
| } |
| |
| status_t AudioHardware::setMasterVolume(float v) |
| { |
| Mutex::Autolock lock(mLock); |
| int vol = ceil(v * 5.0); |
| ALOGI("Set master volume to %d.\n", vol); |
| /* |
| set_volume_rpc(SND_DEVICE_HANDSET, SND_METHOD_VOICE, vol); |
| set_volume_rpc(SND_DEVICE_SPEAKER, SND_METHOD_VOICE, vol); |
| set_volume_rpc(SND_DEVICE_BT, SND_METHOD_VOICE, vol); |
| set_volume_rpc(SND_DEVICE_HEADSET, SND_METHOD_VOICE, vol); |
| */ |
| // 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 status_t do_route_audio_rpc(uint32_t device, |
| bool ear_mute, bool mic_mute) |
| { |
| if (device == -1UL) |
| return NO_ERROR; |
| |
| int fd; |
| #if LOG_SND_RPC |
| ALOGD("rpc_snd_set_device(%d, %d, %d)\n", device, ear_mute, mic_mute); |
| #endif |
| |
| fd = open("/dev/msm_snd", O_RDWR); |
| if (fd < 0) { |
| ALOGE("Can not open snd device"); |
| return -EPERM; |
| } |
| // RPC call to switch audio path |
| /* rpc_snd_set_device( |
| * device, # Hardware device enum to use |
| * ear_mute, # Set mute for outgoing voice audio |
| * # this should only be unmuted when in-call |
| * mic_mute, # Set mute for incoming voice audio |
| * # this should only be unmuted when in-call or |
| * # recording. |
| * ) |
| */ |
| struct msm_snd_device_config args; |
| args.device = device; |
| args.ear_mute = ear_mute ? SND_MUTE_MUTED : SND_MUTE_UNMUTED; |
| args.mic_mute = mic_mute ? SND_MUTE_MUTED : SND_MUTE_UNMUTED; |
| |
| if (ioctl(fd, SND_SET_DEVICE, &args) < 0) { |
| ALOGE("snd_set_device error."); |
| close(fd); |
| return -EIO; |
| } |
| |
| close(fd); |
| return NO_ERROR; |
| } |
| |
| // always call with mutex held |
| status_t AudioHardware::doAudioRouteOrMute(uint32_t device) |
| { |
| if (device == (uint32_t)SND_DEVICE_BT || device == (uint32_t)SND_DEVICE_CARKIT) { |
| if (mBluetoothId) { |
| device = mBluetoothId; |
| } else if (!mBluetoothNrec) { |
| device = SND_DEVICE_BT_EC_OFF; |
| } |
| } |
| ALOGV("doAudioRouteOrMute() device %x, mMode %d, mMicMute %d", device, mMode, mMicMute); |
| return do_route_audio_rpc(device, |
| mMode != AudioSystem::MODE_IN_CALL, mMicMute); |
| } |
| |
| status_t AudioHardware::doRouting() |
| { |
| /* currently this code doesn't work without the htc libacoustic */ |
| if (!acoustic) |
| return 0; |
| |
| Mutex::Autolock lock(mLock); |
| uint32_t outputDevices = mOutput->devices(); |
| status_t ret = NO_ERROR; |
| int (*msm72xx_enable_audpp)(int); |
| msm72xx_enable_audpp = (int (*)(int))::dlsym(acoustic, "msm72xx_enable_audpp"); |
| int audProcess = (ADRC_DISABLE | EQ_DISABLE | RX_IIR_DISABLE); |
| AudioStreamInMSM72xx *input = getActiveInput_l(); |
| uint32_t inputDevice = (input == NULL) ? 0 : input->devices(); |
| int sndDevice = -1; |
| |
| if (inputDevice != 0) { |
| ALOGI("do input routing device %x\n", inputDevice); |
| if (inputDevice & AudioSystem::DEVICE_IN_BLUETOOTH_SCO_HEADSET) { |
| ALOGI("Routing audio to Bluetooth PCM\n"); |
| sndDevice = SND_DEVICE_BT; |
| } else if (inputDevice & AudioSystem::DEVICE_IN_WIRED_HEADSET) { |
| if ((outputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET) && |
| (outputDevices & AudioSystem::DEVICE_OUT_SPEAKER)) { |
| ALOGI("Routing audio to Wired Headset and Speaker\n"); |
| sndDevice = SND_DEVICE_HEADSET_AND_SPEAKER; |
| audProcess = (ADRC_ENABLE | EQ_ENABLE | RX_IIR_ENABLE); |
| } else { |
| ALOGI("Routing audio to Wired Headset\n"); |
| sndDevice = SND_DEVICE_HEADSET; |
| } |
| } else { |
| if (outputDevices & AudioSystem::DEVICE_OUT_SPEAKER) { |
| ALOGI("Routing audio to Speakerphone\n"); |
| sndDevice = SND_DEVICE_SPEAKER; |
| audProcess = (ADRC_ENABLE | EQ_ENABLE | RX_IIR_ENABLE); |
| } else { |
| ALOGI("Routing audio to Handset\n"); |
| sndDevice = SND_DEVICE_HANDSET; |
| } |
| } |
| } |
| // if inputDevice == 0, restore output routing |
| |
| if (sndDevice == -1) { |
| if (outputDevices & (outputDevices - 1)) { |
| if ((outputDevices & AudioSystem::DEVICE_OUT_SPEAKER) == 0) { |
| ALOGW("Hardware does not support requested route combination (%#X)," |
| " picking closest possible route...", outputDevices); |
| } |
| } |
| |
| if (outputDevices & |
| (AudioSystem::DEVICE_OUT_BLUETOOTH_SCO | AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET)) { |
| ALOGI("Routing audio to Bluetooth PCM\n"); |
| sndDevice = SND_DEVICE_BT; |
| } else if (outputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT) { |
| ALOGI("Routing audio to Bluetooth PCM\n"); |
| sndDevice = SND_DEVICE_CARKIT; |
| } else if ((outputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET) && |
| (outputDevices & AudioSystem::DEVICE_OUT_SPEAKER)) { |
| ALOGI("Routing audio to Wired Headset and Speaker\n"); |
| sndDevice = SND_DEVICE_HEADSET_AND_SPEAKER; |
| audProcess = (ADRC_ENABLE | EQ_ENABLE | RX_IIR_ENABLE); |
| } else if (outputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADPHONE) { |
| if (outputDevices & AudioSystem::DEVICE_OUT_SPEAKER) { |
| ALOGI("Routing audio to No microphone Wired Headset and Speaker (%d,%x)\n", mMode, outputDevices); |
| sndDevice = SND_DEVICE_HEADSET_AND_SPEAKER; |
| audProcess = (ADRC_ENABLE | EQ_ENABLE | RX_IIR_ENABLE); |
| } else { |
| ALOGI("Routing audio to No microphone Wired Headset (%d,%x)\n", mMode, outputDevices); |
| sndDevice = SND_DEVICE_NO_MIC_HEADSET; |
| } |
| } else if (outputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET) { |
| ALOGI("Routing audio to Wired Headset\n"); |
| sndDevice = SND_DEVICE_HEADSET; |
| } else if (outputDevices & AudioSystem::DEVICE_OUT_SPEAKER) { |
| ALOGI("Routing audio to Speakerphone\n"); |
| sndDevice = SND_DEVICE_SPEAKER; |
| audProcess = (ADRC_ENABLE | EQ_ENABLE | RX_IIR_ENABLE); |
| } else { |
| ALOGI("Routing audio to Handset\n"); |
| sndDevice = SND_DEVICE_HANDSET; |
| } |
| } |
| |
| if (sndDevice != -1 && sndDevice != mCurSndDevice) { |
| ret = doAudioRouteOrMute(sndDevice); |
| if ((*msm72xx_enable_audpp) == 0 ) { |
| ALOGE("Could not open msm72xx_enable_audpp()"); |
| } else { |
| msm72xx_enable_audpp(audProcess); |
| } |
| mCurSndDevice = sndDevice; |
| } |
| |
| return ret; |
| } |
| |
| status_t AudioHardware::checkMicMute() |
| { |
| Mutex::Autolock lock(mLock); |
| if (mMode != AudioSystem::MODE_IN_CALL) { |
| setMicMute_nosync(true); |
| } |
| |
| return NO_ERROR; |
| } |
| |
| status_t AudioHardware::dumpInternals(int fd, const Vector<String16>& args) |
| { |
| const size_t SIZE = 256; |
| char buffer[SIZE]; |
| String8 result; |
| result.append("AudioHardware::dumpInternals\n"); |
| snprintf(buffer, SIZE, "\tmInit: %s\n", mInit? "true": "false"); |
| result.append(buffer); |
| snprintf(buffer, SIZE, "\tmMicMute: %s\n", mMicMute? "true": "false"); |
| result.append(buffer); |
| snprintf(buffer, SIZE, "\tmBluetoothNrec: %s\n", mBluetoothNrec? "true": "false"); |
| result.append(buffer); |
| snprintf(buffer, SIZE, "\tmBluetoothId: %d\n", mBluetoothId); |
| result.append(buffer); |
| ::write(fd, result.string(), result.size()); |
| return NO_ERROR; |
| } |
| |
| status_t AudioHardware::dump(int fd, const Vector<String16>& args) |
| { |
| dumpInternals(fd, args); |
| for (size_t index = 0; index < mInputs.size(); index++) { |
| mInputs[index]->dump(fd, args); |
| } |
| |
| if (mOutput) { |
| mOutput->dump(fd, args); |
| } |
| return NO_ERROR; |
| } |
| |
| uint32_t AudioHardware::getInputSampleRate(uint32_t sampleRate) |
| { |
| uint32_t i; |
| uint32_t prevDelta; |
| uint32_t delta; |
| |
| for (i = 0, prevDelta = 0xFFFFFFFF; i < sizeof(inputSamplingRates)/sizeof(uint32_t); i++, prevDelta = delta) { |
| delta = abs(sampleRate - inputSamplingRates[i]); |
| if (delta > prevDelta) break; |
| } |
| // i is always > 0 here |
| return inputSamplingRates[i-1]; |
| } |
| |
| // getActiveInput_l() must be called with mLock held |
| AudioHardware::AudioStreamInMSM72xx *AudioHardware::getActiveInput_l() |
| { |
| for (size_t i = 0; i < mInputs.size(); i++) { |
| // return first input found not being in standby mode |
| // as only one input can be in this state |
| if (mInputs[i]->state() > AudioStreamInMSM72xx::AUDIO_INPUT_CLOSED) { |
| return mInputs[i]; |
| } |
| } |
| |
| return NULL; |
| } |
| // ---------------------------------------------------------------------------- |
| |
| AudioHardware::AudioStreamOutMSM72xx::AudioStreamOutMSM72xx() : |
| mHardware(0), mFd(-1), mStartCount(0), mRetryCount(0), mStandby(true), mDevices(0) |
| { |
| } |
| |
| status_t AudioHardware::AudioStreamOutMSM72xx::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; |
| |
| // 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; |
| |
| mDevices = devices; |
| |
| return NO_ERROR; |
| } |
| |
| AudioHardware::AudioStreamOutMSM72xx::~AudioStreamOutMSM72xx() |
| { |
| if (mFd >= 0) close(mFd); |
| } |
| |
| ssize_t AudioHardware::AudioStreamOutMSM72xx::write(const void* buffer, size_t bytes) |
| { |
| // ALOGD("AudioStreamOutMSM72xx::write(%p, %u)", buffer, bytes); |
| status_t status = NO_INIT; |
| size_t count = bytes; |
| const uint8_t* p = static_cast<const uint8_t*>(buffer); |
| |
| if (mStandby) { |
| |
| // open driver |
| ALOGV("open driver"); |
| status = ::open("/dev/msm_pcm_out", O_RDWR); |
| if (status < 0) { |
| ALOGE("Cannot open /dev/msm_pcm_out errno: %d", errno); |
| goto Error; |
| } |
| mFd = status; |
| |
| // configuration |
| ALOGV("get config"); |
| struct msm_audio_config config; |
| status = ioctl(mFd, AUDIO_GET_CONFIG, &config); |
| if (status < 0) { |
| ALOGE("Cannot read config"); |
| goto Error; |
| } |
| |
| ALOGV("set config"); |
| config.channel_count = AudioSystem::popCount(channels()); |
| config.sample_rate = sampleRate(); |
| config.buffer_size = bufferSize(); |
| config.buffer_count = AUDIO_HW_NUM_OUT_BUF; |
| config.codec_type = CODEC_TYPE_PCM; |
| status = ioctl(mFd, AUDIO_SET_CONFIG, &config); |
| if (status < 0) { |
| ALOGE("Cannot set config"); |
| goto Error; |
| } |
| |
| ALOGV("buffer_size: %u", config.buffer_size); |
| ALOGV("buffer_count: %u", config.buffer_count); |
| ALOGV("channel_count: %u", config.channel_count); |
| ALOGV("sample_rate: %u", config.sample_rate); |
| |
| // fill 2 buffers before AUDIO_START |
| mStartCount = AUDIO_HW_NUM_OUT_BUF; |
| mStandby = false; |
| } |
| |
| while (count) { |
| ssize_t written = ::write(mFd, p, count); |
| if (written >= 0) { |
| count -= written; |
| p += written; |
| } else { |
| if (errno != EAGAIN) return written; |
| mRetryCount++; |
| ALOGW("EAGAIN - retry"); |
| } |
| } |
| |
| // start audio after we fill 2 buffers |
| if (mStartCount) { |
| if (--mStartCount == 0) { |
| ioctl(mFd, AUDIO_START, 0); |
| } |
| } |
| return bytes; |
| |
| Error: |
| if (mFd >= 0) { |
| ::close(mFd); |
| mFd = -1; |
| } |
| // Simulate audio output timing in case of error |
| usleep(bytes * 1000000 / frameSize() / sampleRate()); |
| |
| return status; |
| } |
| |
| status_t AudioHardware::AudioStreamOutMSM72xx::standby() |
| { |
| status_t status = NO_ERROR; |
| if (!mStandby && mFd >= 0) { |
| ::close(mFd); |
| mFd = -1; |
| } |
| mStandby = true; |
| return status; |
| } |
| |
| status_t AudioHardware::AudioStreamOutMSM72xx::dump(int fd, const Vector<String16>& args) |
| { |
| const size_t SIZE = 256; |
| char buffer[SIZE]; |
| String8 result; |
| result.append("AudioStreamOutMSM72xx::dump\n"); |
| snprintf(buffer, SIZE, "\tsample rate: %d\n", sampleRate()); |
| result.append(buffer); |
| snprintf(buffer, SIZE, "\tbuffer size: %d\n", bufferSize()); |
| result.append(buffer); |
| snprintf(buffer, SIZE, "\tchannels: %d\n", channels()); |
| result.append(buffer); |
| snprintf(buffer, SIZE, "\tformat: %d\n", format()); |
| result.append(buffer); |
| snprintf(buffer, SIZE, "\tmHardware: %p\n", mHardware); |
| result.append(buffer); |
| snprintf(buffer, SIZE, "\tmFd: %d\n", mFd); |
| result.append(buffer); |
| snprintf(buffer, SIZE, "\tmStartCount: %d\n", mStartCount); |
| result.append(buffer); |
| snprintf(buffer, SIZE, "\tmRetryCount: %d\n", mRetryCount); |
| result.append(buffer); |
| snprintf(buffer, SIZE, "\tmStandby: %s\n", mStandby? "true": "false"); |
| result.append(buffer); |
| ::write(fd, result.string(), result.size()); |
| return NO_ERROR; |
| } |
| |
| bool AudioHardware::AudioStreamOutMSM72xx::checkStandby() |
| { |
| return mStandby; |
| } |
| |
| |
| status_t AudioHardware::AudioStreamOutMSM72xx::setParameters(const String8& keyValuePairs) |
| { |
| AudioParameter param = AudioParameter(keyValuePairs); |
| String8 key = String8(AudioParameter::keyRouting); |
| status_t status = NO_ERROR; |
| int device; |
| ALOGV("AudioStreamOutMSM72xx::setParameters() %s", keyValuePairs.string()); |
| |
| if (param.getInt(key, device) == NO_ERROR) { |
| mDevices = device; |
| ALOGV("set output routing %x", mDevices); |
| status = mHardware->doRouting(); |
| param.remove(key); |
| } |
| |
| if (param.size()) { |
| status = BAD_VALUE; |
| } |
| return status; |
| } |
| |
| String8 AudioHardware::AudioStreamOutMSM72xx::getParameters(const String8& keys) |
| { |
| AudioParameter param = AudioParameter(keys); |
| String8 value; |
| String8 key = String8(AudioParameter::keyRouting); |
| |
| if (param.get(key, value) == NO_ERROR) { |
| ALOGV("get routing %x", mDevices); |
| param.addInt(key, (int)mDevices); |
| } |
| |
| ALOGV("AudioStreamOutMSM72xx::getParameters() %s", param.toString().string()); |
| return param.toString(); |
| } |
| |
| status_t AudioHardware::AudioStreamOutMSM72xx::getRenderPosition(uint32_t *dspFrames) |
| { |
| //TODO: enable when supported by driver |
| return INVALID_OPERATION; |
| } |
| |
| // ---------------------------------------------------------------------------- |
| |
| AudioHardware::AudioStreamInMSM72xx::AudioStreamInMSM72xx() : |
| mHardware(0), mFd(-1), mState(AUDIO_INPUT_CLOSED), mRetryCount(0), |
| mFormat(AUDIO_HW_IN_FORMAT), mChannels(AUDIO_HW_IN_CHANNELS), |
| mSampleRate(AUDIO_HW_IN_SAMPLERATE), mBufferSize(AUDIO_HW_IN_BUFFERSIZE), |
| mAcoustics((AudioSystem::audio_in_acoustics)0), mDevices(0) |
| { |
| } |
| |
| status_t AudioHardware::AudioStreamInMSM72xx::set( |
| AudioHardware* hw, uint32_t devices, int *pFormat, uint32_t *pChannels, uint32_t *pRate, |
| AudioSystem::audio_in_acoustics acoustic_flags) |
| { |
| 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 = hw->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("AudioStreamInMSM72xx::set(%d, %d, %u)", *pFormat, *pChannels, *pRate); |
| if (mFd >= 0) { |
| ALOGE("Audio record already open"); |
| return -EPERM; |
| } |
| |
| // open audio input device |
| status_t status = ::open("/dev/msm_pcm_in", O_RDWR); |
| if (status < 0) { |
| ALOGE("Cannot open /dev/msm_pcm_in errno: %d", errno); |
| goto Error; |
| } |
| mFd = status; |
| |
| // configuration |
| ALOGV("get config"); |
| struct msm_audio_config config; |
| status = ioctl(mFd, AUDIO_GET_CONFIG, &config); |
| if (status < 0) { |
| ALOGE("Cannot read config"); |
| goto Error; |
| } |
| |
| ALOGV("set config"); |
| config.channel_count = AudioSystem::popCount(*pChannels); |
| config.sample_rate = *pRate; |
| config.buffer_size = bufferSize(); |
| config.buffer_count = 2; |
| config.codec_type = CODEC_TYPE_PCM; |
| status = ioctl(mFd, AUDIO_SET_CONFIG, &config); |
| if (status < 0) { |
| ALOGE("Cannot set config"); |
| if (ioctl(mFd, AUDIO_GET_CONFIG, &config) == 0) { |
| if (config.channel_count == 1) { |
| *pChannels = AudioSystem::CHANNEL_IN_MONO; |
| } else { |
| *pChannels = AudioSystem::CHANNEL_IN_STEREO; |
| } |
| *pRate = config.sample_rate; |
| } |
| goto Error; |
| } |
| |
| ALOGV("confirm config"); |
| status = ioctl(mFd, AUDIO_GET_CONFIG, &config); |
| if (status < 0) { |
| ALOGE("Cannot read config"); |
| goto Error; |
| } |
| ALOGV("buffer_size: %u", config.buffer_size); |
| ALOGV("buffer_count: %u", config.buffer_count); |
| ALOGV("channel_count: %u", config.channel_count); |
| ALOGV("sample_rate: %u", config.sample_rate); |
| |
| mDevices = devices; |
| mFormat = AUDIO_HW_IN_FORMAT; |
| mChannels = *pChannels; |
| mSampleRate = config.sample_rate; |
| mBufferSize = config.buffer_size; |
| |
| //mHardware->setMicMute_nosync(false); |
| mState = AUDIO_INPUT_OPENED; |
| |
| if (!acoustic) |
| return NO_ERROR; |
| |
| audpre_index = calculate_audpre_table_index(mSampleRate); |
| tx_iir_index = (audpre_index * 2) + (hw->checkOutputStandby() ? 0 : 1); |
| ALOGD("audpre_index = %d, tx_iir_index = %d\n", audpre_index, tx_iir_index); |
| |
| /** |
| * If audio-preprocessing failed, we should not block record. |
| */ |
| int (*msm72xx_set_audpre_params)(int, int); |
| msm72xx_set_audpre_params = (int (*)(int, int))::dlsym(acoustic, "msm72xx_set_audpre_params"); |
| status = msm72xx_set_audpre_params(audpre_index, tx_iir_index); |
| if (status < 0) |
| ALOGE("Cannot set audpre parameters"); |
| |
| int (*msm72xx_enable_audpre)(int, int, int); |
| msm72xx_enable_audpre = (int (*)(int, int, int))::dlsym(acoustic, "msm72xx_enable_audpre"); |
| mAcoustics = acoustic_flags; |
| status = msm72xx_enable_audpre((int)acoustic_flags, audpre_index, tx_iir_index); |
| if (status < 0) |
| ALOGE("Cannot enable audpre"); |
| |
| return NO_ERROR; |
| |
| Error: |
| if (mFd >= 0) { |
| ::close(mFd); |
| mFd = -1; |
| } |
| return status; |
| } |
| |
| AudioHardware::AudioStreamInMSM72xx::~AudioStreamInMSM72xx() |
| { |
| ALOGV("AudioStreamInMSM72xx destructor"); |
| standby(); |
| } |
| |
| ssize_t AudioHardware::AudioStreamInMSM72xx::read( void* buffer, ssize_t bytes) |
| { |
| ALOGV("AudioStreamInMSM72xx::read(%p, %ld)", buffer, bytes); |
| if (!mHardware) return -1; |
| |
| size_t count = bytes; |
| uint8_t* p = static_cast<uint8_t*>(buffer); |
| |
| if (mState < AUDIO_INPUT_OPENED) { |
| Mutex::Autolock lock(mHardware->mLock); |
| if (set(mHardware, mDevices, &mFormat, &mChannels, &mSampleRate, mAcoustics) != NO_ERROR) { |
| return -1; |
| } |
| } |
| |
| if (mState < AUDIO_INPUT_STARTED) { |
| mState = AUDIO_INPUT_STARTED; |
| // force routing to input device |
| mHardware->clearCurDevice(); |
| mHardware->doRouting(); |
| if (ioctl(mFd, AUDIO_START, 0)) { |
| ALOGE("Error starting record"); |
| standby(); |
| return -1; |
| } |
| } |
| |
| while (count) { |
| ssize_t bytesRead = ::read(mFd, buffer, count); |
| if (bytesRead >= 0) { |
| count -= bytesRead; |
| p += bytesRead; |
| } else { |
| if (errno != EAGAIN) return bytesRead; |
| mRetryCount++; |
| ALOGW("EAGAIN - retrying"); |
| } |
| } |
| return bytes; |
| } |
| |
| status_t AudioHardware::AudioStreamInMSM72xx::standby() |
| { |
| if (mState > AUDIO_INPUT_CLOSED) { |
| if (mFd >= 0) { |
| ::close(mFd); |
| mFd = -1; |
| } |
| mState = AUDIO_INPUT_CLOSED; |
| } |
| if (!mHardware) return -1; |
| // restore output routing if necessary |
| mHardware->clearCurDevice(); |
| mHardware->doRouting(); |
| return NO_ERROR; |
| } |
| |
| status_t AudioHardware::AudioStreamInMSM72xx::dump(int fd, const Vector<String16>& args) |
| { |
| const size_t SIZE = 256; |
| char buffer[SIZE]; |
| String8 result; |
| result.append("AudioStreamInMSM72xx::dump\n"); |
| snprintf(buffer, SIZE, "\tsample rate: %d\n", sampleRate()); |
| result.append(buffer); |
| snprintf(buffer, SIZE, "\tbuffer size: %d\n", bufferSize()); |
| result.append(buffer); |
| snprintf(buffer, SIZE, "\tchannels: %d\n", channels()); |
| result.append(buffer); |
| snprintf(buffer, SIZE, "\tformat: %d\n", format()); |
| result.append(buffer); |
| snprintf(buffer, SIZE, "\tmHardware: %p\n", mHardware); |
| result.append(buffer); |
| snprintf(buffer, SIZE, "\tmFd count: %d\n", mFd); |
| result.append(buffer); |
| snprintf(buffer, SIZE, "\tmState: %d\n", mState); |
| result.append(buffer); |
| snprintf(buffer, SIZE, "\tmRetryCount: %d\n", mRetryCount); |
| result.append(buffer); |
| ::write(fd, result.string(), result.size()); |
| return NO_ERROR; |
| } |
| |
| status_t AudioHardware::AudioStreamInMSM72xx::setParameters(const String8& keyValuePairs) |
| { |
| AudioParameter param = AudioParameter(keyValuePairs); |
| String8 key = String8(AudioParameter::keyRouting); |
| status_t status = NO_ERROR; |
| int device; |
| ALOGV("AudioStreamInMSM72xx::setParameters() %s", keyValuePairs.string()); |
| |
| if (param.getInt(key, device) == NO_ERROR) { |
| ALOGV("set input routing %x", device); |
| if (device & (device - 1)) { |
| status = BAD_VALUE; |
| } else { |
| mDevices = device; |
| status = mHardware->doRouting(); |
| } |
| param.remove(key); |
| } |
| |
| if (param.size()) { |
| status = BAD_VALUE; |
| } |
| return status; |
| } |
| |
| String8 AudioHardware::AudioStreamInMSM72xx::getParameters(const String8& keys) |
| { |
| AudioParameter param = AudioParameter(keys); |
| String8 value; |
| String8 key = String8(AudioParameter::keyRouting); |
| |
| if (param.get(key, value) == NO_ERROR) { |
| ALOGV("get routing %x", mDevices); |
| param.addInt(key, (int)mDevices); |
| } |
| |
| ALOGV("AudioStreamInMSM72xx::getParameters() %s", param.toString().string()); |
| return param.toString(); |
| } |
| |
| // ---------------------------------------------------------------------------- |
| |
| extern "C" AudioHardwareInterface* createAudioHardware(void) { |
| return new AudioHardware(); |
| } |
| |
| }; // namespace android |