| /* |
| ** |
| ** Copyright 2012, 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. |
| */ |
| |
| #define LOG_TAG "AudioHAL:AudioStreamIn" |
| #include <utils/Log.h> |
| |
| #include "AudioStreamIn.h" |
| #include "AudioHardwareInput.h" |
| |
| #include <assert.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <fcntl.h> |
| |
| #include <utils/String8.h> |
| #include <media/AudioParameter.h> |
| |
| // for turning Remote mic on/off |
| #ifdef REMOTE_CONTROL_INTERFACE |
| #include <IRemoteControlService.h> |
| #endif |
| |
| namespace android { |
| |
| const audio_format_t AudioStreamIn::kAudioFormat = AUDIO_FORMAT_PCM_16_BIT; |
| const uint32_t AudioStreamIn::kChannelMask = AUDIO_CHANNEL_IN_MONO; |
| |
| // number of periods in the ALSA buffer |
| const int AudioStreamIn::kPeriodCount = 4; |
| |
| AudioStreamIn::AudioStreamIn(AudioHardwareInput& owner) |
| : mOwnerHAL(owner) |
| , mCurrentDeviceInfo(NULL) |
| , mRequestedSampleRate(0) |
| , mStandby(true) |
| , mDisabled(false) |
| , mPcm(NULL) |
| , mResampler(NULL) |
| , mBuffer(NULL) |
| , mBufferSize(0) |
| , mInputSource(AUDIO_SOURCE_DEFAULT) |
| , mReadStatus(0) |
| , mFramesIn(0) |
| { |
| struct resampler_buffer_provider& provider = |
| mResamplerProviderWrapper.provider; |
| provider.get_next_buffer = getNextBufferThunk; |
| provider.release_buffer = releaseBufferThunk; |
| mResamplerProviderWrapper.thiz = this; |
| } |
| |
| AudioStreamIn::~AudioStreamIn() |
| { |
| Mutex::Autolock _l(mLock); |
| standby_l(); |
| } |
| |
| // Perform stream initialization that may fail. |
| // Must only be called once at construction time. |
| status_t AudioStreamIn::set(audio_format_t *pFormat, uint32_t *pChannelMask, |
| uint32_t *pRate) |
| { |
| Mutex::Autolock _l(mLock); |
| |
| assert(mRequestedSampleRate == 0); |
| |
| // Respond with a request for mono if a different format is given. |
| if (*pChannelMask != kChannelMask) { |
| *pChannelMask = kChannelMask; |
| return BAD_VALUE; |
| } |
| |
| if (*pFormat != kAudioFormat) { |
| *pFormat = kAudioFormat; |
| return BAD_VALUE; |
| } |
| |
| mRequestedSampleRate = *pRate; |
| |
| return NO_ERROR; |
| } |
| |
| uint32_t AudioStreamIn::getSampleRate() |
| { |
| Mutex::Autolock _l(mLock); |
| return mRequestedSampleRate; |
| } |
| |
| status_t AudioStreamIn::setSampleRate(uint32_t rate) |
| { |
| (void) rate; |
| // this is a no-op in other audio HALs |
| return NO_ERROR; |
| } |
| |
| size_t AudioStreamIn::getBufferSize() |
| { |
| Mutex::Autolock _l(mLock); |
| |
| size_t size = AudioHardwareInput::calculateInputBufferSize( |
| mRequestedSampleRate, kAudioFormat, getChannelCount()); |
| return size; |
| } |
| |
| uint32_t AudioStreamIn::getChannelMask() |
| { |
| return kChannelMask; |
| } |
| |
| audio_format_t AudioStreamIn::getFormat() |
| { |
| return kAudioFormat; |
| } |
| |
| status_t AudioStreamIn::setFormat(audio_format_t format) |
| { |
| (void) format; |
| // other audio HALs fail any call to this API (even if the format matches |
| // the current format) |
| return INVALID_OPERATION; |
| } |
| |
| status_t AudioStreamIn::standby() |
| { |
| Mutex::Autolock _l(mLock); |
| return standby_l(); |
| } |
| |
| status_t AudioStreamIn::standby_l() |
| { |
| if (mStandby) { |
| return NO_ERROR; |
| } |
| if (mPcm) { |
| ALOGD("AudioStreamIn::standby_l, call pcm_close()"); |
| pcm_close(mPcm); |
| mPcm = NULL; |
| } |
| |
| // Turn OFF Remote MIC if we were recording from Remote. |
| if (mCurrentDeviceInfo != NULL) { |
| if (mCurrentDeviceInfo->forVoiceRecognition) { |
| setRemoteControlMicEnabled(false); |
| } |
| } |
| |
| if (mResampler) { |
| release_resampler(mResampler); |
| mResampler = NULL; |
| } |
| if (mBuffer) { |
| delete [] mBuffer; |
| mBuffer = NULL; |
| } |
| |
| mCurrentDeviceInfo = NULL; |
| mStandby = true; |
| mDisabled = false; |
| |
| return NO_ERROR; |
| } |
| |
| #define DUMP(a...) \ |
| snprintf(buffer, SIZE, a); \ |
| buffer[SIZE - 1] = 0; \ |
| result.append(buffer); |
| |
| status_t AudioStreamIn::dump(int fd) |
| { |
| const size_t SIZE = 256; |
| char buffer[SIZE]; |
| String8 result; |
| DUMP("\n AudioStreamIn::dump\n"); |
| |
| { |
| DUMP("\toutput sample rate: %d\n", mRequestedSampleRate); |
| if (mPcm) { |
| DUMP("\tinput sample rate: %d\n", mPcmConfig.rate); |
| DUMP("\tinput channels: %d\n", mPcmConfig.channels); |
| } |
| } |
| |
| ::write(fd, result.string(), result.size()); |
| |
| return NO_ERROR; |
| } |
| |
| status_t AudioStreamIn::setParameters(struct audio_stream* stream, |
| const char* kvpairs) |
| { |
| (void) stream; |
| AudioParameter param = AudioParameter(String8(kvpairs)); |
| status_t status = NO_ERROR; |
| String8 keySource = String8(AudioParameter::keyInputSource); |
| int intVal; |
| |
| if (param.getInt(keySource, intVal) == NO_ERROR) { |
| ALOGI("AudioStreamIn::setParameters, mInputSource set to %d", intVal); |
| mInputSource = intVal; |
| } |
| |
| return status; |
| } |
| |
| char* AudioStreamIn::getParameters(const char* keys) |
| { |
| (void) keys; |
| return strdup(""); |
| } |
| |
| status_t AudioStreamIn::setGain(float gain) |
| { |
| (void) gain; |
| // In other HALs, this is a no-op and returns success. |
| return NO_ERROR; |
| } |
| |
| uint32_t AudioStreamIn::getInputFramesLost() |
| { |
| return 0; |
| } |
| |
| status_t AudioStreamIn::addAudioEffect(effect_handle_t effect) |
| { |
| (void) effect; |
| // In other HALs, this is a no-op and returns success. |
| return 0; |
| } |
| |
| status_t AudioStreamIn::removeAudioEffect(effect_handle_t effect) |
| { |
| (void) effect; |
| // In other HALs, this is a no-op and returns success. |
| return 0; |
| } |
| |
| ssize_t AudioStreamIn::read(void* buffer, size_t bytes) |
| { |
| Mutex::Autolock _l(mLock); |
| |
| status_t status = NO_ERROR; |
| |
| if (mStandby) { |
| status = startInputStream_l(); |
| // Only try to start once to prevent pointless spew. |
| // If mic is not available then read will return silence. |
| // This is needed to prevent apps from hanging. |
| mStandby = false; |
| if (status != NO_ERROR) { |
| mDisabled = true; |
| } |
| } |
| |
| if ((status == NO_ERROR) && !mDisabled) { |
| int ret = readFrames_l(buffer, bytes / getFrameSize()); |
| status = (ret < 0) ? INVALID_OPERATION : NO_ERROR; |
| } |
| |
| if ((status != NO_ERROR) || mDisabled) { |
| memset(buffer, 0, bytes); |
| |
| // TODO: This code needs to project a timeline based on the number |
| // of audio frames synthesized from the last time we returned data |
| // from an actual audio device (or establish a fake timeline to obey |
| // if we have never returned any data from an actual device and need |
| // to synth on the first call to read) |
| usleep(bytes * 1000000 / getFrameSize() / mRequestedSampleRate); |
| } else { |
| bool mute; |
| mOwnerHAL.getMicMute(&mute); |
| if (mute) { |
| memset(buffer, 0, bytes); |
| } |
| } |
| |
| return bytes; |
| } |
| |
| void AudioStreamIn::setRemoteControlMicEnabled(bool flag) |
| { |
| #ifdef REMOTE_CONTROL_INTERFACE |
| sp<IRemoteControlService> service = IRemoteControlService::getInstance(); |
| if (service == NULL) { |
| ALOGE("%s: No RemoteControl service detected, ignoring\n", __func__); |
| return; |
| } |
| service->setMicEnabled(flag); |
| #else |
| (void)flag; |
| #endif |
| } |
| |
| status_t AudioStreamIn::startInputStream_l() |
| { |
| |
| ALOGI("AudioStreamIn::startInputStream_l, entry"); |
| |
| // Get the most appropriate device for the given input source, eg VOICE_RECOGNITION |
| const AudioHotplugThread::DeviceInfo *deviceInfo = mOwnerHAL.getBestDevice(mInputSource); |
| if (deviceInfo == NULL) { |
| return INVALID_OPERATION; |
| } |
| |
| memset(&mPcmConfig, 0, sizeof(mPcmConfig)); |
| |
| unsigned int requestedChannelCount = getChannelCount(); |
| |
| // Clip to min/max available. |
| if (requestedChannelCount < deviceInfo->minChannelCount ) { |
| mPcmConfig.channels = deviceInfo->minChannelCount; |
| } else if (requestedChannelCount > deviceInfo->maxChannelCount ) { |
| mPcmConfig.channels = deviceInfo->maxChannelCount; |
| } else { |
| mPcmConfig.channels = requestedChannelCount; |
| } |
| |
| ALOGD("AudioStreamIn::startInputStream_l, mRequestedSampleRate = %d", |
| mRequestedSampleRate); |
| |
| // Clip to min/max available from driver. |
| uint32_t chosenSampleRate = mRequestedSampleRate; |
| if (chosenSampleRate < deviceInfo->minSampleRate) { |
| chosenSampleRate = deviceInfo->minSampleRate; |
| } else if (chosenSampleRate > deviceInfo->maxSampleRate) { |
| chosenSampleRate = deviceInfo->maxSampleRate; |
| } |
| |
| // Turn on RemoteControl MIC if we are recording from it. |
| if (deviceInfo->forVoiceRecognition) { |
| setRemoteControlMicEnabled(true); |
| } |
| |
| mPcmConfig.rate = chosenSampleRate; |
| |
| mPcmConfig.period_size = |
| AudioHardwareInput::kPeriodMsec * mPcmConfig.rate / 1000; |
| mPcmConfig.period_count = kPeriodCount; |
| mPcmConfig.format = PCM_FORMAT_S16_LE; |
| |
| ALOGD("AudioStreamIn::startInputStream_l, call pcm_open()"); |
| struct pcm* pcm = pcm_open(deviceInfo->pcmCard, deviceInfo->pcmDevice, |
| PCM_IN, &mPcmConfig); |
| |
| if (!pcm_is_ready(pcm)) { |
| ALOGE("ERROR AudioStreamIn::startInputStream_l, pcm_open failed"); |
| pcm_close(pcm); |
| if (deviceInfo->forVoiceRecognition) { |
| setRemoteControlMicEnabled(false); |
| } |
| return NO_MEMORY; |
| } |
| |
| mCurrentDeviceInfo = deviceInfo; |
| |
| mBufferSize = pcm_frames_to_bytes(pcm, mPcmConfig.period_size); |
| if (mBuffer) { |
| delete [] mBuffer; |
| } |
| mBuffer = new int16_t[mBufferSize / sizeof(uint16_t)]; |
| |
| if (mResampler) { |
| release_resampler(mResampler); |
| mResampler = NULL; |
| } |
| if (mPcmConfig.rate != mRequestedSampleRate) { |
| ALOGD("AudioStreamIn::startInputStream_l, call create_resampler( %d to %d)", |
| mPcmConfig.rate, mRequestedSampleRate); |
| int ret = create_resampler(mPcmConfig.rate, |
| mRequestedSampleRate, |
| 1, |
| RESAMPLER_QUALITY_DEFAULT, |
| &mResamplerProviderWrapper.provider, |
| &mResampler); |
| if (ret != 0) { |
| ALOGW("AudioStreamIn: unable to create resampler"); |
| pcm_close(pcm); |
| return static_cast<status_t>(ret); |
| } |
| } |
| |
| mPcm = pcm; |
| |
| return NO_ERROR; |
| } |
| |
| // readFrames() reads frames from kernel driver, down samples to the capture |
| // rate if necessary and outputs the number of frames requested to the buffer |
| // specified |
| ssize_t AudioStreamIn::readFrames_l(void* buffer, ssize_t frames) |
| { |
| ssize_t framesWr = 0; |
| size_t frameSize = getFrameSize(); |
| |
| while (framesWr < frames) { |
| size_t framesRd = frames - framesWr; |
| if (mResampler) { |
| char* outFrame = static_cast<char*>(buffer) + |
| (framesWr * frameSize); |
| mResampler->resample_from_provider( |
| mResampler, |
| reinterpret_cast<int16_t*>(outFrame), |
| &framesRd); |
| } else { |
| struct resampler_buffer buf; |
| buf.raw = NULL; |
| buf.frame_count = framesRd; |
| |
| getNextBuffer(&buf); |
| if (buf.raw != NULL) { |
| memcpy(static_cast<char*>(buffer) + (framesWr * frameSize), |
| buf.raw, |
| buf.frame_count * frameSize); |
| framesRd = buf.frame_count; |
| } |
| releaseBuffer(&buf); |
| } |
| // mReadStatus is updated by getNextBuffer(), which is called by the |
| // resampler |
| if (mReadStatus != 0) |
| return mReadStatus; |
| |
| framesWr += framesRd; |
| } |
| return framesWr; |
| } |
| |
| int AudioStreamIn::getNextBufferThunk( |
| struct resampler_buffer_provider* bufferProvider, |
| struct resampler_buffer* buffer) |
| { |
| ResamplerBufferProviderWrapper* wrapper = |
| reinterpret_cast<ResamplerBufferProviderWrapper*>( |
| reinterpret_cast<char*>(bufferProvider) - |
| offsetof(ResamplerBufferProviderWrapper, provider)); |
| |
| return wrapper->thiz->getNextBuffer(buffer); |
| } |
| |
| void AudioStreamIn::releaseBufferThunk( |
| struct resampler_buffer_provider* bufferProvider, |
| struct resampler_buffer* buffer) |
| { |
| ResamplerBufferProviderWrapper* wrapper = |
| reinterpret_cast<ResamplerBufferProviderWrapper*>( |
| reinterpret_cast<char*>(bufferProvider) - |
| offsetof(ResamplerBufferProviderWrapper, provider)); |
| |
| wrapper->thiz->releaseBuffer(buffer); |
| } |
| |
| // called while holding mLock |
| int AudioStreamIn::getNextBuffer(struct resampler_buffer* buffer) |
| { |
| if (buffer == NULL) { |
| return -EINVAL; |
| } |
| |
| if (mPcm == NULL) { |
| buffer->raw = NULL; |
| buffer->frame_count = 0; |
| mReadStatus = -ENODEV; |
| return -ENODEV; |
| } |
| |
| if (mFramesIn == 0) { |
| mReadStatus = pcm_read(mPcm, mBuffer, mBufferSize); |
| if (mReadStatus) { |
| ALOGE("get_next_buffer() pcm_read error %d", mReadStatus); |
| buffer->raw = NULL; |
| buffer->frame_count = 0; |
| return mReadStatus; |
| } |
| |
| mFramesIn = mPcmConfig.period_size; |
| if (mPcmConfig.channels == 2) { |
| // Discard the right channel. |
| // TODO: this is what other HALs are doing to handle stereo input |
| // devices. Need to verify if this is appropriate for ATV Remote. |
| for (unsigned int i = 1; i < mFramesIn; i++) { |
| mBuffer[i] = mBuffer[i * 2]; |
| } |
| } |
| } |
| |
| buffer->frame_count = (buffer->frame_count > mFramesIn) ? |
| mFramesIn : buffer->frame_count; |
| buffer->i16 = mBuffer + (mPcmConfig.period_size - mFramesIn); |
| |
| return mReadStatus; |
| } |
| |
| // called while holding mLock |
| void AudioStreamIn::releaseBuffer(struct resampler_buffer* buffer) |
| { |
| if (buffer == NULL) { |
| return; |
| } |
| |
| mFramesIn -= buffer->frame_count; |
| } |
| |
| }; // namespace android |