/*=============================================================================
	CASampleTools.cpp

	$Log: CASampleTools.cpp,v $
	Revision 1.6  2004/04/29 00:56:31  jcm10
	fix compiler warnings that broke the HAL's build
	
	Revision 1.5  2002/05/14 00:18:12  luke
	integrate changes made in SoundMgr respository <ealdrich, luke>
	
	Revision 1.4  2002/04/25 23:23:56  jcm10
	constants are const
	
	Revision 1.3  2002/04/25 22:27:12  jcm10
	define the constants in the .cpp
	
	Revision 1.2  2002/04/16 19:27:05  luke
	fixes for i386 build <jcm10>
	
	Revision 1.1  2002/04/05 23:05:37  jcm10
	renamed from "CASampleTools.cp"
	
	Revision 1.1  2002/03/01 01:52:40  jcm10
	moved here from ../Utility
	
	Revision 1.10  2002/02/28 23:24:29  jcm10
	added the CA prefix to DebugMacros and LogMacros for more consistency
	
	Revision 1.9  2002/02/02 00:37:16  dwyatt
	fix for gcc3
	
	Revision 1.8  2001/04/05 01:31:57  jcm10
	add endian swapping routines to eliminate dependancy on CoreServices
	
	Revision 1.7  2000/12/16 01:50:36  jcm10
	add a sanity checking funciton
	
	Revision 1.6  2000/11/22 01:29:25  jcm10
	don't use any headers from a sub-framework, like CarbonCore. Use the umbrella's header instead.
	
	Revision 1.5  2000/10/02 18:34:29  jcm10
	XFiles work better
	
	Revision 1.4  2000/09/28 19:34:22  jcm10
	return the right numbers
	
	Revision 1.3  2000/09/23 22:55:15  jcm10
	XFiles works, almost
	
	Revision 1.2  2000/09/22 19:35:53  jcm10
	more XFiles stuff
	
	Revision 1.1  2000/09/21 19:45:03  jcm10
	First checked in
	
	Revision 0.0  2000/01/01 12:34:56  jcm10
	created
		
	$NoKeywords: $
=============================================================================*/

//=============================================================================
//	Includes
//=============================================================================

#include "CASampleTools.h"

#define ASM __asm__ volatile		// bad things happen with plain "asm"

// ____________________________________________________________________________
//
//	Types for use in algorithms
class CAFloat32 {
public:
	typedef float value_type;
	
	static value_type load(const value_type *p) { return *p; }
	static void store(value_type *p, float val)	{ *p = val; }
};

class CASInt8 {
public:
	typedef int8_t value_type;
	
	static value_type load(const value_type *p) { return *p; }
	static void store(value_type *p, int val)	{ *p = val; }
};

class CAUInt8 {
public:
	typedef u_int8_t value_type;
	
	static value_type load(const value_type *p) { return *p ^ 0x80; }
	static void store(value_type *p, int val)	{ *p = val ^ 0x80; }
};

class CASInt16Native {
public:
	typedef int16_t value_type;
	
	static value_type load(const value_type *p) { return *p; }
	static void store(value_type *p, int val)	{ *p = val; }
};

class CASInt16Swap {
public:
	typedef int16_t value_type;
	
	static value_type load(const value_type *p)
	{
#if __ppc__
		value_type val;
		ASM( "lhbrx %0, 0, %1" : : "r" (val), "r" (p) );
		return val;
#else
		return CASampleTools::Int16SwapEndian(*p);
#endif
	}
	static void store(value_type *p, int val)
	{
#if __ppc__
		ASM( "sthbrx %0, 0, %1" : : "r" (val), "r" (p) );
#else
		*p = CASampleTools::Int16SwapEndian(val);
#endif
	}
};

class CASInt32Native {
public:
	typedef int32_t value_type;
	
	static value_type load(const value_type *p) { return *p; }
	static void store(value_type *p, int val)	{ *p = val; }
};

class CASInt32Swap {
public:
	typedef u_int32_t value_type;
	
