/*
 * Copyright (C) 2007 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_TRACK_SHARED_H
#define ANDROID_AUDIO_TRACK_SHARED_H

#include <stdint.h>
#include <sys/types.h>

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

namespace android {

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

// Maximum cumulated timeout milliseconds before restarting audioflinger thread
#define MAX_STARTUP_TIMEOUT_MS  3000    // Longer timeout period at startup to cope with A2DP
                                        // init time
#define MAX_RUN_TIMEOUT_MS      1000
#define WAIT_PERIOD_MS          10

#define CBLK_UNDERRUN   0x01 // set: underrun (out) or overrrun (in), clear: no underrun or overrun
#define CBLK_FORCEREADY 0x02 // set: track is considered ready immediately by AudioFlinger,
                             // clear: track is ready when buffer full
#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.
                Mutex       lock;           // sizeof(int)
                Condition   cv;             // sizeof(int)

                // next 4 are offsets within "buffers"
    volatile    uint32_t    user;
    volatile    uint32_t    server;
                uint32_t    userBase;
                uint32_t    serverBase;

                int         mPad1;          // unused, but preserves cache line alignment

                size_t      frameCount_;    // used during creation to pass actual track buffer size
                                            // from AudioFlinger to client, and not referenced again
                                            // FIXME remove here and replace by createTrack() in/out parameter
                                            // renamed to "_" to detect incorrect use

                // Cache line boundary (32 bytes)

                uint32_t    loopStart;
                uint32_t    loopEnd;        // read-only for server, read/write for client
                int         loopCount;      // read/write for client

                // Channel volumes are fixed point U4.12, so 0x1000 means 1.0.
                // Left channel is in [0:15], right channel is in [16:31].
                // Always read and write the combined pair atomically.
                // For AudioTrack only, not used by AudioRecord.
private:
                uint32_t    mVolumeLR;

                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

                // used by client only
                uint16_t    bufferTimeoutMs; // Maximum cumulated timeout before restarting
                                             // audioflinger

                uint16_t    waitTimeMs;      // Cumulated wait time, used by client only
private:
                // client write-only, server read-only
                uint16_t    mSendLevel;      // Fixed point U4.12 so 0x1000 means 1.0
public:
    volatile    int32_t     flags;

                // 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();

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, 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.

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

    // caller must limit to 0 <= volumeLR <= 0x10001000
    void        setVolumeLR(uint32_t volumeLR) {
        mCblk->mVolumeLR = volumeLR;
    }

    void        setSampleRate(uint32_t sampleRate) {
        mCblk->mSampleRate = sampleRate;
    }

    // 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*/);
    }

};

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

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

};

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

}; // namespace android

#endif // ANDROID_AUDIO_TRACK_SHARED_H