/* blipbuffer.c

Copyright (C) 2003-2006 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
more details. You should have received a copy of the GNU Lesser General
Public License along with this module; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

Original C++ source:
 Blip_Buffer 0.4.0. http://www.slack.net/~ant/

partially reimplemented in C by Gergely Szasz for FUSE

*/

#include "config.h"

#include <limits.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>

#include "blipbuffer.h"


static void _blip_synth_init( Blip_Synth_ * synth_, short *impulses );

inline void
blip_buffer_set_clock_rate( Blip_Buffer * buff, long cps )
{
  buff->factor_ = blip_buffer_clock_rate_factor( buff, buff->clock_rate_ =
                                                 cps );
}

inline long
blip_buffer_samples_avail( Blip_Buffer * buff )
{
  return ( long )( buff->offset_ >> BLIP_BUFFER_ACCURACY );
}

void
blip_synth_set_output( Blip_Synth * synth, Blip_Buffer * b )
{
  synth->impl.buf = b;
  synth->impl.last_amp = 0;
}

void
blip_synth_set_volume( Blip_Synth * synth, double v )
{
  _blip_synth_volume_unit( &synth->impl,
                           v * ( 1.0 /
                                 ( BLIP_SYNTH_RANGE <
                                   0 ? -( BLIP_SYNTH_RANGE ) :
                                   BLIP_SYNTH_RANGE ) ) );
}

#define BLIP_FWD( i )                     \
	t0 = i0 * delta + buf[fwd + i];   \
	t1 = imp[BLIP_RES * (i + 1)] * delta + buf[fwd + 1 + i]; \
	i0 = imp[BLIP_RES * (i + 2)];          \
	buf[fwd + i] = t0;                     \
	buf[fwd + 1 + i] = t1

#define BLIP_REV( r ) \
	t0 = i0 * delta + buf[rev - r];   \
	t1 = imp[BLIP_RES * r] * delta + buf[rev + 1 - r];   \
	i0 = imp[BLIP_RES * (r - 1)];          \
	buf[rev - r] = t0;                     \
	buf[rev + 1 - r] = t1

inline void
blip_synth_offset_resampled( Blip_Synth * synth, blip_resampled_time_t time,
                             int delta, Blip_Buffer * blip_buf )
{
  int phase, fwd, rev, mid;

  imp_t *imp;

  long *buf, i0, t0, t1;

  delta *= synth->impl.delta_factor;
  phase =
    ( int )( time >> ( BLIP_BUFFER_ACCURACY - BLIP_PHASE_BITS ) &
             ( BLIP_RES - 1 ) );
  imp = synth->impulses + BLIP_RES - phase;
  buf = blip_buf->buffer_ + ( time >> BLIP_BUFFER_ACCURACY );
  i0 = *imp;

  fwd = ( BLIP_WIDEST_IMPULSE_ - BLIP_SYNTH_QUALITY ) / 2;
  rev = fwd + BLIP_SYNTH_QUALITY - 2;

  BLIP_FWD( 0 );
  if( BLIP_SYNTH_QUALITY > 8 ) {
    BLIP_FWD( 2 );
  }
  if( BLIP_SYNTH_QUALITY > 12 ) {
    BLIP_FWD( 4 );
  }

  mid = BLIP_SYNTH_QUALITY / 2 - 1;
  t0 = i0 * delta + buf[fwd + mid - 1];
  t1 = imp[BLIP_RES * mid] * delta + buf[fwd + mid];
  imp = synth->impulses + phase;
  i0 = imp[BLIP_RES * mid];
  buf[fwd + mid - 1] = t0;
  buf[fwd + mid] = t1;

  if( BLIP_SYNTH_QUALITY > 12 ) {
    BLIP_REV( 6 );
  }
  if( BLIP_SYNTH_QUALITY > 8 ) {
    BLIP_REV( 4 );
  }
  BLIP_REV( 2 );

  t0 = i0 * delta + buf[rev];
  t1 = *imp * delta + buf[rev + 1];
  buf[rev] = t0;
  buf[rev + 1] = t1;
}

#undef BLIP_FWD
#undef BLIP_REV

void
blip_synth_update( Blip_Synth * synth, blip_time_t t, int amp )
{
  int delta = amp - synth->impl.last_amp;

  synth->impl.last_amp = amp;
  blip_synth_offset_resampled( synth,
                               t * synth->impl.buf->factor_ +
                               synth->impl.buf->offset_, delta,
                               synth->impl.buf );
}

int
_blip_synth_impulses_size( Blip_Synth_ * synth_ )
{
  return BLIP_RES / 2 * BLIP_SYNTH_WIDTH + 1;
}

