////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 RWS Inc, All Rights Reserved
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of version 2 of the GNU General Public License as published by
// the Free Software Foundation
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
///////////////////////////////////////////////////////////////////////////////
//
//	SndFx.cpp
// 
// History:
//		09/06/96 JMI	Started.
//
//		09/10/96	JMI	Now uses a precalculated table (generated by a call to
//							SetFadeAccuracy()) to do fades.
//
//		09/10/96	JMI	Now effects can optionally have a destination other than 
//							the source.
//
//		09/11/96	JMI	Fade outs for 8 bit were sending the fade in variables
//							to the decimate unsigned template.
//
//		09/24/96 MJR	Added <stdlib.h> so it would compile on Mac.
//
//		10/30/96	JMI	Changed:
//							Old label:				New label:
//							=========				=========
//							CSndFx					RSndFx
//							MAX_BITSPERSAMPLE		RSP_SNDFX_MAX_BITSPERSAMPLE
//							NUM_FADE_INTERVALS	RSP_SNDFX_NUM_FADE_INTERVALS
//							SNDFX_*					*	Macros changed to enum/typedef.
//							m_fx						m_fx
//							
//////////////////////////////////////////////////////////////////////////////
//
// Implements sound effects on PCM data.
//
//////////////////////////////////////////////////////////////////////////////

#include <stdlib.h>

#include "Blue.h"

#ifdef PATHS_IN_INCLUDES
	#include "GREEN/SndFx/SndFx.h"
#else
	#include "SndFx.h"
#endif // PATHS_IN_INCLUDES


//////////////////////////////////////////////////////////////////////////////
// Initialize static member variables.
//////////////////////////////////////////////////////////////////////////////

RSndFx::PCMINFO	RSndFx::ms_apcminfo[RSP_SNDFX_MAX_BITSPERSAMPLE + 1] =	// Stores info       
																			// particular to each
																			// PCM type.         
	{
		{ 0x00000000, 0x00000000	},	// 0 Bit.
		{ 0x00000000, 0x00000001	},	// 1 Bit.
		{ 0x00000000, 0x00000003	},	// 2 Bit.
		{ 0x00000000, 0x00000007	},	// 3 Bit.
		{ 0x00000000, 0x0000000F	},	// 4 Bit.
		{ 0x00000000, 0x0000001F	},	// 5 Bit.
		{ 0x00000000, 0x0000003F	},	// 6 Bit.
		{ 0x00000000, 0x0000007F	},	// 7 Bit.
		{ 0x00000000, 0x000000FF	},	// 8 Bit.
		{ 0x00000080, 0x000000FF	},	// 9 Bit.
		{ 0x00000100, 0x000001FF	},	// 10 Bit.
		{ 0x00000200, 0x000003FF	},	// 11 Bit.
		{ 0x00000400, 0x000007FF	},	// 12 Bit.
		{ 0x00000800, 0x00000FFF	},	// 13 Bit.
		{ 0x00001000, 0x00001FFF	},	// 14 Bit.
		{ 0x00002000, 0x00003FFF	},	// 15 Bit.
		{ -32768,		32767	},	// 16 Bit.
		{ 0x00008000, 0x0000FFFF	},	// 17 Bit.
		{ 0x00010000, 0x0001FFFF	},	// 18 Bit.
		{ 0x00020000, 0x0003FFFF	},	// 19 Bit.
		{ 0x00040000, 0x0007FFFF	},	// 20 Bit.
		{ 0x00080000, 0x000FFFFF	},	// 21 Bit.
		{ 0x00100000, 0x001FFFFF	},	// 22 Bit.
		{ 0x00200000, 0x003FFFFF	},	// 23 Bit.
		{ 0x00400000, 0x007FFFFF	},	// 24 Bit.
		{ 0x00800000, 0x00FFFFFF	},	// 25 Bit.
		{ 0x01000000, 0x01FFFFFF	},	// 26 Bit.
		{ 0x02000000, 0x03FFFFFF	},	// 27 Bit.
		{ 0x04000000, 0x07FFFFFF	},	// 28 Bit.
		{ 0x08000000, 0x0FFFFFFF	},	// 29 Bit.
		{ 0x10000000, 0x1FFFFFFF	},	// 30 Bit.
		{ 0x20000000, 0x3FFFFFFF	},	// 31 Bit.
		{ 0x40000000, 0x7FFFFFFF	},	// 32 Bit.
	};

