/* GSL - Generic Sound Layer
 * Copyright (C) 2001-2002 Stefan Westerfeld and Tim Janik
 *
 * 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.
 */
#ifndef __GSL_DATA_UTILS_H__
#define __GSL_DATA_UTILS_H__

#include <gsl/gslmath.h>
#include <gsl/gsldatahandle.h>

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */


/* --- structures --- */
#define GSL_DATA_HANDLE_PEEK_BUFFER     (8192)
typedef struct
{
  gint    dir;   /* initialize direction to -1 or +1 (or 0 for random access) */
  GslLong start; /* initialize to 0 */
  GslLong end;   /* initialize to 0 */
  gfloat  data[GSL_DATA_HANDLE_PEEK_BUFFER];
} GslDataPeekBuffer;
typedef struct
{
  GslLong head_skip;	/* FIXME: remove this */
  GslLong tail_cut;
  GslLong min_loop;
  GslLong max_loop;
} GslLoopSpec;	/* rename this to ...Data... */


/* --- data utils --- */
gboolean	gsl_data_detect_signal		(GslDataHandle		*handle,
						 GslLong		*sigstart,
						 GslLong		*sigend);
GslLong		gsl_data_find_sample		(GslDataHandle		*dhandle,
						 gfloat			 min_value,
						 gfloat			 max_value,
						 GslLong		 start_offset,
						 gint			 direction);
gboolean	gsl_data_find_tailmatch		(GslDataHandle		*dhandle,
						 const GslLoopSpec	*lspec,
						 GslLong		*loop_start_p,
						 GslLong		*loop_end_p);
GslLong		gsl_data_find_block		(GslDataHandle		*handle,
						 guint			 n_values,
						 const gfloat		*values,
						 gfloat			 epsilon);


/* --- data handle utils --- */
static inline gfloat gsl_data_handle_peek_value	(GslDataHandle		*dhandle,
						 GslLong		 position,
						 GslDataPeekBuffer	*peekbuf);
gint /* errno */     gsl_data_handle_dump	(GslDataHandle		*dhandle,
						 gint			 fd,
						 GslWaveFormatType	 format,
						 guint			 byte_order);
gint /* errno */     gsl_data_handle_dump_wav	(GslDataHandle		*dhandle,
						 gint			 fd,
						 guint			 n_bits,
						 guint			 n_channels,
						 guint			 sample_freq);


/* --- conversion utils --- */
static inline guint   gsl_conv_from_float	(GslWaveFormatType format,
						 guint             byte_order,
						 const gfloat     *src,
						 gpointer          dest,
						 guint             n_values);
static inline guint   gsl_conv_from_float_clip	(GslWaveFormatType format,
						 guint             byte_order,
						 const gfloat     *src,
						 gpointer          dest,
						 guint             n_values);
static inline void    gsl_conv_to_float		(GslWaveFormatType format,
						 guint             byte_order,
						 gconstpointer     src,
						 gfloat           *dest,
						 guint             n_values);
static inline guint   gsl_conv_from_double	(GslWaveFormatType format,
						 guint             byte_order,
						 const gdouble    *src,
						 gpointer          dest,
						 guint             n_values);
static inline guint   gsl_conv_from_double_clip	(GslWaveFormatType format,
						 guint             byte_order,
						 const gdouble    *src,
						 gpointer          dest,
						 guint             n_values);
static inline void    gsl_conv_to_double	(GslWaveFormatType format,
						 guint             byte_order,
						 gconstpointer     src,
						 gdouble          *dest,
						 guint             n_values);



/* --- misc implementations --- */
gfloat  gsl_data_peek_value_f   (GslDataHandle     *dhandle,
				 GslLong            pos,
				 GslDataPeekBuffer *peekbuf);

static inline gfloat
gsl_data_handle_peek_value (GslDataHandle     *dhandle,
			    GslLong	       position,
			    GslDataPeekBuffer *peekbuf)
{
  return (position >= peekbuf->start && position < peekbuf->end ?
	  peekbuf->data[position - peekbuf->start] :
	  gsl_data_peek_value_f (dhandle, position, peekbuf));
}