void
blip_synth_set_treble_eq( Blip_Synth * synth, double treble )
{
  blip_eq_t eq = { 0.0, 0, 44100, 0 };

  eq.treble = treble;

  _blip_synth_treble_eq( &synth->impl, &eq );
}

#define BUFFER_EXTRA ( BLIP_WIDEST_IMPULSE_ + 2 )

static void
blip_buffer_init( Blip_Buffer * buff )
{
  buff->factor_ = LONG_MAX;
  buff->offset_ = 0;
  buff->buffer_ = NULL;
  buff->buffer_size_ = 0;
  buff->sample_rate_ = 0;
  buff->reader_accum = 0;
  buff->bass_shift = 0;
  buff->clock_rate_ = 0;
  buff->bass_freq_ = 16;
  buff->length_ = 0;
}

static void
blip_buffer_end( Blip_Buffer * buff )
{
  if( buff->buffer_ )
    free( buff->buffer_ );
  buff->buffer_ = NULL;
}

Blip_Buffer *
new_Blip_Buffer( void )
{
  Blip_Buffer *ret;

  ret = malloc( sizeof( Blip_Buffer ) );
  if( ret ) {
    blip_buffer_init( ret );
  }
  return ret;
}

void
delete_Blip_Buffer( Blip_Buffer ** buff )
{
  if( !*buff )
    return;

  blip_buffer_end( *buff );
  free( *buff );
  *buff = NULL;
}

static void
blip_synth_init( Blip_Synth * synth )
{
  synth->impulses =
    malloc( ( BLIP_RES * ( BLIP_SYNTH_QUALITY / 2 ) +
              1 ) * sizeof( imp_t ) * 4 );
  if( synth->impulses ) {
    _blip_synth_init( &synth->impl, ( short * )synth->impulses );       /* sorry, somewhere imp_t, somewhere short ???? */
  }
}

static void
blip_synth_end( Blip_Synth * synth )
{
  if( synth->impulses ) {
    free( synth->impulses );
    synth->impulses = NULL;
  }
}

Blip_Synth *
new_Blip_Synth( void )
{
  Blip_Synth *ret;

  ret = malloc( sizeof( Blip_Synth ) );
  if( ret ) {
    blip_synth_init( ret );
    if( !ret->impulses ) {
      free( ret );
      return NULL;
    }
  }
  return ret;
}

void
delete_Blip_Synth( Blip_Synth ** synth )
{
  if( !*synth )
    return;

  blip_synth_end( *synth );
  free( *synth );
  *synth = NULL;
}

void
blip_buffer_clear( Blip_Buffer * buff, int entire_buffer )
{
  buff->offset_ = 0;
  buff->reader_accum = 0;
  if( buff->buffer_ ) {
    long count =
      ( entire_buffer ? buff->
        buffer_size_ : blip_buffer_samples_avail( buff ) );
    memset( buff->buffer_, 0, ( count + BUFFER_EXTRA ) * sizeof( buf_t_ ) );
  }
}

blargg_err_t
blip_buffer_set_sample_rate( Blip_Buffer * buff, long new_rate, int msec )
{
  /* start with maximum length that resampled time can represent */
  long new_size = ( ULONG_MAX >> BLIP_BUFFER_ACCURACY ) - BUFFER_EXTRA - 64;

  if( msec != BLIP_MAX_LENGTH ) {
    long s = ( new_rate * ( msec + 1 ) + 999 ) / 1000;

    if( s < new_size )
      new_size = s;
  }

  if( buff->buffer_size_ != new_size ) {
    void *p =
      realloc( buff->buffer_,
               ( new_size + BUFFER_EXTRA ) * sizeof( buf_t_ ) );
    if( !p )
      return "Out of memory";
    buff->buffer_ = ( buf_t_ * ) p;
  }

  buff->buffer_size_ = new_size;

  /* update things based on the sample rate */
  buff->sample_rate_ = new_rate;
  buff->length_ = new_size * 1000 / new_rate - 1;
  if( buff->clock_rate_ )
    blip_buffer_set_clock_rate( buff, buff->clock_rate_ );
  blip_buffer_set_bass_freq( buff, buff->bass_freq_ );

  blip_buffer_clear( buff, BLIP_BUFFER_DEF_ENTIRE_BUFF );

  return 0;                     /*  success */
}

blip_resampled_time_t
blip_buffer_clock_rate_factor( Blip_Buffer * buff, long clock_rate )
{
  double ratio = ( double )buff->sample_rate_ / clock_rate;

  long factor = ( long )floor( ratio * ( 1L << BLIP_BUFFER_ACCURACY ) + 0.5 );

  return ( blip_resampled_time_t ) factor;
}