	static value_type load(const value_type *p)
	{
#if __ppc__
		// __ppc__ gcc 2.95 screws this up somehow
		value_type val;
		ASM( "lwbrx %0, 0, %1" : : "r" (val), "r" (p) );
		return val;
#else
		return CASampleTools::Int32SwapEndian(*p);
#endif
	}
	static void store(value_type *p, int val)
	{
		*p = val;
#if __ppc__
		ASM( "stwbrx %0, 0, %1" : : "r" (val), "r" (p) );
#else
		*p = CASampleTools::Int32SwapEndian(val);
#endif
	}
};

#if __ppc__
	typedef CASInt16Native	CASInt16Big;
	typedef CASInt32Native	CASInt32Big;
	typedef CASInt16Swap	CASInt16Little;
	typedef CASInt32Swap	CASInt32Little;
#else
	typedef CASInt16Native	CASInt16Little;
	typedef CASInt32Native	CASInt32Little;
	typedef CASInt16Swap	CASInt16Big;
	typedef CASInt32Swap	CASInt32Big;
#endif

// ____________________________________________________________________________
//
// CAFloatToInt
inline int32_t CAFloatToInt(double inf)
{
#if __ppc__
	int32_t i;
	union {
		double	d;
		u_int32_t	i[2];
	} u;
	double temp;
	
	ASM( "fctiw %0,%1" : "=f" (temp) : "f" (inf));
		// rounds, doesn't truncate towards zero like fctiwz
	u.d = temp;
	i = u.i[1];
	return i;
#else
	return (int32_t)inf;
#endif
}

inline u_int32_t CAFloatToUInt(double inf)
{
	return u_int32_t(CAFloatToInt(inf));
}

// ____________________________________________________________________________
//
// CAFastInt16ToFloat32
//
// for use in tight loops, keeps compiler from generating redundant instructions
class CAFastInt16ToFloat32 {
public:
#if __ppc__
	CAFastInt16ToFloat32(float /*scale*/, int shift)
	{
		u_int32_t signExp = (0x97 - shift) << 23;
		*(u_int32_t *)mBuf = *(u_int32_t *)&mOffset = signExp | 0x8000;
	}
	float operator() (int16_t val)
	{
		mBuf[1] = u_int16_t(val) ^ 0x8000;
		return *(float *)mBuf - mOffset;
	}

	u_int16_t	mBuf[2];
	float		mOffset;
#else
	CAFastInt16ToFloat32(float scale, int shift) : mScale(scale)
	{
	}
	
	float operator() (int16_t val) { return (float)val * mScale; }
	
	float		mScale;
#endif
};

// ____________________________________________________________________________
//
// CAFastInt32ToFloat64
//
// for use in tight loops, keeps compiler from generating redundant instructions
class CAFastInt32ToFloat64 {
public:
#if __ppc__
	CAFastInt32ToFloat64(double /*scale*/, int shift)
	{
		// this is the saving: we don't keep stuffing this value into memory
		// for every sample
		mBuf[0] = (0x434 - shift) << 20;
		mBuf[1] = 0x80000000;
		mOffset = *(double *)mBuf;
	}
	double operator() (int32_t val)
	{
		// this is the rest of what the compiler does to convert int -> float
		mBuf[1] = val ^ 0x80000000;
		return (*(double *)mBuf - mOffset);
	}

	u_int32_t		mBuf[2];
	double		mOffset;
#else
	CAFastInt32ToFloat64(double scale, int shift) : mScale(scale)
	{
	}
	
	double operator() (int32_t val) { return (double)val * mScale; }
	
	const double		mScale;
#endif
};

//=============================================================================
//	CASampleTools
//=============================================================================

const int8_t	CASampleTools::sMaxSInt8Sample = 127;
const int8_t	CASampleTools::sMinSInt8Sample = -128;
const float		CASampleTools::sHalfRangeSInt8Sample = 128.0;
const int16_t	CASampleTools::sMaxSInt16Sample = 32767;
const int16_t	CASampleTools::sMinSInt16Sample = -32768;
const float		CASampleTools::sHalfRangeSInt16Sample = 32768.0;

