C++程序  |  520行  |  14.1 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: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