#define	GSL_CONV_FORMAT(format, endian_flag)	(((endian_flag) << 16) | ((format) & 0xffff))

static inline guint     /* returns number of bytes used in dest */
gsl_conv_from_float (GslWaveFormatType format,
                     guint             byte_order,
                     const gfloat     *src,
                     gpointer          dest,
                     guint             n_values)
{
  gint8 *i8 = (gint8*) dest;
  guint8 *u8 = (guint8*) dest;
  gint16 *i16 = (gint16*) dest;
  guint16 *u16 = (guint16*) dest;
  guint32 *u32dest = (guint32*) dest;
  const gfloat *bound = src + n_values;
  guint32 *u32src = (guint32*) src, *u32bound = (guint32*) bound;
  
  if (!n_values)
    return 0;
  
  switch (GSL_CONV_FORMAT (format, byte_order == G_BYTE_ORDER))
    {
      GslFpuState fpu;
      gfloat v;
      gint16 vi16;
      guint16 vu16;
      guint32 vu32;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_UNSIGNED_8, G_BYTE_ORDER == G_BYTE_ORDER):
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_UNSIGNED_8, G_BYTE_ORDER != G_BYTE_ORDER):
      do
        *u8++ = *src++ * 128. + 128.5;
      while (src < bound);
      return n_values;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_SIGNED_8, G_BYTE_ORDER == G_BYTE_ORDER):
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_SIGNED_8, G_BYTE_ORDER != G_BYTE_ORDER):
      gsl_fpu_setround (&fpu);
      do
        {
          v = *src++;
          v *= 128.;
          *i8++ = gsl_ftoi (v);
        }
      while (src < bound);
      gsl_fpu_restore (fpu);
      return n_values;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_UNSIGNED_12, G_BYTE_ORDER == G_BYTE_ORDER):
      do
        *u16++ = *src++ * 2048. + 2048.5;
      while (src < bound);
      return n_values << 1;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_UNSIGNED_12, G_BYTE_ORDER != G_BYTE_ORDER):
      do
        {
          vu16 = *src++ * 2048. + 2048.5;
          *u16++ = GUINT16_SWAP_LE_BE (vu16);
        }
      while (src < bound);
      return n_values << 1;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_SIGNED_12, G_BYTE_ORDER == G_BYTE_ORDER):
      gsl_fpu_setround (&fpu);
      do
        {
          v = *src++;
          v *= 2048.;
          *i16++ = gsl_ftoi (v);
        }
      while (src < bound);
      gsl_fpu_restore (fpu);
      return n_values << 1;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_SIGNED_12, G_BYTE_ORDER != G_BYTE_ORDER):
      gsl_fpu_setround (&fpu);
      do
        {
          v = *src++;
          v *= 2048.;
          vi16 = gsl_ftoi (v);
          *i16++ = GUINT16_SWAP_LE_BE (vi16);
        }
      while (src < bound);
      gsl_fpu_restore (fpu);
      return n_values << 1;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_UNSIGNED_16, G_BYTE_ORDER == G_BYTE_ORDER):
      do
        *u16++ = *src++ * 32768. + 32768.5;
      while (src < bound);
      return n_values << 1;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_UNSIGNED_16, G_BYTE_ORDER != G_BYTE_ORDER):
      do
        {
          vu16 = *src++ * 32768. + 32768.5;
          *u16++ = GUINT16_SWAP_LE_BE (vu16);
        }
      while (src < bound);
      return n_values << 1;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_SIGNED_16, G_BYTE_ORDER == G_BYTE_ORDER):
      gsl_fpu_setround (&fpu);
      do
        {
          v = *src++;
	  v *= 32768.;
          *i16++ = gsl_ftoi (v);
        }
      while (src < bound);
      gsl_fpu_restore (fpu);
      return n_values << 1;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_SIGNED_16, G_BYTE_ORDER != G_BYTE_ORDER):
      gsl_fpu_setround (&fpu);
      do
        {
          v = *src++;
          v *= 32768.;
          vi16 = gsl_ftoi (v);
          *i16++ = GUINT16_SWAP_LE_BE (vi16);
        }
      while (src < bound);
      gsl_fpu_restore (fpu);
      return n_values << 1;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_FLOAT, G_BYTE_ORDER == G_BYTE_ORDER):
      return n_values << 2;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_FLOAT, G_BYTE_ORDER != G_BYTE_ORDER):
      do
        {
          vu32 = *u32src++;
          *u32dest++ = GUINT32_SWAP_LE_BE (vu32);
        }
      while (u32src < u32bound);
      return n_values << 2;
    default:
      g_assert_not_reached ();
      return 0;
    }
}

