Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3f502bfc authored by Glenn Kasten's avatar Glenn Kasten Committed by Android (Google) Code Review
Browse files

Merge "Non-blocking audio I/O interface, WIP"

parents 8d0648ef 01066232
Loading
Loading
Loading
Loading
+24 −0
Original line number Diff line number Diff line
@@ -2,6 +2,29 @@ LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)

LOCAL_SRC_FILES := \
    AudioBufferProviderSource.cpp   \
    AudioStreamOutSink.cpp          \
    AudioStreamInSource.cpp         \
    NBAIO.cpp                       \
    MonoPipe.cpp                    \
    MonoPipeReader.cpp              \
    Pipe.cpp                        \
    PipeReader.cpp                  \
    roundup.c                       \
    SourceAudioBufferProvider.cpp

# libsndfile license is incompatible; uncomment to use for local debug only
#LOCAL_SRC_FILES += LibsndfileSink.cpp LibsndfileSource.cpp
#LOCAL_C_INCLUDES += path/to/libsndfile/src
#LOCAL_STATIC_LIBRARIES += libsndfile

LOCAL_MODULE := libnbaio

include $(BUILD_STATIC_LIBRARY)

include $(CLEAR_VARS)

LOCAL_SRC_FILES:=               \
    AudioFlinger.cpp            \
    AudioMixer.cpp.arm          \
@@ -31,6 +54,7 @@ LOCAL_SHARED_LIBRARIES := \
    libpowermanager

LOCAL_STATIC_LIBRARIES := \
    libnbaio \
    libcpustats \
    libmedia_helper

+141 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 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 "AudioBufferProviderSource"
//#define LOG_NDEBUG 0

#include <cutils/compiler.h>
#include <utils/Log.h>
#include "AudioBufferProviderSource.h"

