/* BSE Resampling Datahandles
 * Copyright (C) 2001-2003 Tim Janik
 * Copyright (C) 2006 Stefan Westerfeld
 *
 * This library 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 of the License, or (at your option) any later version.
 *
 * This library 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 library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, MA 02111-1307, USA.
 */

/* 
 * halfband FIR filter for factor 2 resampling, created with octave
 *
 * design method: windowed sinc,  using ultraspherical window
 *
 *   coefficients = 32
 *             x0 = 1.01065
 *          alpha = 0.75
 *
 * design criteria (44100 Hz => 88200 Hz):
 * 
 *       passband = [     0, 18000 ]  1 - 2^-16 <= H(z) <= 1+2^-16
 *     transition = [ 18000, 26100 ]
 *       stopband = [ 26100, 44100 ]  | H(z) | <= -96 dB
 *
 * and for 48 kHz => 96 kHz:
 *
 *       passband = [     0, 19589 ]  1 - 2^-16 <= H(z) <= 1+2^-16
 *     transition = [ 19588, 29386 ]
 *       stopband = [ 29386, 48000 ]  | H(z) | <= -96 dB
 *
 * in order to keep the coefficient number down to 32, the filter
 * does only "almost" fulfill the spec, but its really really close
 * (no stopband ripple > -95 dB)
 */

const double halfband_fir_upsample2_96db_coeffs[32] =
{
  -3.48616530828033e-05,
   0.000112877490936198,
  -0.000278961878372482,
   0.000590495306376081,
  -0.00112566995029848,
   0.00198635062559427,
  -0.00330178798332932,
   0.00523534239035401,
  -0.00799905465189065,
   0.0118867161189188,
  -0.0173508611368417,
   0.0251928452706978,
  -0.0370909694665106,
   0.057408291607388,
  -0.102239638342325,
   0.317002929635456,
   /* here, a 0.5 coefficient will be used */
   0.317002929635456,
  -0.102239638342325,
   0.0574082916073878,
  -0.0370909694665105,
   0.0251928452706976,
  -0.0173508611368415,
   0.0118867161189186,
  -0.00799905465189052,
   0.0052353423903539,
  -0.00330178798332923,
   0.00198635062559419,
  -0.00112566995029842,
   0.000590495306376034,
  -0.00027896187837245,
   0.000112877490936177,
  -3.48616530827983e-05
};

#include "bsedatahandle-resample.h"
#include "bsessefir.hh"
#include "gsldatahandle.h"
#include <sfi/sficxx.hh>
#include <vector>

