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