namespace android {

AudioBufferProviderSource::AudioBufferProviderSource(AudioBufferProvider *provider,
                                                     NBAIO_Format format) :
    NBAIO_Source(format), mProvider(provider), mConsumed(0)
{
    ALOG_ASSERT(provider != NULL);
    ALOG_ASSERT(format != Format_Invalid);
}

AudioBufferProviderSource::~AudioBufferProviderSource()
{
    if (mBuffer.raw != NULL) {
        mProvider->releaseBuffer(&mBuffer);
    }
}

ssize_t AudioBufferProviderSource::availableToRead()
{
    if (CC_UNLIKELY(!mNegotiated)) {
        return NEGOTIATE;
    }
    return mBuffer.raw != NULL ? mBuffer.frameCount - mConsumed : 0;
}

ssize_t AudioBufferProviderSource::read(void *buffer, size_t count)
{
    if (CC_UNLIKELY(!mNegotiated)) {
        return NEGOTIATE;
    }
    if (CC_UNLIKELY(mBuffer.raw == NULL)) {
        mBuffer.frameCount = count;
        status_t status = mProvider->getNextBuffer(&mBuffer, AudioBufferProvider::kInvalidPTS);
        if (status != OK) {
            return status == NOT_ENOUGH_DATA ? (ssize_t) WOULD_BLOCK : (ssize_t) status;
        }
        ALOG_ASSERT(mBuffer.raw != NULL);
        // mConsumed is 0 either from constructor or after releaseBuffer()
    }
    size_t available = mBuffer.frameCount - mConsumed;
    if (CC_UNLIKELY(count > available)) {
        count = available;
    }
    // count could be zero, either because count was zero on entry or
    // available is zero, but both are unlikely so don't check for that
    memcpy(buffer, (char *) mBuffer.raw + (mConsumed << mBitShift), count << mBitShift);
    if (CC_UNLIKELY((mConsumed += count) >= mBuffer.frameCount)) {
        mProvider->releaseBuffer(&mBuffer);
        mBuffer.raw = NULL;
        mConsumed = 0;
    }
    mFramesRead += count;
    // For better responsiveness with large values of count,
    // return a short count rather than continuing with next buffer.
    // This gives the caller a chance to interpolate other actions.
    return count;
}

ssize_t AudioBufferProviderSource::readVia(readVia_t via, size_t total, void *user, size_t block)
{
    if (CC_UNLIKELY(!mNegotiated)) {
        return NEGOTIATE;
    }
    if (CC_UNLIKELY(block == 0)) {
        block = ~0;
    }
    for (size_t accumulator = 0; ; ) {
        ALOG_ASSERT(accumulator <= total);
        size_t count = total - accumulator;
        if (CC_UNLIKELY(count == 0)) {
            return accumulator;
        }
        if (CC_LIKELY(count > block)) {
            count = block;
        }
        // 1 <= count <= block
        if (CC_UNLIKELY(mBuffer.raw == NULL)) {
            mBuffer.frameCount = count;
            status_t status = mProvider->getNextBuffer(&mBuffer, AudioBufferProvider::kInvalidPTS);
            if (CC_LIKELY(status == OK)) {
                ALOG_ASSERT(mBuffer.raw != NULL && mBuffer.frameCount <= count);
                // mConsumed is 0 either from constructor or after releaseBuffer()
                continue;
            }
            // FIXME simplify logic - does the initial count and block checks again for no reason;
            //       don't you just want to fall through to the size_t available line?
            if (CC_LIKELY(status == NOT_ENOUGH_DATA)) {
                status = WOULD_BLOCK;
            }
            return accumulator > 0 ? accumulator : (ssize_t) status;
        }
        size_t available = mBuffer.frameCount - mConsumed;
        if (CC_UNLIKELY(count > available)) {
            count = available;
        }
        if (CC_LIKELY(count > 0)) {
            ssize_t ret = via(user, (char *) mBuffer.raw + (mConsumed << mBitShift), count);
            if (CC_UNLIKELY(ret <= 0)) {
                if (CC_LIKELY(accumulator > 0)) {
                    return accumulator;
                }
                return ret;
            }
            ALOG_ASSERT((size_t) ret <= count);
            mFramesRead += ret;
            accumulator += ret;
            if (CC_LIKELY((mConsumed += ret) < mBuffer.frameCount)) {
                continue;
            }
        }
        mProvider->releaseBuffer(&mBuffer);
        mBuffer.raw = NULL;
        mConsumed = 0;
        // don't get next buffer until we really need it
    }
}

}   // namespace android
+56 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 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.
 */

// Implementation of NBAIO_Source that wraps an AudioBufferProvider

#ifndef ANDROID_AUDIO_BUFFER_PROVIDER_SOURCE_H
#define ANDROID_AUDIO_BUFFER_PROVIDER_SOURCE_H

#include "NBAIO.h"
#include "AudioBufferProvider.h"

namespace android {

class AudioBufferProviderSource : public NBAIO_Source {

public:
    AudioBufferProviderSource(AudioBufferProvider *provider, NBAIO_Format format);
    virtual ~AudioBufferProviderSource();

    // NBAIO_Port interface

    //virtual ssize_t negotiate(const NBAIO_Format offers[], size_t numOffers,
    //                          NBAIO_Format counterOffers[], size_t& numCounterOffers);
    //virtual NBAIO_Format format();

    // NBAIO_Source interface

    //virtual size_t framesRead() const;
    //virtual size_t framesOverrun();
    //virtual size_t overruns();
    virtual ssize_t availableToRead();
    virtual ssize_t read(void *buffer, size_t count);
    virtual ssize_t readVia(readVia_t via, size_t total, void *user, size_t block);

private:
    AudioBufferProvider * const mProvider;
    AudioBufferProvider::Buffer mBuffer;    // current buffer
    size_t                      mConsumed;  // number of frames consumed so far from current buffer
};

}   // namespace android

#endif  // ANDROID_AUDIO_BUFFER_PROVIDER_SOURCE_H
+83 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 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 "AudioStreamInSource"
//#define LOG_NDEBUG 0