static inline guint     /* returns number of bytes used in dest */
gsl_conv_from_float_clip (GslWaveFormatType format,
                          guint             byte_order,
                          const gfloat     *src,
                          gpointer          dest,
                          guint             n_values)
{
  gint8 *i8 = (gint8*) dest;
  guint8 *u8 = (guint8*) dest;
  gint16 *i16 = (gint16*) dest;
  guint16 *u16 = (guint16*) dest;
  guint32 *u32dest = (guint32*) dest;
  const gfloat *bound = src + n_values;
  guint32 *u32src = (guint32*) src, *u32bound = (guint32*) bound;
  
  if (!n_values)
    return 0;
  
  switch (GSL_CONV_FORMAT (format, byte_order == G_BYTE_ORDER))
    {
      GslFpuState fpu;
      gfloat v;
      guint32 vu32;
      gint32 vi32;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_UNSIGNED_8, G_BYTE_ORDER == G_BYTE_ORDER):
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_UNSIGNED_8, G_BYTE_ORDER != G_BYTE_ORDER):
      do
        {
          vi32 = *src++ * 128. + 128.5;
          *u8++ = CLAMP (vi32, 0, 255);
        }
      while (src < bound);
      return n_values;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_SIGNED_8, G_BYTE_ORDER == G_BYTE_ORDER):
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_SIGNED_8, G_BYTE_ORDER != G_BYTE_ORDER):
      gsl_fpu_setround (&fpu);
      do
        {
          v = *src++;
          v *= 128.;
          vi32 = gsl_ftoi (v);
          *i8++ = CLAMP (vi32, -128, 127);
        }
      while (src < bound);
      gsl_fpu_restore (fpu);
      return n_values;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_UNSIGNED_12, G_BYTE_ORDER == G_BYTE_ORDER):
      do
        {
          vi32 = *src++ * 2048. + 2048.5;
          *u16++ = CLAMP (vi32, 0, 4095);
        }
      while (src < bound);
      return n_values << 1;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_UNSIGNED_12, G_BYTE_ORDER != G_BYTE_ORDER):
      do
        {
          vi32 = *src++ * 2048. + 2048.5;
          vi32 = CLAMP (vi32, 0, 4095);
          *u16++ = GUINT16_SWAP_LE_BE (vi32);
        }
      while (src < bound);
      return n_values << 1;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_SIGNED_12, G_BYTE_ORDER == G_BYTE_ORDER):
      gsl_fpu_setround (&fpu);
      do
        {
          v = *src++;
          v *= 2048.;
          vi32 = gsl_ftoi (v);
          *i16++ = CLAMP (vi32, -2048, 2047);
        }
      while (src < bound);
      gsl_fpu_restore (fpu);
      return n_values << 1;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_SIGNED_12, G_BYTE_ORDER != G_BYTE_ORDER):
      gsl_fpu_setround (&fpu);
      do
        {
          v = *src++;
          v *= 2048.;
          vi32 = gsl_ftoi (v);
          vi32 = CLAMP (vi32, -2048, 2047);
          *i16++ = GUINT16_SWAP_LE_BE (vi32);
        }
      while (src < bound);
      gsl_fpu_restore (fpu);
      return n_values << 1;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_UNSIGNED_16, G_BYTE_ORDER == G_BYTE_ORDER):
      do
        {
          vi32 = *src++ * 32768. + 32768.5;
          *u16++ = CLAMP (vi32, 0, 65535);
        }
      while (src < bound);
      return n_values << 1;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_UNSIGNED_16, G_BYTE_ORDER != G_BYTE_ORDER):
      do
        {
          vi32 = *src++ * 32768. + 32768.5;
          vi32 = CLAMP (vi32, 0, 65535);
          *u16++ = GUINT16_SWAP_LE_BE (vi32);
        }
      while (src < bound);
      return n_values << 1;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_SIGNED_16, G_BYTE_ORDER == G_BYTE_ORDER):
      gsl_fpu_setround (&fpu);
      do
        {
          v = *src++;
          v *= 32768.;
          vi32 = gsl_ftoi (v);
          vi32 = CLAMP (vi32, -32768, 32767);
          *i16++ = vi32;
        }
      while (src < bound);
      gsl_fpu_restore (fpu);
      return n_values << 1;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_SIGNED_16, G_BYTE_ORDER != G_BYTE_ORDER):
      gsl_fpu_setround (&fpu);
      do
        {
          v = *src++;
          v *= 32768.;
          vi32 = gsl_ftoi (v);
          vi32 = CLAMP (vi32, -32768, 32767);
          *i16++ = GUINT16_SWAP_LE_BE (vi32);
        }
      while (src < bound);
      gsl_fpu_restore (fpu);
      return n_values << 1;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_FLOAT, G_BYTE_ORDER == G_BYTE_ORDER):
      return n_values << 2;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_FLOAT, G_BYTE_ORDER != G_BYTE_ORDER):
      do
        {
          vu32 = *u32src++;
          *u32dest++ = GUINT32_SWAP_LE_BE (vu32);
        }
      while (u32src < u32bound);
      return n_values << 2;
    default:
      g_assert_not_reached ();
      return 0;
    }
}

