C++程序  |  288行  |  8.2 KB

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