/* Resampling library
 * Copyright (C) <2001> David A. Schleef <ds@schleef.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library 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.
 */


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

/*#include <ml.h> */
#include <resample.h>



#define short_to_double_table
/*#define short_to_double_altivec */
#define short_to_double_unroll

#ifdef short_to_double_table
static float ints_high[256];
static float ints_low[256];

void conv_double_short_table(double *dest, short *src, int n)
{
	static int init = 0;
	int i;
	unsigned int idx;
	if(!init){
		for(i=0;i<256;i++){
			ints_high[i]=256.0*((i<128)?i:i-256);
			ints_low[i]=i;
		}
		init = 1;
	}

	if(n&1){
		idx = (unsigned short)*src++;
		*dest++ = ints_high[(idx>>8)] + ints_low[(idx&0xff)];
		n-=1;
	}
	for(i=0;i<n;i+=2){
		idx = (unsigned short)*src++;
		*dest++ = ints_high[(idx>>8)] + ints_low[(idx&0xff)];
		idx = (unsigned short)*src++;
		*dest++ = ints_high[(idx>>8)] + ints_low[(idx&0xff)];
	}
}

#endif

#ifdef short_to_double_unroll
void conv_double_short_unroll(double *dest, short *src, int n)
{
	if(n&1){
		*dest++ = *src++;
		n--;
	}
	if(n&2){
		*dest++ = *src++;
		*dest++ = *src++;
		n-=2;
	}
	while(n>0){
		*dest++ = *src++;
		*dest++ = *src++;
		*dest++ = *src++;
		*dest++ = *src++;
		n-=4;
	}
}
#endif

void conv_double_short_ref(double *dest, short *src, int n)
{
	int i;
	for(i=0;i<n;i++){
		dest[i]=src[i];
	}
}

#ifdef HAVE_CPU_PPC
#if 0
static union { int i[4]; float f[4]; } av_tmp __attribute__ ((__aligned__ (16)));

void conv_double_short_altivec(double *dest, short *src, int n)
{
	int i;

	for(i=0;i<n;i+=4){
		av_tmp.i[0] = src[0];
		av_tmp.i[1] = src[1];
		av_tmp.i[2] = src[2];
		av_tmp.i[3] = src[3];

		asm(
		"	lvx 0,0,%0\n"
		"	vcfsx 1,0,0\n"
		"	stvx 1,0,%0\n"
		: : "r" (&av_tmp)
		);

		dest[0]=av_tmp.f[0];
		dest[1]=av_tmp.f[1];
		dest[2]=av_tmp.f[2];
		dest[3]=av_tmp.f[3];
		src += 4;
		dest += 4;
	}
}
#endif
#endif



/* double to short */

void conv_short_double_ref(short *dest, double *src, int n)
{
	int i;
	double x;

	for(i=0;i<n;i++){
		x = *src++;
		if(x<-32768.0)x=-32768.0;
		if(x>32767.0)x=32767.0;
		*dest++ = rint(x);
	}
}

#ifdef HAVE_CPU_PPC
void conv_short_double_ppcasm(short *dest, double *src, int n)
{
	int tmp[2];
	double min = -32768.0;
	double max = 32767.0;
	double ftmp0, ftmp1;

	asm __volatile__(
	"\taddic. %3,%3,-8\n"
	"\taddic. %6,%6,-2\n"
	"loop:\n"
	"\tlfdu %0,8(%3)\n"
	"\tfsub %1,%0,%4\n"
	"\tfsel %0,%1,%0,%4\n"
	"\tfsub %1,%0,%5\n"
	"\tfsel %0,%1,%5,%0\n"
	"\tfctiw %1,%0\n"
	"\taddic. 5,5,-1\n"
	"\tstfd %1,0(%2)\n"
	"\tlhz 9,6(%2)\n"
	"\tsthu 9,2(%6)\n"
	"\tbne loop\n"
	: "=&f" (ftmp0), "=&f" (ftmp1)
	: "b" (tmp), "r" (src), "f" (min), "f" (max), "r" (dest)
	: "r9", "r5" );

}
#endif


void conv_double_short_dstr(double *dest, short *src, int n, int dstr)
{
	int i;
	void *d = dest;
	for(i=0;i<n;i++){
		(*(double *)d)=*src++;
		d += dstr;
	}
}

void conv_short_double_sstr(short *dest, double *src, int n, int sstr)
{
	int i;
	double x;
	void *s = src;

	for(i=0;i<n;i++){
		x = *(double *)s;
		if(x<-32768.0)x=-32768.0;
		if(x>32767.0)x=32767.0;
		*dest++ = rint(x);
		s += sstr;
	}
}