static inline void
gsl_conv_to_float (GslWaveFormatType format,
                   guint             byte_order,
                   gconstpointer     src,
                   gfloat           *dest,
                   guint             n_values)
{
  guint8 *u8 = (guint8*) src;
  gint8 *i8 = (gint8*) src;
  guint16 *u16 = (guint16*) src;
  gint16 *i16 = (gint16*) src;
  guint32 *u32src = (guint32*) src;
  gfloat *bound = dest + n_values;
  guint32 *u32dest = (guint32*) dest, *u32bound = (guint32*) bound;
  
  if (!n_values)
    return;
  
  switch (GSL_CONV_FORMAT (format, byte_order == G_BYTE_ORDER))
    {
      gint16 vi16;
      guint16 vu16;
      guint32 vu32;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_UNSIGNED_8, G_BYTE_ORDER == G_BYTE_ORDER):
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_UNSIGNED_8, G_BYTE_ORDER != G_BYTE_ORDER):
      do
        *dest++ = (*u8++ - 128) * (1. / 128.);
      while (dest < bound);
      break;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_SIGNED_8, G_BYTE_ORDER == G_BYTE_ORDER):
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_SIGNED_8, G_BYTE_ORDER != G_BYTE_ORDER):
      do
        *dest++ = *i8++ * (1. / 128.);
      while (dest < bound);
      break;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_UNSIGNED_12, G_BYTE_ORDER == G_BYTE_ORDER):
      do
        *dest++ = ((*u16++ & 0x0fff) - 2048) * (1. / 2048.);
      while (dest < bound);
      break;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_UNSIGNED_12, G_BYTE_ORDER != G_BYTE_ORDER):
      do
        {
          vu16 = *u16++;
          *dest++ = ((GUINT16_SWAP_LE_BE (vu16) & 0x0fff) - 2048) * (1. / 2048.);
        }
      while (dest < bound);
      break;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_SIGNED_12, G_BYTE_ORDER == G_BYTE_ORDER):
      do
        {
          vi16 = *i16++;
          *dest++ = CLAMP (vi16, -2048, 2048) * (1. / 2048.);
        }
      while (dest < bound);
      break;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_SIGNED_12, G_BYTE_ORDER != G_BYTE_ORDER):
      do
        {
          vi16 = *i16++;
          vi16 = GUINT16_SWAP_LE_BE (vi16);
          *dest++ = CLAMP (vi16, -2048, 2048) * (1. / 2048.);
        }
      while (dest < bound);
      break;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_UNSIGNED_16, G_BYTE_ORDER == G_BYTE_ORDER):
      do
        *dest++ = (*u16++ - 32768) * (1. / 32768.);
      while (dest < bound);
      break;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_UNSIGNED_16, G_BYTE_ORDER != G_BYTE_ORDER):
      do
        {
          vu16 = *u16++;
          *dest++ = (GUINT16_SWAP_LE_BE (vu16) - 32768) * (1. / 32768.);
        }
      while (dest < bound);
      break;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_SIGNED_16, G_BYTE_ORDER == G_BYTE_ORDER):
      do
        *dest++ = *i16++ * (1. / 32768.);
      while (dest < bound);
      break;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_SIGNED_16, G_BYTE_ORDER != G_BYTE_ORDER):
      do
        {
          vi16 = *i16++;
          *dest++ = GUINT16_SWAP_LE_BE (vi16) * (1. / 32768.);
        }
      while (dest < bound);
      break;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_FLOAT, G_BYTE_ORDER == G_BYTE_ORDER):
      break;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_FLOAT, G_BYTE_ORDER != G_BYTE_ORDER):
      do
        {
          vu32 = *u32src++;
          *u32dest++ = GUINT32_SWAP_LE_BE (vu32);
        }
      while (u32dest < u32bound);
      break;
    default:
      g_assert_not_reached ();
    }
}