void
blip_buffer_set_bass_freq( Blip_Buffer * buff, int freq )
{
  int shift = 31;

  long f;

  buff->bass_freq_ = freq;
  if( freq > 0 ) {
    shift = 13;
    f = ( freq << 16 ) / buff->sample_rate_;
    while( ( f >>= 1 ) && --shift ) {};
  }

  buff->bass_shift = shift;
}

void
blip_buffer_end_frame( Blip_Buffer * buff, blip_time_t t )
{
  buff->offset_ += t * buff->factor_;
}

inline void
blip_buffer_remove_silence( Blip_Buffer * buff, long count )
{
  buff->offset_ -= ( blip_resampled_time_t ) count << BLIP_BUFFER_ACCURACY;
}

inline void
blip_buffer_remove_samples( Blip_Buffer * buff, long count )
{
  long remain;

  if( count ) {
    blip_buffer_remove_silence( buff, count );

    /*  copy remaining samples to beginning and clear old samples */
    remain = blip_buffer_samples_avail( buff ) + BUFFER_EXTRA;
    memmove( buff->buffer_, buff->buffer_ + count,
             remain * sizeof( buf_t_ ) );
    memset( buff->buffer_ + remain, 0, count * sizeof( buf_t_ ) );
  }
}

/*  Blip_Synth_ */

void
_blip_synth_init( Blip_Synth_ * synth_, short *p )
{
  synth_->impulses = p;
  synth_->volume_unit_ = 0.0;
  synth_->kernel_unit = 0;
  synth_->buf = NULL;
  synth_->last_amp = 0;
  synth_->delta_factor = 0;
}

#define PI 3.1415926535897932384626433832795029

static void
gen_sinc( float *out, int count, double oversample, double treble,
          double cutoff )
{
  int i;

  double maxh, rolloff, pow_a_n, to_angle;

  if( cutoff > 0.999 )
    cutoff = 0.999;
  if( treble < -300.0 )
    treble = -300.0;
  if( treble > 5.0 )
    treble = 5.0;

  maxh = 4096.0;
  rolloff = pow( 10.0, 1.0 / ( maxh * 20.0 ) * treble / ( 1.0 - cutoff ) );
  pow_a_n = pow( rolloff, maxh - maxh * cutoff );
  to_angle = PI / 2 / maxh / oversample;
  for( i = 0; i < count; i++ ) {
    double angle, c, cos_nc_angle, cos_nc1_angle, cos_angle, d, b, a;

    angle = ( ( i - count ) * 2 + 1 ) * to_angle;
    c = rolloff * cos( ( maxh - 1.0 ) * angle ) - cos( maxh * angle );
    cos_nc_angle = cos( maxh * cutoff * angle );
    cos_nc1_angle = cos( ( maxh * cutoff - 1.0 ) * angle );
    cos_angle = cos( angle );

    c = c * pow_a_n - rolloff * cos_nc1_angle + cos_nc_angle;
    d = 1.0 + rolloff * ( rolloff - cos_angle - cos_angle );
    b = 2.0 - cos_angle - cos_angle;
    a = 1.0 - cos_angle - cos_nc_angle + cos_nc1_angle;

    out[i] = ( float )( ( a * d + c * b ) / ( b * d ) );        /*  a / b + c / d */
  }
}

static void
blip_eq_generate( blip_eq_t * eq, float *out, int count )
{
  /* lower cutoff freq for narrow kernels with their wider transition band
     (8 points->1.49, 16 points->1.15) */
  int i;

  double to_fraction, cutoff;

  double oversample = BLIP_RES * 2.25 / count + 0.85;

  double half_rate = eq->sample_rate * 0.5;

  if( eq->cutoff_freq )
    oversample = half_rate / eq->cutoff_freq;

  cutoff = eq->rolloff_freq * oversample / half_rate;

  gen_sinc( out, count, BLIP_RES * oversample, eq->treble, cutoff );

  /* apply (half of) hamming window */
  to_fraction = PI / ( count - 1 );
  for( i = count; i--; )
    out[i] *= 0.54 - 0.46 * cos( i * to_fraction );

}

void
_blip_synth_adjust_impulse( Blip_Synth_ * synth_ )
{
  /* sum pairs for each phase and add error correction to end of first half */
  int size = _blip_synth_impulses_size( synth_ );

  int i, p, p2, error;

  for( p = BLIP_RES; p-- >= BLIP_RES / 2; ) {
    error = synth_->kernel_unit;
    p2 = BLIP_RES - 2 - p;
    for( i = 1; i < size; i += BLIP_RES ) {
      error -= synth_->impulses[i + p];
      error -= synth_->impulses[i + p2];
    }

    if( p == p2 )
      error /= 2;    /*  phase = 0.5 impulse uses same half for both sides */

    synth_->impulses[size - BLIP_RES + p] += error;
  }
}


