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

Commit e3aa659e authored by Glenn Kasten's avatar Glenn Kasten
Browse files

Start isolating control block accesses in a proxy

The proxy object will eventually be the only code that understands the
details of the control block.  This should make it easier to change the
control block in the future.

Initial set of control block fields that are isolated:
 - sample rate
 - send level
 - volume

Prepare for streaming/static separation by adding a union to the control
block for the new fields.

Fix bug in handling of max sample rate on a track.  It was only checking
at re-configuration, not at each mix.

Simplify OutputTrack::obtainBuffer.

Change-Id: I2249f9d04f73a911a922ad1d7f6197292c74cd92
parent 84b6440f
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@
namespace android {

class audio_track_cblk_t;
class AudioRecordClientProxy;

// ----------------------------------------------------------------------------

@@ -374,6 +375,7 @@ private:
    uint32_t                mUpdatePeriod;      // in ms

    // constant after constructor or set()
    uint32_t                mSampleRate;
    size_t                  mFrameCount;
    audio_format_t          mFormat;
    uint8_t                 mChannelCount;
@@ -393,6 +395,7 @@ private:

    int                     mPreviousPriority;          // before start()
    SchedPolicy             mPreviousSchedulingGroup;
    AudioRecordClientProxy* mProxy;
};

}; // namespace android
+3 −0
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ namespace android {
// ----------------------------------------------------------------------------

class audio_track_cblk_t;
class AudioTrackClientProxy;

// ----------------------------------------------------------------------------

@@ -538,6 +539,7 @@ protected:

    float                   mVolume[2];
    float                   mSendLevel;
    uint32_t                mSampleRate;
    size_t                  mFrameCount;            // corresponds to current IAudioTrack
    size_t                  mReqFrameCount;         // frame count to request the next time a new
                                                    // IAudioTrack is needed
@@ -596,6 +598,7 @@ protected:
    bool                    mIsTimed;
    int                     mPreviousPriority;          // before start()
    SchedPolicy             mPreviousSchedulingGroup;
    AudioTrackClientProxy*  mProxy;
};

class TimedAudioTrack : public AudioTrack
+183 −45
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@
#include <sys/types.h>

#include <utils/threads.h>
#include <utils/Log.h>