const float	CASampleTools::sMaxSInt8Float32Sample = static_cast<float>(sMaxSInt8Sample) / sHalfRangeSInt8Sample;
const float	CASampleTools::sMinSInt8Float32Sample = static_cast<float>(sMinSInt8Sample) / sHalfRangeSInt8Sample;
const float	CASampleTools::sMaxSInt16Float32Sample = static_cast<float>(sMaxSInt16Sample) / sHalfRangeSInt16Sample;
const float	CASampleTools::sMinSInt16Float32Sample = static_cast<float>(sMinSInt16Sample) / sHalfRangeSInt16Sample;

void	CASampleTools::CopySInt8ToFloat32Samples(const int8_t* inSource, float* outDestination, u_int32_t inNumberSamples)
{
	const CASInt8::value_type *src = (const CASInt8::value_type *)inSource - 1;
	CAFloat32::value_type *dest = (CAFloat32::value_type *)outDestination - 1;
	CAFastInt16ToFloat32 i2f(1.0 / float(1UL << (8 - 1)), 8);
	int count;
	
	count = inNumberSamples >> 2;
	while (count--) {
		int i1 = CASInt8::load(++src);
		int i2 = CASInt8::load(++src);
		int i3 = CASInt8::load(++src);
		int i4 = CASInt8::load(++src);
		CAFloat32::value_type f1 = i2f(i1);
		CAFloat32::value_type f2 = i2f(i2);
		CAFloat32::value_type f3 = i2f(i3);
		CAFloat32::value_type f4 = i2f(i4);
		CAFloat32::store(++dest, f1);
		CAFloat32::store(++dest, f2);
		CAFloat32::store(++dest, f3);
		CAFloat32::store(++dest, f4);
	}
	
	count = inNumberSamples & 3;
	while (count--) {
		int i1 = CASInt8::load(++src);
		CAFloat32::value_type f1 = i2f(i1);
		CAFloat32::store(++dest, f1);
	}
}

void	CASampleTools::CopyLESInt16ToFloat32Samples(const int16_t* inSource, float* outDestination, u_int32_t inNumberSamples)
{
	const CASInt16Little::value_type *src = (const CASInt16Little::value_type *)inSource - 1;
	CAFloat32::value_type *dest = (CAFloat32::value_type *)outDestination - 1;
	CAFastInt16ToFloat32 i2f(1.0 / float(1UL << (16 - 1)), 16);
	int count;
	
	count = inNumberSamples >> 2;
	while (count--) {
		int i1 = CASInt16Little::load(++src);
		int i2 = CASInt16Little::load(++src);
		int i3 = CASInt16Little::load(++src);
		int i4 = CASInt16Little::load(++src);
		CAFloat32::value_type f1 = i2f(i1);
		CAFloat32::value_type f2 = i2f(i2);
		CAFloat32::value_type f3 = i2f(i3);
		CAFloat32::value_type f4 = i2f(i4);
		CAFloat32::store(++dest, f1);
		CAFloat32::store(++dest, f2);
		CAFloat32::store(++dest, f3);
		CAFloat32::store(++dest, f4);
	}
	
	count = inNumberSamples & 3;
	while (count--) {
		int i1 = CASInt16Little::load(++src);
		CAFloat32::value_type f1 = i2f(i1);
		CAFloat32::store(++dest, f1);
	}
}

void	CASampleTools::CopyBESInt16ToFloat32Samples(const int16_t* inSource, float* outDestination, u_int32_t inNumberSamples)
{
	const CASInt16Big::value_type *src = (const CASInt16Big::value_type *)inSource - 1;
	CAFloat32::value_type *dest = (CAFloat32::value_type *)outDestination - 1;
	CAFastInt16ToFloat32 i2f(1.0 / float(1UL << (16 - 1)), 16);
	int count;
	
	count = inNumberSamples >> 2;
	while (count--) {
		int i1 = CASInt16Big::load(++src);
		int i2 = CASInt16Big::load(++src);
		int i3 = CASInt16Big::load(++src);
		int i4 = CASInt16Big::load(++src);
		CAFloat32::value_type f1 = i2f(i1);
		CAFloat32::value_type f2 = i2f(i2);
		CAFloat32::value_type f3 = i2f(i3);
		CAFloat32::value_type f4 = i2f(i4);
		CAFloat32::store(++dest, f1);
		CAFloat32::store(++dest, f2);
		CAFloat32::store(++dest, f3);
		CAFloat32::store(++dest, f4);
	}
	
	count = inNumberSamples & 3;
	while (count--) {
		int i1 = CASInt16Big::load(++src);
		CAFloat32::value_type f1 = i2f(i1);
		CAFloat32::store(++dest, f1);
	}
}