void
_blip_synth_treble_eq( Blip_Synth_ * synth_, blip_eq_t * eq )
{
  double total, base_unit, rescale, sum, next, vol;

  int impulses_size, i;

  float fimpulse[BLIP_RES / 2 * ( BLIP_WIDEST_IMPULSE_ - 1 ) + BLIP_RES * 2];

  int half_size = BLIP_RES / 2 * ( BLIP_SYNTH_WIDTH - 1 );

  blip_eq_generate( eq, &fimpulse[BLIP_RES], half_size );

  /* need mirror slightly past center for calculation */
  for( i = BLIP_RES; i--; )
    fimpulse[BLIP_RES + half_size + i] =
      fimpulse[BLIP_RES + half_size - 1 - i];

  /* starts at 0 */
  for( i = 0; i < BLIP_RES; i++ )
    fimpulse[i] = 0.0f;

  /* find rescale factor */
  total = 0.0;
  for( i = 0; i < half_size; i++ )
    total += fimpulse[BLIP_RES + i];

/* double const base_unit = 44800.0 - 128 * 18;  allows treble up to +0 dB
   double const base_unit = 37888.0;  allows treble to +5 dB */
  base_unit = 32768.0;          /*  necessary for blip_unscaled to work */
  rescale = base_unit / 2 / total;
  synth_->kernel_unit = ( long )base_unit;

  /* integrate, first difference, rescale, convert to int */
  sum = 0.0;
  next = 0.0;
  impulses_size = _blip_synth_impulses_size( synth_ );

  for( i = 0; i < impulses_size; i++ ) {
    synth_->impulses[i] = ( short )floor( ( next - sum ) * rescale + 0.5 );
    sum += fimpulse[i];
    next += fimpulse[i + BLIP_RES];
  }

  _blip_synth_adjust_impulse( synth_ );

  /* volume might require rescaling */
  vol = synth_->volume_unit_;
  if( vol ) {
    synth_->volume_unit_ = 0.0;
    _blip_synth_volume_unit( synth_, vol );
  }
}

void
_blip_synth_volume_unit( Blip_Synth_ * synth_, double new_unit )
{
  if( new_unit != synth_->volume_unit_ ) {
    double factor;

    /* use default eq if it hasn't been set yet */
    if( !synth_->kernel_unit ) {
      blip_eq_t eq = { -8.0, 0, 44100, 0 };

      _blip_synth_treble_eq( synth_, &eq );
    }

    synth_->volume_unit_ = new_unit;
    factor = new_unit * ( 1L << BLIP_SAMPLE_BITS ) / synth_->kernel_unit;

    if( factor > 0.0 ) {
      int shift = 0;

      /* if unit is really small, might need to attenuate kernel */
      while( factor < 2.0 ) {
        shift++;
        factor *= 2.0;
      }

      if( shift ) {
        /* keep values positive to avoid round-towards-zero of sign-preserving
         * right shift for negative values */
        long offset = 0x8000 + ( 1 << ( shift - 1 ) );

        long offset2 = 0x8000 >> shift;

        int i;

        synth_->kernel_unit >>= shift;

        for( i = _blip_synth_impulses_size( synth_ ); i--; )
          synth_->impulses[i] =
            ( short )( ( ( synth_->impulses[i] + offset ) >> shift ) -
                       offset2 );

        _blip_synth_adjust_impulse( synth_ );
      }
    }
    synth_->delta_factor = ( int )floor( factor + 0.5 );
  }
}

long
blip_buffer_read_samples( Blip_Buffer * buff, blip_sample_t * out,
                          long max_samples, int stereo )
{
  long count = blip_buffer_samples_avail( buff );

  if( count > max_samples )
    count = max_samples;

  if( count ) {
    int sample_shift = BLIP_SAMPLE_BITS - 16;

    int my_bass_shift = buff->bass_shift;

    long accum = buff->reader_accum;

    buf_t_ *in = buff->buffer_;

    if( !stereo ) {
      int n;

      for( n = count; n--; ) {
        long s = accum >> sample_shift;

        accum -= accum >> my_bass_shift;
        accum += *in++;
        *out++ = ( blip_sample_t ) s;

        /* clamp sample */
        if( ( blip_sample_t ) s != s )
          out[-1] = ( blip_sample_t ) ( 0x7FFF - ( s >> 24 ) );
      }
    } else {
      int n;

      for( n = count; n--; ) {
        long s = accum >> sample_shift;

        accum -= accum >> my_bass_shift;
        accum += *in++;
        *out = ( blip_sample_t ) s;
        out += 2;

        /* clamp sample */
        if( ( blip_sample_t ) s != s )
          out[-2] = ( blip_sample_t ) ( 0x7FFF - ( s >> 24 ) );
      }
    }

    buff->reader_accum = accum;
    blip_buffer_remove_samples( buff, count );
  }

  return count;
}