namespace android {

@@ -38,9 +39,26 @@ namespace android {
#define CBLK_INVALID    0x04 // track buffer invalidated by AudioFlinger, need to re-create
#define CBLK_DISABLED   0x08 // track disabled by AudioFlinger due to underrun, need to re-start

struct AudioTrackSharedStreaming {
    // similar to NBAIO MonoPipe
    volatile int32_t mFront;
    volatile int32_t mRear;
};

// future
struct AudioTrackSharedStatic {
    int mReserved;
};

// ----------------------------------------------------------------------------

// Important: do not add any virtual methods, including ~
struct audio_track_cblk_t
{
                friend class Proxy;
                friend class AudioTrackClientProxy;
                friend class AudioRecordClientProxy;
                friend class ServerProxy;

    // The data members are grouped so that members accessed frequently and in the same context
    // are in the same line of data cache.
@@ -72,12 +90,13 @@ struct audio_track_cblk_t
                // For AudioTrack only, not used by AudioRecord.
private:
                uint32_t    mVolumeLR;
public:

                uint32_t    sampleRate;
                uint32_t    mSampleRate;    // AudioTrack only: client's requested sample rate in Hz
                                            // or 0 == default. Write-only client, read-only server.

                uint8_t     mPad2;           // unused

public:
                // read-only for client, server writes once at initialization and is then read-only
                uint8_t     mName;           // normal tracks: track name, fast tracks: track index

@@ -94,65 +113,184 @@ public:

                // Cache line boundary (32 bytes)

#if 0
                union {
                    AudioTrackSharedStreaming   mStreaming;
                    AudioTrackSharedStatic      mStatic;
                    int                         mAlign[8];
                } u;

                // Cache line boundary (32 bytes)
#endif

                // Since the control block is always located in shared memory, this constructor
                // is only used for placement new().  It is never used for regular new() or stack.
                            audio_track_cblk_t();

                // called by client only, where client includes regular
                // AudioTrack and AudioFlinger::PlaybackThread::OutputTrack
                uint32_t    stepUserIn(size_t stepCount, size_t frameCount) { return stepUser(stepCount, frameCount, false); }
                uint32_t    stepUserOut(size_t stepCount, size_t frameCount) { return stepUser(stepCount, frameCount, true); }

                bool        stepServer(size_t stepCount, size_t frameCount, bool isOut);

private:
                // if there is a shared buffer, "buffers" is the value of pointer() for the shared
                // buffer, otherwise "buffers" points immediately after the control block
                void*       buffer(void *buffers, uint32_t frameSize, uint32_t offset) const;

                uint32_t    framesAvailableIn(size_t frameCount)
                                { return framesAvailable(frameCount, false); }
                uint32_t    framesAvailableOut(size_t frameCount)
                                { return framesAvailable(frameCount, true); }
                uint32_t    framesAvailableIn_l(size_t frameCount)
                                { return framesAvailable_l(frameCount, false); }
                uint32_t    framesAvailableOut_l(size_t frameCount)
                                { return framesAvailable_l(frameCount, true); }
                uint32_t    framesReadyIn() { return framesReady(false); }
                uint32_t    framesReadyOut() { return framesReady(true); }
                void*       buffer(void *buffers, uint32_t frameSize, size_t offset) const;

                bool        tryLock();

                // isOut == true means AudioTrack, isOut == false means AudioRecord
                bool        stepServer(size_t stepCount, size_t frameCount, bool isOut);
                uint32_t    stepUser(size_t stepCount, size_t frameCount, bool isOut);
                uint32_t    framesAvailable(size_t frameCount, bool isOut);
                uint32_t    framesAvailable_l(size_t frameCount, bool isOut);
                uint32_t    framesReady(bool isOut);
};

// ----------------------------------------------------------------------------

// Proxy for shared memory control block, to isolate callers from needing to know the details.
// There is exactly one ClientProxy and one ServerProxy per shared memory control block.
// The proxies are located in normal memory, and are not multi-thread safe within a given side.
class Proxy {
protected:
    Proxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount, size_t frameSize)
        : mCblk(cblk), mBuffers(buffers), mFrameCount(frameCount), mFrameSize(frameSize) { }
    virtual ~Proxy() { }

public:
    void*   buffer(size_t offset) const {
        return mCblk->buffer(mBuffers, mFrameSize, offset);
    }

protected:
    // These refer to shared memory, and are virtual addresses with respect to the current process.
    // They may have different virtual addresses within the other process.
    audio_track_cblk_t* const   mCblk;          // the control block
    void* const                 mBuffers;       // starting address of buffers

    const size_t                mFrameCount;    // not necessarily a power of 2
    const size_t                mFrameSize;     // in bytes
#if 0
    const size_t                mFrameCountP2;  // mFrameCount rounded to power of 2, streaming mode
#endif

};

// ----------------------------------------------------------------------------

// Proxy seen by AudioTrack client and AudioRecord client
class ClientProxy : public Proxy {
protected:
    ClientProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount, size_t frameSize)
        : Proxy(cblk, buffers, frameCount, frameSize) { }
    virtual ~ClientProxy() { }
};

// ----------------------------------------------------------------------------