void	CASampleTools::CopyLESInt32ToFloat32Samples(const int32_t* inSource, float* outDestination, u_int32_t inNumberSamples)
{
	const CASInt32Little::value_type *src = (const CASInt32Little::value_type *)inSource - 1;
	CAFloat32::value_type *dest = (CAFloat32::value_type *)outDestination - 1;
	CAFastInt32ToFloat64 i2f(1.0 / float(1UL << (32 - 1)), 32);
	int count;
	
	count = inNumberSamples >> 2;
	while (count--) {
		int i1 = CASInt32Little::load(++src);
		int i2 = CASInt32Little::load(++src);
		int i3 = CASInt32Little::load(++src);
		int i4 = CASInt32Little::load(++src);
		CAFloat32::value_type f1 = i2f(i1);
		CAFloat32::value_type f2 = i2f(i2);
		CAFloat32::value_type f3 = i2f(i3);
		CAFloat32::value_type f4 = i2f(i4);
		CAFloat32::store(++dest, f1);
		CAFloat32::store(++dest, f2);
		CAFloat32::store(++dest, f3);
		CAFloat32::store(++dest, f4);
	}
	
	count = inNumberSamples & 3;
	while (count--) {
		int i1 = CASInt32Little::load(++src);
		CAFloat32::value_type f1 = i2f(i1);
		CAFloat32::store(++dest, f1);
	}
}

void	CASampleTools::CopyBESInt32ToFloat32Samples(const int32_t* inSource, float* outDestination, u_int32_t inNumberSamples)
{
	const CASInt32Big::value_type *src = (const CASInt32Big::value_type *)inSource - 1;
	CAFloat32::value_type *dest = (CAFloat32::value_type *)outDestination - 1;
	CAFastInt32ToFloat64 i2f(1.0 / float(1UL << (32 - 1)), 32);
	int count;
	
	count = inNumberSamples >> 2;
	while (count--) {
		int i1 = CASInt32Big::load(++src);
		int i2 = CASInt32Big::load(++src);
		int i3 = CASInt32Big::load(++src);
		int i4 = CASInt32Big::load(++src);
		CAFloat32::value_type f1 = i2f(i1);
		CAFloat32::value_type f2 = i2f(i2);
		CAFloat32::value_type f3 = i2f(i3);
		CAFloat32::value_type f4 = i2f(i4);
		CAFloat32::store(++dest, f1);
		CAFloat32::store(++dest, f2);
		CAFloat32::store(++dest, f3);
		CAFloat32::store(++dest, f4);
	}
	
	count = inNumberSamples & 3;
	while (count--) {
		int i1 = CASInt32Big::load(++src);
		CAFloat32::value_type f1 = i2f(i1);
		CAFloat32::store(++dest, f1);
	}
}

void	CASampleTools::CopyFloat32ToSInt8Samples(const float* inSource, int8_t* outDestination, u_int32_t inNumberSamples)
{
	const CAFloat32::value_type *src = (const CAFloat32::value_type *)inSource - 1;
	CASInt8::value_type *dest = (CASInt8::value_type *)outDestination - 1;
	float maxInt32 = 2147483648.0;	// 1 << 31
	int shift = (32 - 8), count;
	
	count = inNumberSamples >> 2;
	while (count--) {
		CAFloat32::value_type f1 = CAFloat32::load(++src) * maxInt32;
		CAFloat32::value_type f2 = CAFloat32::load(++src) * maxInt32;
		CAFloat32::value_type f3 = CAFloat32::load(++src) * maxInt32;
		CAFloat32::value_type f4 = CAFloat32::load(++src) * maxInt32;
		int i1 = CAFloatToInt(f1) >> shift;
		int i2 = CAFloatToInt(f2) >> shift;
		int i3 = CAFloatToInt(f3) >> shift;
		int i4 = CAFloatToInt(f4) >> shift;
		CASInt8::store(++dest, i1);
		CASInt8::store(++dest, i2);
		CASInt8::store(++dest, i3);
		CASInt8::store(++dest, i4);
	}
	
	count = inNumberSamples & 3;
	while (count--) {
		CAFloat32::value_type f1 = CAFloat32::load(++src) * maxInt32;
		int i1 = CAFloatToInt(f1) >> shift;
		CASInt8::store(++dest, i1);
	}
}