U8*	RSndFx::ms_pu8Fade	= NULL;	// Unsigned 8 bit output
												// fade table.
S16*	RSndFx::ms_ps16Fade	= NULL;	// Signed 16 bit output
												// fade table.

short	RSndFx::ms_sNumFadeSteps	= 0;	// Number of fade steps.

long	RSndFx::ms_lSamplesPerSec	= 0;	// Samples per second.
long	RSndFx::ms_lBitsPerSample	= 0;	// Bits per sample.
long	RSndFx::ms_lNumChannels		= 0;	// Number of channels.

long	RSndFx::ms_lBitsPerSec	= 0;	// Number of bits per second.
										// Can be used to convert bytes to milliseconds
										// and convert milliseconds into bytes.  See
										// macros BYTES2MS and MS2BYTES in SndFx.CPP.

//////////////////////////////////////////////////////////////////////////////
// Macros.
//////////////////////////////////////////////////////////////////////////////

// Converts bytes into milliseconds using a pre calculated variable.
// 8000L consists of 8 (for bytes to bits) and 1000 (for seconds
// to milliseconds).
#define BYTES2MS(bytes)	(((bytes) * 8000L) / ms_lBitsPerSec)
// Converts milliseconds into bytes using a pre calculated variable.
// 8000L consists of 8 (for bits to bytes) and 1000 (for milliseconds
// to seconds).
#define MS2BYTES(ms)		(((ms) * ms_lBitsPerSec) / 8000L)

#define PCM8_SILENCE		0x7F		// 0x80 will not work for unsigned algorithm.
#define PCM16_SILENCE	0x0000

#define FIXED_POINT_DEPTH		1000		// Depth of fixed point notation (e.g.,
													// 1000 == depth of 1/1000).

//////////////////////////////////////////////////////////////////////////////
// Functions.
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//
// Default Constructor.
// Returns nothing.
// (public)
//
//////////////////////////////////////////////////////////////////////////////
RSndFx::RSndFx()
	{
	// Initializes members.
	Init();
	}

//////////////////////////////////////////////////////////////////////////////
//
// Destructor.
// Returns nothing.
// (public)
//
//////////////////////////////////////////////////////////////////////////////
RSndFx::~RSndFx()
	{
	// Clear and free.
	Clear();
	}

//////////////////////////////////////////////////////////////////////////////
//
// Initialize instantiable members.
// Returns nothing.
// (public)
//
//////////////////////////////////////////////////////////////////////////////
void RSndFx::Init(void)
	{
	// Initialize members.
	m_fx					= NoFX;	// Currently active effects.    
                                                  
	///////////////// Fade In /////////////////////////////////////////

	m_lFadeInMillisecondsDuration		= 0;	// Original duration.
	m_lFadeInBytesDurationAffected	= 0;	// Amount left to fade.

	///////////////// Fade Out ////////////////////////////////////////

	m_lFadeOutMillisecondsDuration	= 0;	// Original duration.
	m_lFadeOutBytesDurationRemaining	= 0;	// Amount left to fade.
	}

///////////////////////////////////////////////////////////////////////////////
//
// Release any dynamic memory and reset variables.
// Clears all effects.
// (public)
//
///////////////////////////////////////////////////////////////////////////////
void RSndFx::Clear(void)
	{
	Init();
	}

///////////////////////////////////////////////////////////////////////////////
//
// Release any dynamic memory referenced by static members.
// (static/public)
//
///////////////////////////////////////////////////////////////////////////////
void RSndFx::CleanUp(void)
	{
	if (ms_pu8Fade != NULL)
		{
		free(ms_pu8Fade);
		ms_pu8Fade	= NULL;
		}

	if (ms_ps16Fade != NULL)
		{
		free(ms_ps16Fade);
		ms_ps16Fade	= NULL;
		}
	}

///////////////////////////////////////////////////////////////////////////////
//
// Reset effects to start over.
//
///////////////////////////////////////////////////////////////////////////////
void RSndFx::Reset(void)
	{
	///////////////// Fade In /////////////////////////////////////////////////

	SetUpFadeIn(m_lFadeInMillisecondsDuration);
														
	///////////////// Fade Out ////////////////////////////////////////////////

	SetUpFadeOut(m_lFadeOutMillisecondsDuration); 
	}

