/*
 * audio_decoder.h: The audio decoder wrapper
 *
 * See the README file for copyright information and how to reach the author.
 *
 * $Id: audio_decoder.h,v 1.2 2005/12/22 10:42:20 cm Exp $
 */

#ifndef __AUDIODECORER_H
#define __AUDIODECODER_H

#include <stdint.h>
#include <vdr/thread.h>
#include <mad.h>
#include "audio_buffer.h"

#ifdef DECODER_FAAD
#  include <mp4ff.h>
#  include <neaacdec.h>
#endif

#if defined(PLUGIN_DAAP)
#  include "daap_client.h"
#  include "daap_database.h"
#elif defined(PLUGIN_CDDA)
#  include "cdda_device.h"
#endif

const long PCM_BUFFER_SIZE = 524288;
// is this correct ? (channels = 2 * 1152 * sample size = 2)
const int OUTPUT_BUFFER_SIZE = 4608;
// 5 * 8192
const int INPUT_BUFFER_SIZE = 40960;

typedef enum eDecoderSeek {
  dsCurrent,
  dsSet
};

class cAudioDecoder : public cThread {
  protected:
#if defined(PLUGIN_DAAP)
    const cMediaDatabaseBlob *mpo_blob;
#elif defined(PLUGIN_CDDA)
    cCddaTrack *mpo_track;
#endif
    cSimpleRingBuffer mo_PCMBuffer;
  public:
#if defined(PLUGIN_DAAP)
    cAudioDecoder(const cMediaDatabaseBlob *blob);
#elif defined(PLUGIN_CDDA)
    cAudioDecoder(cCddaTrack *track);
#endif
    virtual ~cAudioDecoder();
    virtual unsigned long Read(uint8_t *data, unsigned long count) { return mo_PCMBuffer.Get(data, count); };
    virtual unsigned long Available(void) const { return mo_PCMBuffer.Available(); };
    virtual void Seek(long offset, eDecoderSeek whence) = 0;
    // returns time in milliseconds
    virtual signed long GetCurrentTime(void) = 0;
    // returns bits per second
    virtual unsigned long GetAvgBitRate(void) = 0;
    // returns the number of channels
    virtual unsigned char GetChannels(void) const = 0;
    // returns the sample rate
    virtual unsigned long GetSampleRate(void) const = 0;
    // returns total time
    virtual signed long GetTime(void) const = 0;
};

#if defined(PLUGIN_DAAP)

class cMp3Decoder : public cAudioDecoder {
  private:
    enum eDecoderState { dsNormal, dsReSync };
    static const unsigned int MinScanFrames;

    eDecoderState m_state;
    unsigned char m_channels;
    long m_time;
    unsigned long m_dataReadPos;
    unsigned long m_bitRate;
    unsigned long m_sampleRate;
    mad_timer_t m_timer;
    void Initialize(void);
    signed short MADFixedToSshort(mad_fixed_t sample) const;
    void Action();
  public:
    cMp3Decoder(const cMediaDatabaseBlob *blob);
    ~cMp3Decoder();
    void Seek(long offset, eDecoderSeek whence);
    signed long GetCurrentTime(void) { return mad_timer_count(m_timer, MAD_UNITS_MILLISECONDS); };
    unsigned long GetAvgBitRate(void) { return m_bitRate; };
    unsigned char GetChannels(void) const { return m_channels; };
    unsigned long GetSampleRate(void) const { return m_sampleRate; };
    signed long GetTime(void) const { return m_time; };
};

#ifdef DECODER_FAAD

class cMp4AACDecoder : public cAudioDecoder {
  friend uint32_t fMp4ffRead_Decoder(void *user_data, void *buffer, uint32_t length);
  friend uint32_t fMp4ffSeek_Decoder(void *user_data, uint64_t position);
  private:
    float m_curTime;
    unsigned char m_channels;
    int m_curSample;
    int m_maxSamples;
    int32_t m_selTrack;
    long m_time; 
    unsigned long m_dataReadPos;
    unsigned long m_avgBitRate;
    unsigned long m_sampleRate;
    mp4ff_t *mp_mp4;
    mp4ff_callback_t *mp_mp4cb;
    void Action();
  public:
    cMp4AACDecoder(const cMediaDatabaseBlob *blob);
    ~cMp4AACDecoder();
    void Seek(long offset, eDecoderSeek whence);
    signed long GetCurrentTime(void) { return long(m_curTime); };
    unsigned long GetAvgBitRate(void) { return m_avgBitRate; };
    unsigned char GetChannels(void) const { return m_channels; };
    unsigned long GetSampleRate(void) const { return m_sampleRate; };
    signed long GetTime(void) const { return m_time; };
};

#endif // DECODER_FAAD
#endif // PLUGIN_DAAP

#if defined(PLUGIN_CDDA)

class cCdaDecoder : public cAudioDecoder {
  private:
    float m_time;
    float m_curTime;
    unsigned long m_avgBitRate;
    unsigned long m_curFrame;
    unsigned long m_maxFrames;
    void Action();
  public:
    cCdaDecoder(cCddaTrack *track);
    ~cCdaDecoder();
    void Seek(long offset, eDecoderSeek whence);
    signed long GetCurrentTime(void) { return long(m_curTime); };
    unsigned long GetAvgBitRate(void) { return m_avgBitRate; };
    unsigned char GetChannels(void) const { return mpo_track->GetChannels(); };
    unsigned long GetSampleRate(void) const { return 44100; };
    signed long GetTime(void) const { return long(m_time); };
};

#endif

class cAudioDecoders {
  public:
    cAudioDecoders();
    ~cAudioDecoders();
#if defined(PLUGIN_DAAP)
    cAudioDecoder *Get(const char *format, const cMediaDatabaseBlob *) const;
#elif defined(PLUGIN_CDDA)
    cAudioDecoder *Get(const char *format, cCddaTrack *) const;
#endif
};

#endif
