/*
**
** Copyright 2011, 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_OUTPUT_H
#define ANDROID_AUDIO_OUTPUT_H

#include <semaphore.h>
#include <tinyalsa/asoundlib.h>
#include <utils/LinearTransform.h>
#include <utils/String16.h>
#include <utils/String8.h>
#include <utils/threads.h>
#include <utils/Vector.h>

namespace android {

class AudioStreamOut;

class AudioOutput : public RefBase {
  public:

    // Audio ouput state machine states.
    enum State {
        // Ouput not yet started or synchronized.
        OUT_OF_SYNC,

        // Silence primed to output to start DMA.
        PRIMED,

        // DMA started, ready to align to other inputs.
        DMA_START,

        // DMA active.
        ACTIVE,

        // Fatal, unrecoverable error.
        FATAL,
    };

                        AudioOutput(const char* alsa_name,
                                    enum pcm_format alsa_pcm_format);
    virtual            ~AudioOutput();

    virtual status_t    initCheck();
    virtual status_t    setupForStream(const AudioStreamOut& stream) = 0;

    // State machine transition functions.
    State               getState() { return mState; };
    bool                hasFatalError() { return mState == FATAL; }

    // Prime data to output device, go to PRIMED state.
    void                primeOutput(bool hasActiveOutputs);

    // Adjust for write timestamp difference, go to ACTIVE state.
    void                adjustDelay(int32_t nFrames);

    // Send one chunk of data to ALSA, if state machine permits. This is called
    // for every chunk sent down, regardless of the state of the output.
    void                processOneChunk(const uint8_t* data, size_t len,
                                        bool hasActiveOutputs, audio_format_t format);

    status_t            getNextWriteTimestamp(int64_t* timestamp,
                                              bool* discon);
    bool                getLastNextWriteTSValid() const;
    int64_t             getLastNextWriteTS() const;

    uint32_t            getExternalDelay_uSec() const;
    void                setExternalDelay_uSec(uint32_t delay);
    void                setDelayComp_uSec(uint32_t delay_usec);

    void                setVolume(float vol);
    void                setMute(bool mute);
    void                setOutputIsFixed(bool fixed);
    void                setFixedOutputLevel(float level);

    float               getVolume()           const { return mVolume; }
    bool                getMute()             const { return mMute; }
    bool                getOutputIsFixed()    const { return mOutputFixed; }
    float               getFixedOutputLevel() const { return mFixedLvl; }

    int                 getHardwareTimestamp(unsigned int *pAvail,
                                struct timespec *pTimestamp);
    uint32_t            getKernelBufferSize() { return mFramesPerChunk * mBufferChunks; }

    virtual void        dump(String8& result) = 0;

    virtual const char* getOutputName() = 0;
    virtual uint32_t    devMask() const = 0;

    virtual void        cleanupResources();

    static const uint32_t kMaxDelayCompensationMSec;
    static const uint32_t kPrimeTimeoutChunks;

  protected:

    void                pushSilence(uint32_t nFrames);

    virtual void        openPCMDevice();
    virtual void        reset();
    virtual status_t    getDMAStartData(int64_t* dma_start_time,
                                        int64_t* frames_queued_to_driver);
    void                doPCMWrite(const uint8_t* data, size_t len, audio_format_t format);
    void                setupInternal();

    // Current state machine state.
    State               mState;

    // Output format
    uint32_t            mFramesPerChunk;
    uint32_t            mFramesPerSec;
    uint32_t            mBufferChunks;
    uint32_t            mChannelCnt;
    const char*         mALSAName;
    enum pcm_format     mALSAFormat;

    // These numbers are relative to the ALSA output.
    uint32_t            mBytesPerSample;
    uint32_t            mBytesPerFrame;
    uint32_t            mBytesPerChunk;
    size_t              mStagingSize;
    void*               mStagingBuf;
    size_t              mSilenceSize;
    void*               mSilenceBuf;

    // Get next write time stuff.
    bool                mLastNextWriteTimeValid;
    int64_t             mLastNextWriteTime;
    int64_t             mLastDMAStartTime;

    // External delay compensation.
    uint32_t            mMaxDelayCompFrames;
    uint32_t            mExternalDelayUSec;
    uint32_t            mExternalDelayLocalTicks;

    LinearTransform     mFramesToLocalTime;

    // ALSA device stuff.
    Mutex               mDeviceLock;
    struct pcm*         mDevice;
    int                 mDeviceExtFd;
    int                 mALSACardID;
    uint64_t            mFramesQueuedToDriver;
    uint32_t            mPrimeTimeoutChunks;

    // reduce log spew
    bool                mReportedWriteFail;

    // Volume stuff
    Mutex               mVolumeLock;
    float               mVolume;
    float               mFixedLvl;
    bool                mMute;
    bool                mOutputFixed;
    bool                mVolParamsDirty;
    virtual void        applyPendingVolParams() = 0;
};

typedef Vector< sp<AudioOutput> > AudioOutputList;

}  // namespace android
#endif  // ANDROID_AUDIO_OUTPUT_H