/* same as above with s/float/double */
static inline guint     /* returns number of bytes used in dest */
gsl_conv_from_double (GslWaveFormatType format,
                      guint             byte_order,
                      const gdouble    *src,
                      gpointer          dest,
                      guint             n_values)
{
  gint8 *i8 = (gint8*) dest;
  guint8 *u8 = (guint8*) dest;
  gint16 *i16 = (gint16*) dest;
  guint16 *u16 = (guint16*) dest;
  guint32 *u32dest = (guint32*) dest;
  const gdouble *bound = src + n_values;
  guint32 *u32src = (guint32*) src, *u32bound = (guint32*) bound;
  
  if (!n_values)
    return 0;
  
  switch (GSL_CONV_FORMAT (format, byte_order == G_BYTE_ORDER))
    {
      GslFpuState fpu;
      gdouble v;
      gint16 vi16;
      guint16 vu16;
      guint32 vu32;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_UNSIGNED_8, G_BYTE_ORDER == G_BYTE_ORDER):
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_UNSIGNED_8, G_BYTE_ORDER != G_BYTE_ORDER):
      do
        *u8++ = *src++ * 128. + 128.5;
      while (src < bound);
      return n_values;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_SIGNED_8, G_BYTE_ORDER == G_BYTE_ORDER):
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_SIGNED_8, G_BYTE_ORDER != G_BYTE_ORDER):
      gsl_fpu_setround (&fpu);
      do
        {
          v = *src++;
          v *= 128.;
          *i8++ = gsl_ftoi (v);
        }
      while (src < bound);
      gsl_fpu_restore (fpu);
      return n_values;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_UNSIGNED_12, G_BYTE_ORDER == G_BYTE_ORDER):
      do
        *u16++ = *src++ * 2048. + 2048.5;
      while (src < bound);
      return n_values << 1;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_UNSIGNED_12, G_BYTE_ORDER != G_BYTE_ORDER):
      do
        {
          vu16 = *src++ * 2048. + 2048.5;
          *u16++ = GUINT16_SWAP_LE_BE (vu16);
        }
      while (src < bound);
      return n_values << 1;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_SIGNED_12, G_BYTE_ORDER == G_BYTE_ORDER):
      gsl_fpu_setround (&fpu);
      do
        {
          v = *src++;
          v *= 2048.;
          *i16++ = gsl_ftoi (v);
        }
      while (src < bound);
      gsl_fpu_restore (fpu);
      return n_values << 1;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_SIGNED_12, G_BYTE_ORDER != G_BYTE_ORDER):
      gsl_fpu_setround (&fpu);
      do
        {
          v = *src++;
          v *= 2048.;
          vi16 = gsl_ftoi (v);
          *i16++ = GUINT16_SWAP_LE_BE (vi16);
        }
      while (src < bound);
      gsl_fpu_restore (fpu);
      return n_values << 1;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_UNSIGNED_16, G_BYTE_ORDER == G_BYTE_ORDER):
      do
        *u16++ = *src++ * 32768. + 32768.5;
      while (src < bound);
      return n_values << 1;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_UNSIGNED_16, G_BYTE_ORDER != G_BYTE_ORDER):
      do
        {
          vu16 = *src++ * 32768. + 32768.5;
          *u16++ = GUINT16_SWAP_LE_BE (vu16);
        }
      while (src < bound);
      return n_values << 1;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_SIGNED_16, G_BYTE_ORDER == G_BYTE_ORDER):
      gsl_fpu_setround (&fpu);
      do
        {
          v = *src++;
          v *= 32768.;
          *i16++ = gsl_ftoi (v);
        }
      while (src < bound);
      gsl_fpu_restore (fpu);
      return n_values << 1;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_SIGNED_16, G_BYTE_ORDER != G_BYTE_ORDER):
      gsl_fpu_setround (&fpu);
      do
        {
          v = *src++;
          v *= 32768.;
          vi16 = gsl_ftoi (v);
          *i16++ = GUINT16_SWAP_LE_BE (vi16);
        }
      while (src < bound);
      gsl_fpu_restore (fpu);
      return n_values << 1;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_FLOAT, G_BYTE_ORDER == G_BYTE_ORDER):
      return n_values << 2;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_FLOAT, G_BYTE_ORDER != G_BYTE_ORDER):
      do
        {
          vu32 = *u32src++;
          *u32dest++ = GUINT32_SWAP_LE_BE (vu32);
        }
      while (u32src < u32bound);
      return n_values << 2;
    default:
      g_assert_not_reached ();
      return 0;
    }
}