namespace Bse
{

using std::vector;
using SseFir::Upsampler2;

class DataHandleUpsample2 : public GslDataHandle,
			    public Sfi::GNewable /* 0 initialization */
{
  GslDataHandle		     *src_handle;

public:
  gboolean		      init_ok;
  vector<Upsampler2<32,1> *>  upsamplers;
  vector<int64>		      pcm_offsets;
  static const int            FILTER_DELAY = 18;

  DataHandleUpsample2 (GslDataHandle *src_handle)
    : src_handle (src_handle),
      init_ok (false)
  {
    g_return_if_fail (src_handle != NULL);
  
    init_ok = gsl_data_handle_common_init (this, NULL);
    if (init_ok)
      {
	name = g_strconcat (src_handle->name, "// #upsample2 /", NULL);
	src_handle = gsl_data_handle_ref (src_handle);
      }
  }

  ~DataHandleUpsample2()
  {
    gsl_data_handle_unref (src_handle);
    gsl_data_handle_common_free (this);
  }

  BseErrorType
  open (GslDataHandleSetup *setup)
  {
    BseErrorType error = gsl_data_handle_open (src_handle);
    if (error != BSE_ERROR_NONE)
      return error;

    *setup = src_handle->setup; /* copies setup.xinfos by pointer */
    setup->mix_freq *= 2.0;
    setup->n_values *= 2;

    const int T = 32;

    /* FIXME: should that loop go into upsampler init? but then we loose precision? */
    float utaps[T];
    for (int i = 0; i < T; i++)
      utaps[i] = halfband_fir_upsample2_96db_coeffs[i] * 2;

    for (guint i = 0; i < setup->n_channels; i++)
      {
	upsamplers.push_back (new Upsampler2<T,1> (utaps));
	pcm_offsets.push_back (-FILTER_DELAY);
      }

    return BSE_ERROR_NONE;
  }

  int64
  src_read (int64 voffset,
	    int64 n_values,
	    gfloat *values)
  {
    voffset += FILTER_DELAY - 1;  /* filter shift */
    int64 left = n_values;
    do
      {
	int64 l;
	if (voffset < 0) /* FIXME: maybe bound this more exactly */
	  {
	    *values = 0;
	    l = 1;
	  }
	else if (voffset >= src_handle->setup.n_values)
	  {
	    *values = 0;
	    l = 1;
	  }
	else
	  {
	    l = gsl_data_handle_read (src_handle, voffset, std::min (left, src_handle->setup.n_values - voffset), values);
	  }

	if (l < 0)
	  return l;	/* pass on errors */

	voffset += l;
	left -= l;
	values += l;
      }
    while (left > 0);

    return n_values;
  }

  int64
  read (int64          voffset,
	int64          n_values,
	gfloat        *values)
  {
    g_return_val_if_fail (setup.n_channels == 1, 0);

    /*
     * if we're somewhat before the actual read request, we'll simply skip the data
     * we don't need and then perform the read
     */
    if (pcm_offsets[0] <= voffset && pcm_offsets[0] + 64 > voffset)
      {
	while (pcm_offsets[0] != voffset)
	  {
	    float in, out[2]; /* FIXME: efficiency */

	    int64 l = src_read (pcm_offsets[0], 1, &in);
	    if (l < 0)
	      return l; /* pass on errors */

	    upsamplers[0]->process_block(&in, 1, out);
	    pcm_offsets[0] += l * 2;
	    printf ("skipping -> pcm_offsets[0] = %lld, voffset = %lld\n", pcm_offsets[0], voffset);
	  }
      }

    assert (voffset == pcm_offsets[0]);
    assert (n_values % 2 == 0);

    float in[n_values / 2];
    float out[n_values];

    src_read (voffset / 2, n_values / 2, in);
    upsamplers[0]->process_block (in, n_values / 2, values);

    pcm_offsets[0] += n_values;
    return n_values;
  }

  void
  close()
  {
    setup.xinfos = NULL;	/* cleanup pointer reference */
    gsl_data_handle_close (src_handle);
  }
};

} // namespace Bse

using namespace Bse;

static BseErrorType
dh_upsample2_open (GslDataHandle *dhandle, GslDataHandleSetup *setup)
{
  return reinterpret_cast<DataHandleUpsample2 *> (dhandle)->open (setup);
}

static void
dh_upsample2_close (GslDataHandle *dhandle)
{
  reinterpret_cast<DataHandleUpsample2 *> (dhandle)->close();
}


static void
dh_upsample2_destroy (GslDataHandle *dhandle)
{
  delete reinterpret_cast<DataHandleUpsample2 *> (dhandle);
}

static int64
dh_upsample2_read (GslDataHandle *dhandle,
		     int64          voffset,
		     int64          n_values,
		     gfloat        *values)
{
  return reinterpret_cast<DataHandleUpsample2 *> (dhandle)->read (voffset, n_values, values);
}

GslDataHandle*
bse_data_handle_new_upsample2 (GslDataHandle *src_handle)
{
  static GslDataHandleFuncs dh_upsample2_vtable = {
    dh_upsample2_open,
    dh_upsample2_read,
    dh_upsample2_close,
    NULL,
    dh_upsample2_destroy,
  };
  DataHandleUpsample2 *dhandle = new DataHandleUpsample2 (src_handle);
  if (dhandle->init_ok)
    {
      dhandle->vtable = &dh_upsample2_vtable;
      return dhandle;
    }
  else
    {
      delete dhandle;
      return NULL;
    }
}
