

#ifndef VALIB_SPK_H
#define VALIB_SPK_H

#include "defs.h"






#define FORMAT_UNKNOWN     (-1)
#define FORMAT_RAWDATA     0
#define FORMAT_LINEAR      1


#define FORMAT_PCM16       2
#define FORMAT_PCM24       3
#define FORMAT_PCM32       4


#define FORMAT_PCM16_BE    5
#define FORMAT_PCM24_BE    6
#define FORMAT_PCM32_BE    7


#define FORMAT_PCMFLOAT    8


#define FORMAT_PES        10 
#define FORMAT_SPDIF      11 


#define FORMAT_MPA        12
#define FORMAT_AC3        13
#define FORMAT_DTS        14


#define FORMAT_AAC        15
#define FORMAT_OGG        16






#define FORMAT_MASK(format)  (1 << (format))


#define FORMAT_MASK_RAWDATA      FORMAT_MASK(FORMAT_RAWDATA)
#define FORMAT_MASK_LINEAR       FORMAT_MASK(FORMAT_LINEAR)


#define FORMAT_MASK_PCM16        FORMAT_MASK(FORMAT_PCM16)
#define FORMAT_MASK_PCM24        FORMAT_MASK(FORMAT_PCM24)
#define FORMAT_MASK_PCM32        FORMAT_MASK(FORMAT_PCM32)


#define FORMAT_MASK_PCM16_BE     FORMAT_MASK(FORMAT_PCM16_BE)
#define FORMAT_MASK_PCM24_BE     FORMAT_MASK(FORMAT_PCM24_BE)
#define FORMAT_MASK_PCM32_BE     FORMAT_MASK(FORMAT_PCM32_BE)


#define FORMAT_MASK_PCMFLOAT     FORMAT_MASK(FORMAT_PCMFLOAT)


#define FORMAT_MASK_PES          FORMAT_MASK(FORMAT_PES)
#define FORMAT_MASK_SPDIF        FORMAT_MASK(FORMAT_SPDIF)


#define FORMAT_MASK_AC3          FORMAT_MASK(FORMAT_AC3)
#define FORMAT_MASK_MPA          FORMAT_MASK(FORMAT_MPA)
#define FORMAT_MASK_DTS          FORMAT_MASK(FORMAT_DTS)
#define FORMAT_MASK_AAC          FORMAT_MASK(FORMAT_AAC)
#define FORMAT_MASK_OGG          FORMAT_MASK(FORMAT_OGG)





#define FORMAT_CLASS_PCM_LE      (FORMAT_MASK_PCM16    | FORMAT_MASK_PCM24    | FORMAT_MASK_PCM32)
#define FORMAT_CLASS_PCM_BE      (FORMAT_MASK_PCM16_BE | FORMAT_MASK_PCM24_BE | FORMAT_MASK_PCM32_BE)
#define FORMAT_CLASS_PCM         (FORMAT_CLASS_PCM_LE  | FORMAT_CLASS_PCM_BE  | FORMAT_MASK_PCMFLOAT)
#define FORMAT_CLASS_CONTAINER   (FORMAT_MASK_PES | FORMAT_MASK_SPDIF)
#define FORMAT_CLASS_SPDIFABLE   (FORMAT_MASK_MPA | FORMAT_MASK_AC3 | FORMAT_MASK_DTS)
#define FORMAT_CLASS_COMPRESSED  0x1f000






#define CH_L    0  
#define CH_C    1  
#define CH_R    2  
#define CH_SL   3  
#define CH_SR   4  
#define CH_LFE  5  
#define CH_NONE 6  


#define CH_M    1  
#define CH_CH1  0  
#define CH_CH2  2  
#define CH_S    3  







#define CH_MASK(ch)  (1 << (ch & 0x1f))


#define CH_MASK_L    1
#define CH_MASK_C    2
#define CH_MASK_R    4
#define CH_MASK_SL   8
#define CH_MASK_SR   16
#define CH_MASK_LFE  32


#define CH_MASK_M    2
#define CH_MASK_C1   0
#define CH_MASK_C2   4
#define CH_MASK_S    8





