
/*
 * --- GSMP-COPYRIGHT-NOTE-BEGIN ---
 * 
 * This copyright note is auto-generated by ./scripts/Create-CopyPatch.
 * Please add additional copyright information _after_ the line containing
 * the GSMP-COPYRIGHT-NOTE-END tag. Otherwise it might get removed by
 * the ./scripts/Create-CopyPatch script. Do not edit this copyright text!
 * 
 * GSMP: pcm/include/MADHelper.hh
 * General Sound Manipulation Program is Copyright (C) 2000 - 2004
 *   Valentin Ziegler and René Rebe
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2. A copy of the GNU General
 * Public License can be found in the file LICENSE.
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANT-
 * ABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
 * Public License for more details.
 * 
 * --- GSMP-COPYRIGHT-NOTE-END ---
 */

/* Because Robert Leslie pointed me to it to get a reference implementaion,
 * this code is mostly adapted from CoolPlayer's CPI_Player_CoDec_MPEG.c:
 * CoolPlayer - Blazing fast audio player.
 *   Copyright (C) 2000-2001 Niek Albers
 *   Copyright (C) 2001 Robert Leslie
 *
 *   Also released under the terms of the GPL v.2
 *
 * Feel free to use it as mid-layer C++ API!
 *
 */

/* Short Description:
 *   Midlevel and C++ API for the libMAD !!! (mp3 library)
 */

#ifndef LOWLEVEL__MADHELPER_HH__
#define LOWLEVEL__MADHELPER_HH__

#include <stdio.h>
#include <iostream>
#include <string>
#include <deque>

#include <unistd.h>

// I WANT A REAL OO only OS !!!!!!!!!!!

extern "C" {
#include "mad.h" // GPL mp3 lib
}

namespace GSMP {
  
  class MADFrameCache;

  /***********************
   * A dither class hack *
   ***********************/
  
  class MADDither
  {
  public:
    
    inline int64_t Prng () {
      return (prng_state * 0x0019660dL + 0x3c6ef35fL) & 0xffffffffL;
    }
    
    inline int32_t DoDither (mad_fixed_t sample)
    {
      // we use 24 bit here
      const unsigned int scalebits = MAD_F_FRACBITS + 1 - 24;
      
      // more example code at EOF
      
      return sample >> scalebits;
    }
  
  protected:
    int64_t prng_state;
    mad_fixed_t dither_error[3];
    mad_fixed_t dither_random;
  };

  
  class MADHelper
  {
  public:
    
    MADHelper ();
    ~MADHelper ();
  
    bool Open (const std::string& fname);
    bool Close ();
  
    long Size ();
    int Channels ();
    int Rate ();
    
    // inside MAD the channels are kept in seperate buffer - and since we usually
    // need deinterleaved buffer we export them - for each channel size samples
    // are read
    bool Read (int size, mad_fixed_t** buffer);
    bool Seek (int64_t pos);
  
  protected:
  
    void Cleanup ();
    bool ParseXing (struct mad_bitptr ptr, unsigned int bitlen);
    bool ScanHeader ();
  
    bool DecodeNext (bool frame_too);
    
    std::string m_fname;
    int m_fd;                        /* stream handle */
  
    long m_size;                     /* stream size, in bytes */
  
    signed long m_frames;            /* number of current decoded frame */
    long m_byte_of_mp3frame;         /* byte pos in mp3-stream */
    int m_sample_of_mp3frame;        /* pcm_frame of current mp3-frame */
    int m_pcm_frames;                /* total pcm_frames in file */
  
    int m_samplerate;                /* samplerate */
    int m_channels;                    /* channels / channels */
  
    struct xing {
      long flags;
      unsigned long frames;
      unsigned long bytes;
      unsigned char toc [100];
      long scale;
    };
    xing m_xing;                     /* Xing VBR tag data */
  
    mad_stream m_stream;             /* MAD stream structure */
    mad_frame m_frame;               /* MAD frame structure */
    mad_synth m_synth;               /* MAD synth structure */
  
    unsigned int m_samplecount;      /* samples output from current frame */
  
    mad_timer_t m_timer;             /* current playing time position */
    mad_timer_t m_length;            /* total playing time of current stream */
    unsigned long m_rate;            /* bitrate sum for computing average */
    
    unsigned int m_buflen;           /* input stream buffer vaild length */
    unsigned char m_buffer [40000];  /* input stream buffer - Rene: main src */ 
    MADFrameCache* m_frame_cache;
    MADDither dither [2];
  };
  
  
  // frame start table (needed for efficent seek) / can be shared
  // between MADHelpers ...
  class MADFrameCache {
  public:
    MADFrameCache ();
    ~MADFrameCache ();
    
    void Use ();
    void Unuse ();    
  protected:
    friend class MADHelper;
    unsigned int used;
    std::deque <int64_t> frames;
  };

}

#ifdef DO_DITHER
mad_fixed_t output, mask, random;

/* noise shape */
sample += dither_error[0] - dither_error[1] + dither_error[2];

dither_error[2] = dither_error[1];
dither_error[1] = dither_error[0] / 2;

/* bias */
output = sample + (1L << (scalebits - 1) );
mask = (1L << (scalebits + 1) ) - 1;

/* dither */
random  = Prng ();
output += (random & mask) - (dither_random & mask);

// dither_random = random;

/* clip */
const mad_fixed_t t_max = MAD_F_ONE - 1;
const mad_fixed_t t_min = -MAD_F_ONE;

if (output > t_max) {
  output = t_max;
  
  if (sample > t_max)
    sample = t_max;
}
else if (output < t_min) {
  output = t_min;
  
  if (sample < t_min)
    sample = t_min;
}

/* quantize */
output &= ~mask;

/* error feedback */
dither_error[0] = sample - output;

/* scale */
return output >> scalebits;
#endif

#endif // LOWLEVEL__MADHELPER_HH__
