/*
**
** 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:AudioHardwareInput"
#include <utils/Log.h>
#include <fcntl.h>
#include <sys/eventfd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <utils/String8.h>
#include "AudioHardwareInput.h"
#include "AudioHotplugThread.h"
#include "AudioStreamIn.h"
namespace android {
// Global singleton.
AudioHardwareInput gAudioHardwareInput;
AudioHardwareInput::AudioHardwareInput()
: mMicMute(false)
{
mHotplugThread = new AudioHotplugThread(*this);
if (mHotplugThread == NULL) {
ALOGE("Unable to create ATV Remote audio hotplug thread. "
"Pluggable audio input devices will not function.");
} else if (!mHotplugThread->start()) {
ALOGE("Unable to start ATV Remote audio hotplug thread. "
"Pluggable audio input devices will not function.");
mHotplugThread.clear();
}
for (int i=0; i<kMaxDevices; i++) {
mDeviceInfos[i].valid = false;
}
}
AudioHardwareInput::~AudioHardwareInput()
{
if (mHotplugThread != NULL) {
mHotplugThread->shutdown();
mHotplugThread.clear();
}
closeAllInputStreams();
}
status_t AudioHardwareInput::setMicMute(bool mute)
{
mMicMute = mute;
return NO_ERROR;
}
status_t AudioHardwareInput::getMicMute(bool* mute)
{
*mute = mMicMute;
return NO_ERROR;
}
// milliseconds per ALSA period
const uint32_t AudioHardwareInput::kPeriodMsec = 10;
size_t AudioHardwareInput::calculateInputBufferSize(uint32_t outputSampleRate,
audio_format_t format,
uint32_t channelCount)
{
size_t size;
// AudioFlinger expects audio buffers to be a multiple of 16 frames
size = (kPeriodMsec * outputSampleRate) / 1000;
size = ((size + 15) / 16) * 16;
return size * channelCount * audio_bytes_per_sample(format);
}
status_t AudioHardwareInput::getInputBufferSize(const audio_config* config)
{
size_t size = calculateInputBufferSize(config->sample_rate,
config->format,
audio_channel_count_from_in_mask(config->channel_mask));
return size;
}
AudioStreamIn* AudioHardwareInput::openInputStream(uint32_t devices,
audio_format_t* format, uint32_t* channelMask, uint32_t* sampleRate,
status_t* status)
{
(void) devices;
Mutex::Autolock _l(mLock);
AudioStreamIn* in;
in = new AudioStreamIn(*this);
if (in == NULL) {
*status = NO_MEMORY;
return NULL;
}
*status = in->set(format, channelMask, sampleRate);
if (*status != NO_ERROR) {
delete in;
return NULL;
}
mInputStreams.add(in);
return in;
}
void AudioHardwareInput::closeInputStream(AudioStreamIn* in)
{
Mutex::Autolock _l(mLock);
for (size_t i = 0; i < mInputStreams.size(); i++) {
if (in == mInputStreams[i]) {
mInputStreams.removeAt(i);
in->standby();
delete in;
break;
}
}
}
void AudioHardwareInput::closeAllInputStreams()
{
while (mInputStreams.size() != 0) {
AudioStreamIn* in = mInputStreams[0];
mInputStreams.removeAt(0);
in->standby();
delete in;
}
}
void AudioHardwareInput::standbyAllInputStreams(const AudioHotplugThread::DeviceInfo* deviceInfo)
{
for (size_t i = 0; i < mInputStreams.size(); i++) {
if (deviceInfo == NULL || deviceInfo == mInputStreams[i]->getDeviceInfo()) {
mInputStreams[i]->standby();
}
}
}
#define DUMP(a...) \
snprintf(buffer, SIZE, a); \
buffer[SIZE - 1] = 0; \
result.append(buffer);
#define B2STR(b) b ? "true" : "false"
status_t AudioHardwareInput::dump(int fd)
{
const size_t SIZE = 256;
char buffer[SIZE];
String8 result;
DUMP("\nAudioHardwareInput::dump\n");
for (int i=0; i<kMaxDevices; i++) {
if (mDeviceInfos[i].valid) {
DUMP("device[%d] is valid\n", i);
DUMP("\tcapture card: %d\n", mDeviceInfos[i].pcmCard);
DUMP("\tcapture device: %d\n", mDeviceInfos[i].pcmDevice);
}
}
::write(fd, result.string(), result.size());
{
Mutex::Autolock _l(mLock);
for (size_t i = 0; i < mInputStreams.size(); i++) {
mInputStreams[i]->dump(fd);
}
}
return NO_ERROR;
}
#undef DUMP
#undef B2STR
// called on the audio hotplug thread
void AudioHardwareInput::onDeviceFound(
const AudioHotplugThread::DeviceInfo& devInfo)
{
bool foundSlot = false;
Mutex::Autolock _l(mLock);
ALOGD("AudioHardwareInput::onDeviceFound pcmCard = %d", devInfo.pcmCard);
for (int i=0; i<kMaxDevices; i++) {
if (mDeviceInfos[i].valid) {
if ((mDeviceInfos[i].pcmCard == devInfo.pcmCard)
&& (mDeviceInfos[i].pcmDevice == devInfo.pcmDevice)) {
ALOGW("AudioHardwareInput::onDeviceFound already has %d:%d",
devInfo.pcmCard, devInfo.pcmDevice);
return; // Got it already so no action needed.
}
}
}
// New device so find an empty slot and save it.
for (int i=0; i<kMaxDevices; i++) {
if (!mDeviceInfos[i].valid) {
ALOGD("AudioHardwareInput::onDeviceFound saving as device #%d", i);
mDeviceInfos[i] = devInfo;
mDeviceInfos[i].valid = true;
foundSlot = true;
/* Restart any currently running streams. */
standbyAllInputStreams(NULL);
break;
}
}
if (!foundSlot) {
ALOGW("AudioHardwareInput::onDeviceFound found more devices than expected! Dropped");
}
}
// called on the audio hotplug thread
void AudioHardwareInput::onDeviceRemoved(unsigned int pcmCard, unsigned int pcmDevice)
{
Mutex::Autolock _l(mLock);
ALOGD("AudioHardwareInput::onDeviceRemoved pcmCard = %d", pcmCard);
// Find matching DeviceInfo.
for (int i=0; i<kMaxDevices; i++) {
if (mDeviceInfos[i].valid) {
if ((mDeviceInfos[i].pcmCard == pcmCard) && (mDeviceInfos[i].pcmDevice == pcmDevice)) {
ALOGD("AudioHardwareInput::onDeviceRemoved matches #%d", i);
mDeviceInfos[i].valid = false;
/* If currently active stream is using this device then restart. */
standbyAllInputStreams(&mDeviceInfos[i]);
break;
}
}
}
}
const AudioHotplugThread::DeviceInfo* AudioHardwareInput::getBestDevice(int inputSource)
{
bool doVoiceRecognition = (inputSource == AUDIO_SOURCE_VOICE_RECOGNITION);
int chosenDeviceIndex = -1;
Mutex::Autolock _l(mLock);
ALOGD("AudioHardwareInput::getBestDevice inputSource = %d, doVoiceRecognition = %d",
inputSource, (doVoiceRecognition ? 1 : 0));
// RemoteControl is the only input device usable for voice recognition
// and no other devices are used for voice recognition.
// Currently the RemoteControl is the only device marked with forVoiceRecognition=true.
// A connected USB mic could be used for anything but voice recognition.
for (int i=0; i<kMaxDevices; i++) {
if (mDeviceInfos[i].valid) {
if (mDeviceInfos[i].forVoiceRecognition == doVoiceRecognition) {
chosenDeviceIndex = i;
break;
}
}
}
if (chosenDeviceIndex < 0) {
ALOGE("ERROR AudioHardwareInput::getBestDevice, none for source %d", inputSource);
} else {
ALOGD("AudioHardwareInput::getBestDevice chose #%d", chosenDeviceIndex);
}
return (chosenDeviceIndex >= 0) ? &mDeviceInfos[chosenDeviceIndex] : NULL;
}
}; // namespace android