//////////////////////////////////////////////////////////////////////////////
//
// Sets the fade accuracy (i.e., the number of steps to perform a fade).
// This costs sNumStemps * 256 * bits per sample / 8 bytes of memory.
// This function should only be called after the bits per sample have
// been set via a call to SetDataType().
//
//
// 
//												Power A
// Power difference in dB	= 10 log	-------
//												Power B
// 
// Every 3dB, the audible loudness doubles.
// For acoustics, 0dB often means the threshold of hearing, 0.0002ubar 
// (Microbars: a bar is the "normal" pressure of air).
// (static).
//
//////////////////////////////////////////////////////////////////////////////
short RSndFx::SetFadeAccuracy(	// Returns 0 on success.
	short sNumSteps)					// Number of steps to fades; see above.
	{
	short	sRes	= 0;	// Assume success.

	if (ms_lBitsPerSec != 0)
		{
		// Clean up any existing memory.
		CleanUp();

		// If bits per sample is value that indicates signage . . .
		switch (ms_lBitsPerSample)
			{
			case 8:
				{
				ms_pu8Fade	= (U8*)malloc((long)sNumSteps * 256L * sizeof(S16));
				if (ms_pu8Fade != NULL)
					{
					short	sStep;
					short	sSample;
					float	fStep				= 1.0F / (float)sNumSteps;
					float	fCurDecimation	= fStep;
					U8*	pu8	= ms_pu8Fade;
					for (sStep	= 0; sStep < sNumSteps; sStep++)
						{
						for (sSample = 0; sSample < 256; sSample++)
							{
							*pu8++	= (U8)( (float)(sSample - 0x80) * fCurDecimation) + 0x80;
							}
						
						fCurDecimation	+= fStep;
						}

					// Success.
					ms_sNumFadeSteps	= sNumSteps;

					static short sSetUpAtExit	= FALSE;	// Remember if we already did this.
					// If we haven't already set up the at exit function . . .
					if (sSetUpAtExit == FALSE)
						{
						// Set up atexit to deallocate memory.
						atexit(CleanUp);
						sSetUpAtExit	= TRUE;
						}
					}
				else
					{
					TRACE("SetFadeAccuracy(): Failed to allocate fade lookup table.\n");
					sRes	= -1;
					}
				break;
				}

			case 16:
				{
				ms_ps16Fade	= (S16*)malloc((long)sNumSteps * 256L * sizeof(S16));
				if (ms_ps16Fade != NULL)
					{
					short	sStep;
					short	sSample;
					float	fStep				= 1.0F / (float)sNumSteps;
					float	fCurDecimation	= fStep;
					S16*	ps16	= ms_ps16Fade;
					for (sStep	= 0; sStep < sNumSteps; sStep++)
						{
						for (sSample = 0; sSample < 256; sSample++)
							{
							*ps16++	= (S16)( (float)((sSample - 128) << 8) * fCurDecimation);
							}
						
						fCurDecimation	+= fStep;
						}

					// Success.
					ms_sNumFadeSteps	= sNumSteps;

					static short sSetUpAtExit	= FALSE;	// Remember if we already did this.
					// If we haven't already set up the at exit function . . .
					if (sSetUpAtExit == FALSE)
						{
						// Set up atexit to deallocate memory.
						atexit(CleanUp);
						sSetUpAtExit	= TRUE;
						}
					}
				else
					{
					TRACE("SetFadeAccuracy(): Failed to allocate fade lookup table.\n");
					sRes	= -1;
					}
				break;
				}
			
			default:
				TRACE("SetFadeAccuracy(): %ld bits per sample not supported.\n", ms_lBitsPerSample);
				break;
			}
		}
	else
		{
		TRACE("SetFadeAccuracy(): No data type set.  Use SetDataType().\n");
		sRes	= 1;
		}

	return sRes;
	}

//////////////////////////////////////////////////////////////////////////////
//
// Set type of PCM data in use.  This will reset all effects.
// (static).
//
//////////////////////////////////////////////////////////////////////////////
void RSndFx::SetDataType(	// Returns nothing.
	long lSamplesPerSec,		// Samples per second.
	long lBitsPerSample,		// Bits per sample.
	long lNumChannels)		// Number of channels.
	{
	ms_lSamplesPerSec	= lSamplesPerSec;
	ms_lBitsPerSample	= lBitsPerSample;
	ms_lNumChannels	= lNumChannels;

	// Precalculate bits per second to aid in computation time.
	ms_lBitsPerSec	= lSamplesPerSec * lBitsPerSample * lNumChannels;
	}