void	CASampleTools::CopyFloat32ToLESInt16Samples(const float* inSource, int16_t* outDestination, u_int32_t inNumberSamples)
{
	const CAFloat32::value_type *src = (const CAFloat32::value_type *)inSource - 1;
	CASInt16Little::value_type *dest = (CASInt16Little::value_type *)outDestination - 1;
	float maxInt32 = 2147483648.0;	// 1 << 31
	int shift = (32 - 16), count;
	
	count = inNumberSamples >> 2;
	while (count--) {
		CAFloat32::value_type f1 = CAFloat32::load(++src) * maxInt32;
		CAFloat32::value_type f2 = CAFloat32::load(++src) * maxInt32;
		CAFloat32::value_type f3 = CAFloat32::load(++src) * maxInt32;
		CAFloat32::value_type f4 = CAFloat32::load(++src) * maxInt32;
		int i1 = CAFloatToInt(f1) >> shift;
		int i2 = CAFloatToInt(f2) >> shift;
		int i3 = CAFloatToInt(f3) >> shift;
		int i4 = CAFloatToInt(f4) >> shift;
		CASInt16Little::store(++dest, i1);
		CASInt16Little::store(++dest, i2);
		CASInt16Little::store(++dest, i3);
		CASInt16Little::store(++dest, i4);
	}
	
	count = inNumberSamples & 3;
	while (count--) {
		CAFloat32::value_type f1 = CAFloat32::load(++src) * maxInt32;
		int i1 = CAFloatToInt(f1) >> shift;
		CASInt16Little::store(++dest, i1);
	}
}

void	CASampleTools::CopyFloat32ToBESInt16Samples(const float* inSource, int16_t* outDestination, u_int32_t inNumberSamples)
{
	const CAFloat32::value_type *src = (const CAFloat32::value_type *)inSource - 1;
	CASInt16Big::value_type *dest = (CASInt16Big::value_type *)outDestination - 1;
	float maxInt32 = 2147483648.0;	// 1 << 31
	int shift = (32 - 16), count;
	
	count = inNumberSamples >> 2;
	while (count--) {
		CAFloat32::value_type f1 = CAFloat32::load(++src) * maxInt32;
		CAFloat32::value_type f2 = CAFloat32::load(++src) * maxInt32;
		CAFloat32::value_type f3 = CAFloat32::load(++src) * maxInt32;
		CAFloat32::value_type f4 = CAFloat32::load(++src) * maxInt32;
		int i1 = CAFloatToInt(f1) >> shift;
		int i2 = CAFloatToInt(f2) >> shift;
		int i3 = CAFloatToInt(f3) >> shift;
		int i4 = CAFloatToInt(f4) >> shift;
		CASInt16Big::store(++dest, i1);
		CASInt16Big::store(++dest, i2);
		CASInt16Big::store(++dest, i3);
		CASInt16Big::store(++dest, i4);
	}
	
	count = inNumberSamples & 3;
	while (count--) {
		CAFloat32::value_type f1 = CAFloat32::load(++src) * maxInt32;
		int i1 = CAFloatToInt(f1) >> shift;
		CASInt16Big::store(++dest, i1);
	}
}