#include <cutils/compiler.h>
#include <utils/Log.h>
#include "AudioStreamInSource.h"

namespace android {

AudioStreamInSource::AudioStreamInSource(audio_stream_in *stream) :
        NBAIO_Source(),
        mStream(stream),
        mStreamBufferSizeBytes(0),
        mFramesOverrun(0),
        mOverruns(0)
{
    ALOG_ASSERT(stream != NULL);
}

AudioStreamInSource::~AudioStreamInSource()
{
}

ssize_t AudioStreamInSource::negotiate(const NBAIO_Format offers[], size_t numOffers,
                                      NBAIO_Format counterOffers[], size_t& numCounterOffers)
{
    if (mFormat == Format_Invalid) {
        mStreamBufferSizeBytes = mStream->common.get_buffer_size(&mStream->common);
        audio_format_t streamFormat = mStream->common.get_format(&mStream->common);
        if (streamFormat == AUDIO_FORMAT_PCM_16_BIT) {
            uint32_t sampleRate = mStream->common.get_sample_rate(&mStream->common);
            audio_channel_mask_t channelMask =
                    (audio_channel_mask_t) mStream->common.get_channels(&mStream->common);
            mFormat = Format_from_SR_C(sampleRate, popcount(channelMask));
            mBitShift = Format_frameBitShift(mFormat);
        }
    }
    return NBAIO_Source::negotiate(offers, numOffers, counterOffers, numCounterOffers);
}

size_t AudioStreamInSource::framesOverrun()
{
    uint32_t framesOverrun = mStream->get_input_frames_lost(mStream);
    if (framesOverrun > 0) {
        mFramesOverrun += framesOverrun;
        // FIXME only increment for contiguous ranges
        ++mOverruns;
    }
    return mFramesOverrun;
}

ssize_t AudioStreamInSource::read(void *buffer, size_t count)
{
    if (CC_UNLIKELY(mFormat == Format_Invalid)) {
        return NEGOTIATE;
    }
    ssize_t bytesRead = mStream->read(mStream, buffer, count << mBitShift);
    if (bytesRead > 0) {
        size_t framesRead = bytesRead >> mBitShift;
        mFramesRead += framesRead;
        return framesRead;
    } else {
        return bytesRead;
    }
}

}   // namespace android
+65 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 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.
 */

#ifndef ANDROID_AUDIO_STREAM_IN_SOURCE_H
#define ANDROID_AUDIO_STREAM_IN_SOURCE_H

#include <hardware/audio.h>
#include "NBAIO.h"

namespace android {

// not multi-thread safe
class AudioStreamInSource : public NBAIO_Source {

public:
    AudioStreamInSource(audio_stream_in *stream);
    virtual ~AudioStreamInSource();

    // NBAIO_Port interface

    virtual ssize_t negotiate(const NBAIO_Format offers[], size_t numOffers,
                              NBAIO_Format counterOffers[], size_t& numCounterOffers);
    //virtual NBAIO_Format format() const;

    // NBAIO_Sink interface

    //virtual size_t framesRead() const;
    virtual size_t framesOverrun();
    virtual size_t overruns() { (void) framesOverrun(); return mOverruns; }

    // This is an over-estimate, and could dupe the caller into making a blocking read()
    // FIXME Use an audio HAL API to query the buffer filling status when it's available.
    virtual ssize_t availableToRead() { return mStreamBufferSizeBytes >> mBitShift; }

    virtual ssize_t read(void *buffer, size_t count);

    // NBAIO_Sink end

#if 0   // until necessary
    audio_stream_in *stream() const { return mStream; }
#endif

private:
    audio_stream_in * const mStream;
    size_t              mStreamBufferSizeBytes; // as reported by get_buffer_size()
    size_t              mFramesOverrun;
    size_t              mOverruns;
};

}   // namespace android

#endif  // ANDROID_AUDIO_STREAM_IN_SOURCE_H
Loading