/////////////////////////////////////////////////////////////////////////
//
// Fades in lNumBytes of pnData for signed TYPE.
//
/////////////////////////////////////////////////////////////////////////
template <
	class TYPE>				// Data type, signed.
inline void Decimate(	// Returns nothing.
	TYPE* psnSrcData,		// Data buffer to decimate.
	TYPE* psnDstData,		// Decimation destination.
	long lNumBytes,		// Number of bytes in buffer.
	TYPE* psnTable)		// Attenuation table.
	{
	long	lNumSamples	= lNumBytes / sizeof(TYPE);
	while (lNumSamples-- > 0)
		{
		*psnDstData++	= psnTable[((*psnSrcData++ + 0x0080) >> 8) + 128];
		}
	}

/////////////////////////////////////////////////////////////////////////
//
// Fades in lNumBytes of punData for unsigned UTYPE.
//
/////////////////////////////////////////////////////////////////////////
template <
	class UTYPE>					// Data type, unsigned.
inline void UnsignedDecimate(	// Returns nothing.
	UTYPE* punSrcData,			// Data buffer to decimate.
	UTYPE* punDstData,			// Decimation destination.
	long lNumBytes,				// Number of bytes in buffer.
	UTYPE* punTable)				// Attenuation table.
	{

	long	lNumSamples	= lNumBytes / sizeof(UTYPE);
	while (lNumSamples-- > 0)
		{
		*punDstData++	= punTable[*punSrcData++];
		}
	}

/////////////////////////////////////////////////////////////////////////
//
// Implements the effect on the provided buffer.
//
/////////////////////////////////////////////////////////////////////////
void RSndFx::Do(						// Returns nothing.
	UCHAR* pucSrcData,				// Data to affect.
	long lBufSize,						// Amount of data.
	UCHAR* pucDstData /*= NULL*/)	// Destination for data, defaults 
											// to same as source.
	{
	if (ms_lBitsPerSec > 0)
		{
		// If any active effects . . .
		if (m_fx != 0)
			{
			// If there is no destination . . .
			if (pucDstData == NULL)
				{
				pucDstData	= pucSrcData;
				}

			if (m_fx & FadeIn)
				{
				if (m_lFadeInBytesDurationAffected < m_lFadeInBytesDuration)
					{
					switch (ms_lBitsPerSample)
						{
						case 8:
							{
							UnsignedDecimate(							// Returns nothing.           
										(U8*)pucSrcData,				// Data buffer to fade.
										(U8*)pucDstData,				// Dest data buffer.
										lBufSize, 						// Number of bytes in buffer. 
										ms_pu8Fade + (m_lFadeInBytesDurationAffected / m_lFadeInRate) * 256 );	// Attenuation Table.
							break;
							}
						case 16:
							{
							Decimate(									// Returns nothing.
										(S16*)pucSrcData,				// Data buffer to fade.
										(S16*)pucDstData,				// Dest data buffer.
										lBufSize, 						// Number of bytes in buffer.
										ms_ps16Fade + (m_lFadeInBytesDurationAffected / m_lFadeInRate) * 256 );	// Attenuation Table.
							break;
							}
						default:
							{
							TRACE("Do(): Unsupported bits per sample.\n");
							break;
							}
						}

					m_lFadeInBytesDurationAffected	+= lBufSize;
					}
				else
					{
					// Done.
					ActivateFadeIn(FALSE);
					}
				}
			else
				{
				if (m_fx & FadeOut)
					{
					if (m_lFadeOutBytesDurationRemaining > 0)
						{
						switch (ms_lBitsPerSample)
							{
							case 8:
								{
								UnsignedDecimate(							// Returns nothing.           
											(U8*)pucSrcData,				// Data buffer to fade.
											(U8*)pucDstData,				// Dest data buffer.
											lBufSize, 						// Number of bytes in buffer. 
											ms_pu8Fade + (m_lFadeOutBytesDurationRemaining / m_lFadeOutRate) * 256 );	// Attenuation Table.
								break;
								}
							case 16:
								{
								Decimate(									// Returns nothing.           
											(S16*)pucSrcData,				// Data buffer to fade.
											(S16*)pucDstData,				// Dest data buffer.
											lBufSize, 						// Number of bytes in buffer. 
											ms_ps16Fade + (m_lFadeOutBytesDurationRemaining / m_lFadeOutRate) * 256 ); // Attenuation table.         
								break;
								}
							default:
								{
								TRACE("Do(): Unsupported bits per sample.\n");
								break;
								}
							}

						m_lFadeOutBytesDurationRemaining	-= lBufSize;
						}
					else
						{
						// Done.
						ActivateFadeOut(FALSE);
						}
					}
				}
			}
		}
	else
		{
		TRACE("Do(): Called, but no data format set.  Use SetDataType().\n");
		}
	}
		