// Proxy used by AudioTrack client, which also includes AudioFlinger::PlaybackThread::OutputTrack
class AudioTrackClientProxy : public ClientProxy {
public:
    AudioTrackClientProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount, size_t frameSize)
        : ClientProxy(cblk, buffers, frameCount, frameSize) { }
    virtual ~AudioTrackClientProxy() { }

    // No barriers on the following operations, so the ordering of loads/stores
    // with respect to other parameters is UNPREDICTABLE. That's considered safe.

                // for AudioTrack client only, caller must limit to 0.0 <= sendLevel <= 1.0
    // caller must limit to 0.0 <= sendLevel <= 1.0
    void        setSendLevel(float sendLevel) {
                    mSendLevel = uint16_t(sendLevel * 0x1000);
        mCblk->mSendLevel = uint16_t(sendLevel * 0x1000);
    }

                // for AudioFlinger only; the return value must be validated by the caller
                uint16_t    getSendLevel_U4_12() const {
                    return mSendLevel;
    // caller must limit to 0 <= volumeLR <= 0x10001000
    void        setVolumeLR(uint32_t volumeLR) {
        mCblk->mVolumeLR = volumeLR;
    }

                // for AudioTrack client only, caller must limit to 0 <= volumeLR <= 0x10001000
                void        setVolumeLR(uint32_t volumeLR) {
                    mVolumeLR = volumeLR;
    void        setSampleRate(uint32_t sampleRate) {
        mCblk->mSampleRate = sampleRate;
    }

                // for AudioFlinger only; the return value must be validated by the caller
                uint32_t    getVolumeLR() const {
                    return mVolumeLR;
    // called by:
    //   PlaybackThread::OutputTrack::write
    //   AudioTrack::createTrack_l
    //   AudioTrack::releaseBuffer
    //   AudioTrack::reload
    //   AudioTrack::restoreTrack_l (2 places)
    size_t      stepUser(size_t stepCount) {
        return mCblk->stepUser(stepCount, mFrameCount, true /*isOut*/);
    }

    // called by AudioTrack::obtainBuffer and AudioTrack::processBuffer
    size_t      framesAvailable() {
        return mCblk->framesAvailable(mFrameCount, true /*isOut*/);
    }

    // called by AudioTrack::obtainBuffer and PlaybackThread::OutputTrack::obtainBuffer
    // FIXME remove this API since it assumes a lock that should be invisible to caller
    size_t      framesAvailable_l() {
        return mCblk->framesAvailable_l(mFrameCount, true /*isOut*/);
    }

private:
                // isOut == true means AudioTrack, isOut == false means AudioRecord
                uint32_t    stepUser(size_t stepCount, size_t frameCount, bool isOut);
                uint32_t    framesAvailable(size_t frameCount, bool isOut);
                uint32_t    framesAvailable_l(size_t frameCount, bool isOut);
                uint32_t    framesReady(bool isOut);
};

// ----------------------------------------------------------------------------

// Proxy used by AudioRecord client
class AudioRecordClientProxy : public ClientProxy {
public:
    AudioRecordClientProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount, size_t frameSize)
        : ClientProxy(cblk, buffers, frameCount, frameSize) { }
    ~AudioRecordClientProxy() { }

    // called by AudioRecord::releaseBuffer
    size_t      stepUser(size_t stepCount) {
        return mCblk->stepUser(stepCount, mFrameCount, false /*isOut*/);
    }

    // called by AudioRecord::processBuffer
    size_t      framesAvailable() {
        return mCblk->framesAvailable(mFrameCount, false /*isOut*/);
    }

    // called by AudioRecord::obtainBuffer
    size_t      framesReady() {
        return mCblk->framesReady(false /*isOut*/);
    }

};

// ----------------------------------------------------------------------------

// Proxy used by AudioFlinger server
class ServerProxy : public Proxy {
public:
    ServerProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount, size_t frameSize, bool isOut)
        : Proxy(cblk, buffers, frameCount, frameSize), mIsOut(isOut) { }
    virtual ~ServerProxy() { }

    // for AudioTrack and AudioRecord
    bool        step(size_t stepCount) { return mCblk->stepServer(stepCount, mFrameCount, mIsOut); }

    // return value of these methods must be validated by the caller
    uint32_t    getSampleRate() const { return mCblk->mSampleRate; }
    uint16_t    getSendLevel_U4_12() const { return mCblk->mSendLevel; }
    uint32_t    getVolumeLR() const { return mCblk->mVolumeLR; }

    // for AudioTrack only
    size_t      framesReady() {
        ALOG_ASSERT(mIsOut);
        return mCblk->framesReady(true);
    }

    // for AudioRecord only, called by RecordThread::RecordTrack::getNextBuffer
    // FIXME remove this API since it assumes a lock that should be invisible to caller
    size_t      framesAvailableIn_l() {
        ALOG_ASSERT(!mIsOut);
        return mCblk->framesAvailable_l(mFrameCount, false);
    }

private:
    const bool  mIsOut;     // true for AudioTrack, false for AudioRecord

};

// ----------------------------------------------------------------------------

+31 −16
Original line number Diff line number Diff line
@@ -75,7 +75,8 @@ status_t AudioRecord::getMinFrameCount(

AudioRecord::AudioRecord()
    : mStatus(NO_INIT), mSessionId(0),
      mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(SP_DEFAULT)
      mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(SP_DEFAULT),
      mProxy(NULL)
{
}

@@ -90,7 +91,9 @@ AudioRecord::AudioRecord(
        int notificationFrames,
        int sessionId)
    : mStatus(NO_INIT), mSessionId(0),
      mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(SP_DEFAULT)
      mPreviousPriority(ANDROID_PRIORITY_NORMAL),
      mPreviousSchedulingGroup(SP_DEFAULT),
      mProxy(NULL)
{
    mStatus = set(inputSource, sampleRate, format, channelMask,
            frameCount, cbf, user, notificationFrames, sessionId);
@@ -112,6 +115,7 @@ AudioRecord::~AudioRecord()
        IPCThreadState::self()->flushCommands();
        AudioSystem::releaseAudioSessionId(mSessionId);
    }
    delete mProxy;
}