static inline guint     /* returns number of bytes used in dest */
gsl_conv_from_double_clip (GslWaveFormatType format,
			   guint             byte_order,
			   const gdouble    *src,
			   gpointer          dest,
			   guint             n_values)
{
  gint8 *i8 = (gint8*) dest;
  guint8 *u8 = (guint8*) dest;
  gint16 *i16 = (gint16*) dest;
  guint16 *u16 = (guint16*) dest;
  guint32 *u32dest = (guint32*) dest;
  const gdouble *bound = src + n_values;
  guint32 *u32src = (guint32*) src, *u32bound = (guint32*) bound;
  
  if (!n_values)
    return 0;
  
  switch (GSL_CONV_FORMAT (format, byte_order == G_BYTE_ORDER))
    {
      GslFpuState fpu;
      gdouble v;
      guint32 vu32;
      gint32 vi32;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_UNSIGNED_8, G_BYTE_ORDER == G_BYTE_ORDER):
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_UNSIGNED_8, G_BYTE_ORDER != G_BYTE_ORDER):
      do
        {
          vi32 = *src++ * 128. + 128.5;
          *u8++ = CLAMP (vi32, 0, 255);
        }
      while (src < bound);
      return n_values;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_SIGNED_8, G_BYTE_ORDER == G_BYTE_ORDER):
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_SIGNED_8, G_BYTE_ORDER != G_BYTE_ORDER):
      gsl_fpu_setround (&fpu);
      do
        {
          v = *src++;
          v *= 128.;
          vi32 = gsl_ftoi (v);
          *i8++ = CLAMP (vi32, -128, 127);
        }
      while (src < bound);
      gsl_fpu_restore (fpu);
      return n_values;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_UNSIGNED_12, G_BYTE_ORDER == G_BYTE_ORDER):
      do
        {
          vi32 = *src++ * 2048. + 2048.5;
          *u16++ = CLAMP (vi32, 0, 4095);
        }
      while (src < bound);
      return n_values << 1;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_UNSIGNED_12, G_BYTE_ORDER != G_BYTE_ORDER):
      do
        {
          vi32 = *src++ * 2048. + 2048.5;
          vi32 = CLAMP (vi32, 0, 4095);
          *u16++ = GUINT16_SWAP_LE_BE (vi32);
        }
      while (src < bound);
      return n_values << 1;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_SIGNED_12, G_BYTE_ORDER == G_BYTE_ORDER):
      gsl_fpu_setround (&fpu);
      do
        {
          v = *src++;
          v *= 2048.;
          vi32 = gsl_ftoi (v);
          *i16++ = CLAMP (vi32, -2048, 2047);
        }
      while (src < bound);
      gsl_fpu_restore (fpu);
      return n_values << 1;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_SIGNED_12, G_BYTE_ORDER != G_BYTE_ORDER):
      gsl_fpu_setround (&fpu);
      do
        {
          v = *src++;
          v *= 2048.;
          vi32 = gsl_ftoi (v);
          vi32 = CLAMP (vi32, -2048, 2047);
          *i16++ = GUINT16_SWAP_LE_BE (vi32);
        }
      while (src < bound);
      gsl_fpu_restore (fpu);
      return n_values << 1;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_UNSIGNED_16, G_BYTE_ORDER == G_BYTE_ORDER):
      do
        {
          vi32 = *src++ * 32768. + 32768.5;
          *u16++ = CLAMP (vi32, 0, 65535);
        }
      while (src < bound);
      return n_values << 1;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_UNSIGNED_16, G_BYTE_ORDER != G_BYTE_ORDER):
      do
        {
          vi32 = *src++ * 32768. + 32768.5;
          vi32 = CLAMP (vi32, 0, 65535);
          *u16++ = GUINT16_SWAP_LE_BE (vi32);
        }
      while (src < bound);
      return n_values << 1;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_SIGNED_16, G_BYTE_ORDER == G_BYTE_ORDER):
      gsl_fpu_setround (&fpu);
      do
        {
          v = *src++;
          v *= 32768.;
          vi32 = gsl_ftoi (v);
          vi32 = CLAMP (vi32, -32768, 32767);
          *i16++ = vi32;
        }
      while (src < bound);
      gsl_fpu_restore (fpu);
      return n_values << 1;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_SIGNED_16, G_BYTE_ORDER != G_BYTE_ORDER):
      gsl_fpu_setround (&fpu);
      do
        {
          v = *src++;
          v *= 32768.;
          vi32 = gsl_ftoi (v);
          vi32 = CLAMP (vi32, -32768, 32767);
          *i16++ = GUINT16_SWAP_LE_BE (vi32);
        }
      while (src < bound);
      gsl_fpu_restore (fpu);
      return n_values << 1;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_FLOAT, G_BYTE_ORDER == G_BYTE_ORDER):
      return n_values << 2;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_FLOAT, G_BYTE_ORDER != G_BYTE_ORDER):
      do
        {
          vu32 = *u32src++;
          *u32dest++ = GUINT32_SWAP_LE_BE (vu32);
        }
      while (u32src < u32bound);
      return n_values << 2;
    default:
      g_assert_not_reached ();
      return 0;
    }
}