#define MODE_UNDEFINED 0
#define MODE_1_0     (CH_MASK_M)
#define MODE_2_0     (CH_MASK_L | CH_MASK_R)
#define MODE_3_0     (CH_MASK_L | CH_MASK_C  | CH_MASK_R)
#define MODE_2_1     (MODE_2_0  | CH_MASK_S)
#define MODE_3_1     (MODE_3_0  | CH_MASK_S)
#define MODE_2_2     (MODE_2_0  | CH_MASK_SL | CH_MASK_SR)
#define MODE_3_2     (MODE_3_0  | CH_MASK_SL | CH_MASK_SR)
#define MODE_1_0_LFE (CH_MASK_M | CH_MASK_LFE)
#define MODE_2_0_LFE (CH_MASK_L | CH_MASK_R  | CH_MASK_LFE)
#define MODE_3_0_LFE (CH_MASK_L | CH_MASK_C  | CH_MASK_R  | CH_MASK_LFE)
#define MODE_2_1_LFE (MODE_2_0  | CH_MASK_S  | CH_MASK_LFE)
#define MODE_3_1_LFE (MODE_3_0  | CH_MASK_S  | CH_MASK_LFE)
#define MODE_2_2_LFE (MODE_2_0  | CH_MASK_SL | CH_MASK_SR | CH_MASK_LFE)
#define MODE_3_2_LFE (MODE_3_0  | CH_MASK_SL | CH_MASK_SR | CH_MASK_LFE)


#define MODE_MONO    MODE_1_0
#define MODE_STEREO  MODE_2_0
#define MODE_QUADRO  MODE_2_2
#define MODE_5_1     MODE_3_2_LFE






#define NO_RELATION       0  
#define RELATION_DOLBY    1  
#define RELATION_DOLBY2   2  
#define RELATION_SUMDIFF  3  






class Speakers
{
public:
  int format;           
  int mask;             
  int sample_rate;      
  int relation;         
  sample_t level;       

  Speakers() 
  {
    set_unknown();
  };

  Speakers(int _format, int _mask, int _sample_rate, sample_t _level = -1, int _relation = NO_RELATION)
  {
    set(_format, _mask, _sample_rate, _level, _relation);
  }

  inline void set(int format, int mask, int sample_rate, sample_t level = -1, int relation = NO_RELATION);
  inline void set_unknown();

  inline bool is_unknown() const;
  inline bool is_linear() const;
  inline bool is_rawdata() const;

  inline bool is_pcm()   const;
  inline bool is_spdif() const;

  inline int  nch()   const;
  inline bool lfe()   const;

  inline const int *order() const;

  inline int  sample_size() const;

  inline bool operator ==(const Speakers &spk) const;
  inline bool operator !=(const Speakers &spk) const;

  inline const char *format_text() const;
  inline const char *mode_text() const;
};





extern const Speakers spk_unknown;
extern const Speakers spk_rawdata;






extern const int std_order[NCHANNELS];
extern const int win_order[NCHANNELS];






struct samples_t
{
  sample_t *samples[NCHANNELS];

  inline sample_t *&operator [](int ch)       { return samples[ch]; }
  inline sample_t  *operator [](int ch) const { return samples[ch]; }

  inline samples_t &operator +=(int n);
  inline samples_t &operator -=(int n);
  inline samples_t &zero();

  void reorder_to_std(Speakers spk, const int order[NCHANNELS]);
  void reorder_from_std(Speakers spk, const int order[NCHANNELS]);
  void reorder(Speakers spk, const int input_order[NCHANNELS], const int output_order[NCHANNELS]);
};





extern const int sample_size_tbl[32];
extern const int mask_nch_tbl[64];
extern const int mask_order_tbl[64][6];
extern const char *mode_text[64];

inline int sample_size(int format)
{
  return sample_size_tbl[format & 0x2f];
}

inline int mask_nch(int mask)
{
  return mask_nch_tbl[mask & 0x3f];
}

inline const int *mask_order(int mask)
{
  return mask_order_tbl[mask & 0x3f];
}