/////////////////////////////////////////////////////////////////////////
// Various FX.
/////////////////////////////////////////////////////////////////////////

//////////////////////////// Fade In ////////////////////////////////////

/////////////////////////////////////////////////////////////////////////
//
// Set up a fade in.
//
/////////////////////////////////////////////////////////////////////////
short RSndFx::SetUpFadeIn(	// Returns 0 on success.
	long lDuration)			// Duration until silence in milliseconds.
	{
	short	sRes	= 0;	// Assume success.

	// Must set data type before setting up effect . . .
	if (ms_lBitsPerSec > 0)
		{
		if (lDuration > 0)
			{
			m_lFadeInMillisecondsDuration		= lDuration;
			m_lFadeInBytesDurationAffected	= 0;
			m_lFadeInBytesDuration				= MS2BYTES(m_lFadeInMillisecondsDuration);

			// Determine the number of bytes per table index.
			m_lFadeInRate							= m_lFadeInBytesDuration / ms_sNumFadeSteps;
			}
		}
	else
		{
		TRACE("FadeIn(): Data type not set.  Use SetDataType().\n");
		sRes	= 1;
		}

	return sRes;
	}

/////////////////////////////////////////////////////////////////////////
//
// Activate/Deactivate fade in.
//
/////////////////////////////////////////////////////////////////////////
void RSndFx::ActivateFadeIn(	// Returns nothing.
	short	sActivate)				// TRUE to activate, FALSE to deactivate.
	{ 
	if (sActivate == FALSE)
		{
		m_fx	= (FX)(m_fx & (~FadeIn)); 
		}
	else
		{
		m_fx	= (FX)(m_fx | FadeIn); 
		}
	}

/////////////////////// Fade Out ////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////
//
// Set up a fade out.
//
/////////////////////////////////////////////////////////////////////////
short RSndFx::SetUpFadeOut(	// Returns 0 on success.
	long lDuration)				// Duration until full volume in milliseconds.
	{
	short	sRes	= 0;	// Assume success.

	// Must set data type before setting up effect . . .
	if (ms_lBitsPerSec > 0)
		{
		if (lDuration > 0)
			{
			m_lFadeOutMillisecondsDuration	= lDuration;
			m_lFadeOutBytesDurationRemaining	= MS2BYTES(m_lFadeOutMillisecondsDuration);
			m_lFadeOutBytesDuration				= m_lFadeOutBytesDurationRemaining;

			// Determine the number of bytes per table index.
			m_lFadeOutRate							= m_lFadeOutBytesDuration / ms_sNumFadeSteps;
			}
		}
	else
		{
		TRACE("FadeOut(): Data type not set.  Use SetDataType().\n");
		sRes	= 1;
		}

	return sRes;
	}

/////////////////////////////////////////////////////////////////////////
//
// Activate/Deactivate fade out.
//
/////////////////////////////////////////////////////////////////////////
void RSndFx::ActivateFadeOut(	// Returns nothing.
	short	sActivate)				// TRUE to activate, FALSE to deactivate.
	{ 
	if (sActivate != FALSE)
		{
		// Deactivates fade ins.
		ActivateFadeIn(FALSE);

		m_fx	= (FX)(m_fx | FadeOut); 
		}
	else
		{
		m_fx	= (FX)(m_fx & (~FadeOut)); 
		}
	}

//////////////////////////////////////////////////////////////////////////////
// EOF
//////////////////////////////////////////////////////////////////////////////