static inline void
gsl_conv_to_double (GslWaveFormatType format,
		    guint             byte_order,
		    gconstpointer     src,
		    gdouble          *dest,
		    guint             n_values)
{
  guint8 *u8 = (guint8*) src;
  gint8 *i8 = (gint8*) src;
  guint16 *u16 = (guint16*) src;
  gint16 *i16 = (gint16*) src;
  guint32 *u32src = (guint32*) src;
  gdouble *bound = dest + n_values;
  guint32 *u32dest = (guint32*) dest, *u32bound = (guint32*) bound;
  
  if (!n_values)
    return;
  
  switch (GSL_CONV_FORMAT (format, byte_order == G_BYTE_ORDER))
    {
      gint16 vi16;
      guint16 vu16;
      guint32 vu32;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_UNSIGNED_8, G_BYTE_ORDER == G_BYTE_ORDER):
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_UNSIGNED_8, G_BYTE_ORDER != G_BYTE_ORDER):
      do
        *dest++ = (*u8++ - 128) * (1. / 128.);
      while (dest < bound);
      break;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_SIGNED_8, G_BYTE_ORDER == G_BYTE_ORDER):
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_SIGNED_8, G_BYTE_ORDER != G_BYTE_ORDER):
      do
        *dest++ = *i8++ * (1. / 128.);
      while (dest < bound);
      break;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_UNSIGNED_12, G_BYTE_ORDER == G_BYTE_ORDER):
      do
        *dest++ = ((*u16++ & 0x0fff) - 2048) * (1. / 2048.);
      while (dest < bound);
      break;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_UNSIGNED_12, G_BYTE_ORDER != G_BYTE_ORDER):
      do
        {
          vu16 = *u16++;
          *dest++ = ((GUINT16_SWAP_LE_BE (vu16) & 0x0fff) - 2048) * (1. / 2048.);
        }
      while (dest < bound);
      break;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_SIGNED_12, G_BYTE_ORDER == G_BYTE_ORDER):
      do
        {
          vi16 = *i16++;
          *dest++ = CLAMP (vi16, -2048, 2048) * (1. / 2048.);
        }
      while (dest < bound);
      break;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_SIGNED_12, G_BYTE_ORDER != G_BYTE_ORDER):
      do
        {
          vi16 = *i16++;
          vi16 = GUINT16_SWAP_LE_BE (vi16);
          *dest++ = CLAMP (vi16, -2048, 2048) * (1. / 2048.);
        }
      while (dest < bound);
      break;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_UNSIGNED_16, G_BYTE_ORDER == G_BYTE_ORDER):
      do
        *dest++ = (*u16++ - 32768) * (1. / 32768.);
      while (dest < bound);
      break;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_UNSIGNED_16, G_BYTE_ORDER != G_BYTE_ORDER):
      do
        {
          vu16 = *u16++;
          *dest++ = (GUINT16_SWAP_LE_BE (vu16) - 32768) * (1. / 32768.);
        }
      while (dest < bound);
      break;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_SIGNED_16, G_BYTE_ORDER == G_BYTE_ORDER):
      do
        *dest++ = *i16++ * (1. / 32768.);
      while (dest < bound);
      break;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_SIGNED_16, G_BYTE_ORDER != G_BYTE_ORDER):
      do
        {
          vi16 = *i16++;
          *dest++ = GUINT16_SWAP_LE_BE (vi16) * (1. / 32768.);
        }
      while (dest < bound);
      break;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_FLOAT, G_BYTE_ORDER == G_BYTE_ORDER):
      break;
    case GSL_CONV_FORMAT (GSL_WAVE_FORMAT_FLOAT, G_BYTE_ORDER != G_BYTE_ORDER):
      do
        {
          vu32 = *u32src++;
          *u32dest++ = GUINT32_SWAP_LE_BE (vu32);
        }
      while (u32dest < u32bound);
      break;
    default:
      g_assert_not_reached ();
    }
}


#ifdef __cplusplus
}
#endif /* __cplusplus */

#endif /* __GSL_DATA_UTILS_H__ */

/* vim:set ts=8 sts=2 sw=2: */