inline void 
Speakers::set(int _format, int _mask, int _sample_rate, sample_t _level, int _relation)
{
  format = _format;
  mask = _mask;
  sample_rate = _sample_rate;
  level = _level;
  if (level < 0) switch (format)
  {
    case FORMAT_PCM16: case FORMAT_PCM16_BE: level = 32787; break;
    case FORMAT_PCM24: case FORMAT_PCM24_BE: level = 8388607; break;
    case FORMAT_PCM32: case FORMAT_PCM32_BE: level = 2147483647; break;
    default: level = 1.0;
  }
  relation = _relation;
}

inline void 
Speakers::set_unknown()
{
  format = FORMAT_UNKNOWN;
  mask = 0;
  sample_rate = 0;
  relation = NO_RELATION;
  level = 1.0;
}

inline bool Speakers::is_unknown() const
{ return format == FORMAT_UNKNOWN; }

inline bool Speakers::is_linear() const
{ return format == FORMAT_LINEAR; }

inline bool Speakers::is_rawdata() const
{ return format != FORMAT_LINEAR; }

inline bool Speakers::is_pcm() const
{ return (FORMAT_MASK(format) & FORMAT_CLASS_PCM) != 0; }

inline bool Speakers::is_spdif() const
{ return format == FORMAT_SPDIF; }

inline int Speakers::nch() const
{ return mask_nch(mask); }

inline bool 
Speakers::lfe() const
{
  return (mask & CH_MASK_LFE) != 0;
}

inline const int *
Speakers::order() const
{
  return ::mask_order(mask);
}

inline int 
Speakers::sample_size() const
{
  return ::sample_size(format);
}

inline bool
Speakers::operator ==(const Speakers &_spk) const
{
  return (format == _spk.format) &&
         (mask == _spk.mask) &&
         (sample_rate == _spk.sample_rate) &&
         (level == _spk.level) &&
         (relation == _spk.relation);
}

inline bool
Speakers::operator !=(const Speakers &_spk) const
{
  return (format != _spk.format) ||
         (mask != _spk.mask) ||
         (sample_rate != _spk.sample_rate) ||
         (level != _spk.level) ||
         (relation != _spk.relation);
}

inline const char *
Speakers::format_text() const
{
  switch (format)
  {
    case FORMAT_RAWDATA:     return "Raw data";
    case FORMAT_LINEAR:      return "Linear PCM";

    case FORMAT_PCM16:       return "PCM16";
    case FORMAT_PCM24:       return "PCM24";
    case FORMAT_PCM32:       return "PCM32";

    case FORMAT_PCM16_BE:    return "PCM16 BE";
    case FORMAT_PCM24_BE:    return "PCM24 BE";
    case FORMAT_PCM32_BE:    return "PCM32 BE";

    case FORMAT_PCMFLOAT:    return "PCM Float";

    case FORMAT_PES:         return "MPEG Program Stream";
    case FORMAT_SPDIF:       return "SPDIF";

    case FORMAT_AC3:         return "AC3";
    case FORMAT_MPA:         return "MPEG Audio";
    case FORMAT_DTS:         return "DTS";
    case FORMAT_AAC:         return "AAC";
    case FORMAT_OGG:         return "OGG";

    default: return "Unknown";
  };
}

inline const char *
Speakers::mode_text() const
{
  switch (relation)
  {
    case RELATION_DOLBY:   return "Dolby Surround";
    case RELATION_DOLBY2:  return "Dolby ProLogic II";
    case RELATION_SUMDIFF: return "Sum-difference";
  }

  return ::mode_text[mask];
}





inline samples_t &
samples_t::operator +=(int _n)
{
  samples[0] += _n;
  samples[1] += _n;
  samples[2] += _n;
  samples[3] += _n;
  samples[4] += _n;
  samples[5] += _n;
  return *this;
}

inline samples_t &
samples_t::operator -=(int _n)
{
  samples[0] -= _n;
  samples[1] -= _n;
  samples[2] -= _n;
  samples[3] -= _n;
  samples[4] -= _n;
  samples[5] -= _n;
  return *this;
}

inline samples_t &
samples_t::zero()
{
  samples[0] = 0;
  samples[1] = 0;
  samples[2] = 0;
  samples[3] = 0;
  samples[4] = 0;
  samples[5] = 0;
  return *this;
}

#endif