status_t AudioRecord::set(
@@ -149,6 +153,8 @@ status_t AudioRecord::set(
    if (sampleRate == 0) {
        sampleRate = DEFAULT_SAMPLE_RATE;
    }
    mSampleRate = sampleRate;

    // these below should probably come from the audioFlinger too...
    if (format == AUDIO_FORMAT_DEFAULT) {
        format = AUDIO_FORMAT_PCM_16_BIT;
@@ -166,6 +172,12 @@ status_t AudioRecord::set(
    uint32_t channelCount = popcount(channelMask);
    mChannelCount = channelCount;

    if (audio_is_linear_pcm(mFormat)) {
        mFrameSize = channelCount * audio_bytes_per_sample(format);
    } else {
        mFrameSize = sizeof(uint8_t);
    }

    if (sessionId == 0 ) {
        mSessionId = AudioSystem::newAudioSessionId();
    } else {
@@ -218,12 +230,6 @@ status_t AudioRecord::set(
    // Update buffer size in case it has been limited by AudioFlinger during track creation
    mFrameCount = mCblk->frameCount_;

    if (audio_is_linear_pcm(mFormat)) {
        mFrameSize = channelCount * audio_bytes_per_sample(format);
    } else {
        mFrameSize = sizeof(uint8_t);
    }

    mActive = false;
    mCbf = cbf;
    mNotificationFrames = notificationFrames;
@@ -360,7 +366,7 @@ bool AudioRecord::stopped() const

uint32_t AudioRecord::getSampleRate() const
{
    return mCblk->sampleRate;
    return mSampleRate;
}

status_t AudioRecord::setMarkerPosition(uint32_t marker)
@@ -473,11 +479,18 @@ status_t AudioRecord::openRecord_l(
    mBuffers = (char*)cblk + sizeof(audio_track_cblk_t);
    cblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS;
    cblk->waitTimeMs = 0;

    // update proxy
    delete mProxy;
    mProxy = new AudioRecordClientProxy(cblk, mBuffers, frameCount, mFrameSize);

    return NO_ERROR;
}

status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, int32_t waitCount)
{
    ALOG_ASSERT(mStatus == NO_ERROR && mProxy != NULL);

    AutoMutex lock(mLock);
    bool active;
    status_t result = NO_ERROR;
@@ -488,7 +501,7 @@ status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, int32_t waitCount)
    audioBuffer->frameCount  = 0;
    audioBuffer->size        = 0;

    uint32_t framesReady = cblk->framesReadyIn();
    size_t framesReady = mProxy->framesReady();

    if (framesReady == 0) {
        cblk->lock.lock();
@@ -551,7 +564,7 @@ create_new_record:
            }
            // read the server count again
        start_loop_here:
            framesReady = cblk->framesReadyIn();
            framesReady = mProxy->framesReady();
        }
        cblk->lock.unlock();
    }
@@ -573,15 +586,17 @@ create_new_record:

    audioBuffer->frameCount  = framesReq;
    audioBuffer->size        = framesReq * mFrameSize;
    audioBuffer->raw         = cblk->buffer(mBuffers, mFrameSize, u);
    audioBuffer->raw         = mProxy->buffer(u);
    active = mActive;
    return active ? status_t(NO_ERROR) : status_t(STOPPED);
}

void AudioRecord::releaseBuffer(Buffer* audioBuffer)
{
    ALOG_ASSERT(mStatus == NO_ERROR && mProxy != NULL);

    AutoMutex lock(mLock);
    mCblk->stepUserIn(audioBuffer->frameCount, mFrameCount);
    (void) mProxy->stepUser(audioBuffer->frameCount);
}

audio_io_handle_t AudioRecord::getInput() const
@@ -594,7 +609,7 @@ audio_io_handle_t AudioRecord::getInput() const
audio_io_handle_t AudioRecord::getInput_l()
{
    mInput = AudioSystem::getInput(mInputSource,
                                mCblk->sampleRate,
                                mSampleRate,
                                mFormat,
                                mChannelMask,
                                mSessionId);
@@ -745,7 +760,7 @@ bool AudioRecord::processAudioBuffer(const sp<AudioRecordThread>& thread)


    // Manage overrun callback
    if (active && (cblk->framesAvailableIn(mFrameCount) == 0)) {
    if (active && (mProxy->framesAvailable() == 0)) {
        // The value of active is stale, but we are almost sure to be active here because
        // otherwise we would have exited when obtainBuffer returned STOPPED earlier.
        ALOGV("Overrun user: %x, server: %x, flags %04x", cblk->user, cblk->server, cblk->flags);
@@ -781,7 +796,7 @@ status_t AudioRecord::restoreRecord_l(audio_track_cblk_t*& refCblk)
    // if the new IAudioRecord is created, openRecord_l() will modify the
    // following member variables: mAudioRecord, mCblkMemory and mCblk.
    // It will also delete the strong references on previous IAudioRecord and IMemory
    result = openRecord_l(cblk->sampleRate, mFormat, mFrameCount, getInput_l());
    result = openRecord_l(mSampleRate, mFormat, mFrameCount, getInput_l());
    if (result == NO_ERROR) {
        newCblk = mCblk;
        // callback thread or sync event hasn't changed
+71 −38

File changed.

Preview size limit exceeded, changes collapsed.

Loading