void	CASampleTools::CopyFloat32ToLESInt32Samples(const float* inSource, int32_t* outDestination, u_int32_t inNumberSamples)
{
	const CAFloat32::value_type *src = (const CAFloat32::value_type *)inSource - 1;
	CASInt32Little::value_type *dest = (CASInt32Little::value_type *)outDestination - 1;
	float maxInt32 = 2147483648.0;	// 1 << 31
	int shift = (32 - 32), count;
	
	count = inNumberSamples >> 2;
	while (count--) {
		CAFloat32::value_type f1 = CAFloat32::load(++src) * maxInt32;
		CAFloat32::value_type f2 = CAFloat32::load(++src) * maxInt32;
		CAFloat32::value_type f3 = CAFloat32::load(++src) * maxInt32;
		CAFloat32::value_type f4 = CAFloat32::load(++src) * maxInt32;
		int i1 = CAFloatToInt(f1) >> shift;
		int i2 = CAFloatToInt(f2) >> shift;
		int i3 = CAFloatToInt(f3) >> shift;
		int i4 = CAFloatToInt(f4) >> shift;
		CASInt32Little::store(++dest, i1);
		CASInt32Little::store(++dest, i2);
		CASInt32Little::store(++dest, i3);
		CASInt32Little::store(++dest, i4);
	}
	
	count = inNumberSamples & 3;
	while (count--) {
		CAFloat32::value_type f1 = CAFloat32::load(++src) * maxInt32;
		int i1 = CAFloatToInt(f1) >> shift;
		CASInt32Little::store(++dest, i1);
	}
}

void	CASampleTools::CopyFloat32ToBESInt32Samples(const float* inSource, int32_t* outDestination, u_int32_t inNumberSamples)
{
	const CAFloat32::value_type *src = (const CAFloat32::value_type *)inSource - 1;
	CASInt32Big::value_type *dest = (CASInt32Big::value_type *)outDestination - 1;
	float maxInt32 = 2147483648.0;	// 1 << 31
	int shift = (32 - 32), count;
	
	count = inNumberSamples >> 2;
	while (count--) {
		CAFloat32::value_type f1 = CAFloat32::load(++src) * maxInt32;
		CAFloat32::value_type f2 = CAFloat32::load(++src) * maxInt32;
		CAFloat32::value_type f3 = CAFloat32::load(++src) * maxInt32;
		CAFloat32::value_type f4 = CAFloat32::load(++src) * maxInt32;
		int i1 = CAFloatToInt(f1) >> shift;
		int i2 = CAFloatToInt(f2) >> shift;
		int i3 = CAFloatToInt(f3) >> shift;
		int i4 = CAFloatToInt(f4) >> shift;
		CASInt32Big::store(++dest, i1);
		CASInt32Big::store(++dest, i2);
		CASInt32Big::store(++dest, i3);
		CASInt32Big::store(++dest, i4);
	}
	
	count = inNumberSamples & 3;
	while (count--) {
		CAFloat32::value_type f1 = CAFloat32::load(++src) * maxInt32;
		int i1 = CAFloatToInt(f1) >> shift;
		CASInt32Big::store(++dest, i1);
	}
}

void	CASampleTools::MixFloat32Samples(const float* inSource, float* outDestination, u_int32_t inNumberSamples)
{
	const CAFloat32::value_type *src = (const CAFloat32::value_type *)inSource - 1;
	CAFloat32::value_type *dest = (CAFloat32::value_type *)outDestination - 1;
	int count;
	
	count = inNumberSamples >> 2;
	while (count--) {
		CAFloat32::value_type f1a = CAFloat32::load(++src);
		CAFloat32::value_type f2a = CAFloat32::load(++src);
		CAFloat32::value_type f3a = CAFloat32::load(++src);
		CAFloat32::value_type f4a = CAFloat32::load(++src);
		CAFloat32::value_type f1b = CAFloat32::load(dest + 0);
		CAFloat32::value_type f2b = CAFloat32::load(dest + 1);
		CAFloat32::value_type f3b = CAFloat32::load(dest + 2);
		CAFloat32::value_type f4b = CAFloat32::load(dest + 3);
		CAFloat32::store(++dest, f1a + f1b);
		CAFloat32::store(++dest, f2a + f2b);
		CAFloat32::store(++dest, f3a + f3b);
		CAFloat32::store(++dest, f4a + f4b);
	}
	
	count = inNumberSamples & 3;
	while (count--) {
		CAFloat32::value_type f1a = CAFloat32::load(++src);
		CAFloat32::value_type f1b = CAFloat32::load(dest);
		CAFloat32::store(++dest, f1a + f1b);
	}
}
