/* SampleStorageActual.c */
/*****************************************************************************/
/*                                                                           */
/*    Out Of Phase:  Digital Music Synthesis on General Purpose Computers    */
/*    Copyright (C) 1994  Thomas R. Lawrence                                 */
/*                                                                           */
/*    This program is free software; you can redistribute it and/or modify   */
/*    it under the terms of the GNU General Public License as published by   */
/*    the Free Software Foundation; either version 2 of the License, or      */
/*    (at your option) any later version.                                    */
/*                                                                           */
/*    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., 675 Mass Ave, Cambridge, MA 02139, USA.              */
/*                                                                           */
/*    Thomas R. Lawrence can be reached at tomlaw@world.std.com.             */
/*                                                                           */
/*****************************************************************************/

#include "MiscInfo.h"
#include "Audit.h"
#include "Debug.h"
#include "Definitions.h"

#include "SampleStorageActual.h"
#include "Memory.h"


/* data is stored in the following format: */
/*  - 8bit mono:  packed array of bytes */
/*  - 16bit mono:  packed array of short integers */
/*  - 8bit stereo:  packed array of 2-byte units, the lower == left channel */
/*  - 16bit stereo:  packed array of 2-short int units, the lower == left channel */


struct SampleStorageActualRec
	{
		char*									Buffer;
		NumBitsType						NumberOfBits;
		NumChannelsType				MonoStereo;
		long									NumFrames;
	};


/* create a new sample storage object, with the array zeroed out */
SampleStorageActualRec*		NewSampleStorageActual(NumBitsType NumBits,
														NumChannelsType NumChannels, long NumSampleFrames)
	{
		SampleStorageActualRec*	Storage;
		long										BytesPerFrame;
		long										Limit;
		long										Scan;

		ERROR((NumBits != eSample8bit) && (NumBits != eSample16bit),PRERR(ForceAbort,
			"eSampleStereo:  bad number of bits"));
		ERROR((NumChannels != eSampleMono) && (NumChannels != eSampleStereo),
			PRERR(ForceAbort,"eSampleStereo:  bad number of channels"));
		Storage = (SampleStorageActualRec*)AllocPtrCanFail(sizeof(SampleStorageActualRec),
			"SampleStorageActualRec");
		if (Storage == NIL)
			{
			 FailurePoint1:
				return NIL;
			}
		BytesPerFrame = 1;
		switch (NumBits)
			{
				default:
					EXECUTE(PRERR(ForceAbort,"NewSampleStorageActual:  bad num bits"));
					break;
				case eSample8bit:
					break;
				case eSample16bit:
					BytesPerFrame = BytesPerFrame * (sizeof(short) / sizeof(char));
					break;
			}
		switch (NumChannels)
			{
				default:
					EXECUTE(PRERR(ForceAbort,"NewSampleStorageActual:  bad num channels"));
					break;
				case eSampleMono:
					break;
				case eSampleStereo:
					BytesPerFrame = BytesPerFrame * 2;
					break;
			}
		Storage->Buffer = AllocPtrCanFail((NumSampleFrames + 1) * BytesPerFrame,
			"SampleArray");
		if (Storage->Buffer == NIL)
			{
			 FailurePoint2:
				ReleasePtr((char*)Storage);
				goto FailurePoint1;
			}
		Limit = PtrSize(Storage->Buffer);
		for (Scan = 0; Scan < Limit; Scan += 1)
			{
				Storage->Buffer[Scan] = 0;
			}
		Storage->NumberOfBits = NumBits;
		Storage->MonoStereo = NumChannels;
		Storage->NumFrames = NumSampleFrames;
		return Storage;
	}


/* dispose of a sample storage object */
void											DisposeSampleStorageActual(SampleStorageActualRec* Storage)
	{
		CheckPtrExistence(Storage);
		ReleasePtr(Storage->Buffer);
		ReleasePtr((char*)Storage);
	}


/* get the number of bits the sample storage object contains */
NumBitsType								GetSampleStorageActualNumBits(SampleStorageActualRec* Storage)
	{
		CheckPtrExistence(Storage);
		return Storage->NumberOfBits;
	}


/* get the number of channels the storage object contains */
NumChannelsType						GetSampleStorageActualNumChannels(
														SampleStorageActualRec* Storage)
	{
		CheckPtrExistence(Storage);
		return Storage->MonoStereo;
	}


/* get the number of sample frames the object contains */
long											GetSampleStorageActualNumFrames(
														SampleStorageActualRec* Storage)
	{
		CheckPtrExistence(Storage);
		return Storage->NumFrames;
	}


/* get the value of a sample frame */
largefixedsigned					GetSampleStorageActualValue(SampleStorageActualRec* Storage,
														long Index, ChannelType WhichChannel)
	{
		double									Resultant;

		CheckPtrExistence(Storage);
		switch (Storage->MonoStereo)
			{
				default:
					EXECUTE(PRERR(ForceAbort,
						"GetSampleStorageActualValue:  bad internal num channels"));
					break;
				case eSampleMono:
					ERROR(WhichChannel != eMonoChannel,PRERR(ForceAbort,
						"GetSampleStorageActualValue:  invalid channel selector"));
					switch (Storage->NumberOfBits)
						{
							default:
								EXECUTE(PRERR(ForceAbort,
									"GetSampleStorageActualValue:  bad internal num bits"));
								break;
							case eSample8bit:
								PRNGCHK(Storage->Buffer,&(((char*)Storage->Buffer)[Index]),
									sizeof(char));
								Resultant = ((double)((signed char*)Storage->Buffer)[Index]) / MAX8BIT;
								break;
							case eSample16bit:
								PRNGCHK(Storage->Buffer,&(((short*)Storage->Buffer)[Index]),
									sizeof(short));
								Resultant = ((double)((signed short*)Storage->Buffer)[Index]) / MAX16BIT;
								break;
						}
					break;
				case eSampleStereo:
					switch (Storage->NumberOfBits)
						{
							default:
								EXECUTE(PRERR(ForceAbort,
									"GetSampleStorageActualValue:  bad internal num bits"));
								break;
							case eSample8bit:
								switch (WhichChannel)
									{
										default:
											EXECUTE(PRERR(ForceAbort,
												"GetSampleStorageActualValue:  bad channel selector"));
											break;
										case eLeftChannel:
											PRNGCHK(Storage->Buffer,&(((char*)Storage->Buffer)[2 * Index]),
												sizeof(char));
											Resultant = ((double)((signed char*)Storage->Buffer)[2 * Index])
												/ MAX8BIT;
											break;
										case eRightChannel:
											PRNGCHK(Storage->Buffer,&(((char*)Storage->Buffer)
												[(2 * Index) + 1]),sizeof(char));
											Resultant = ((double)((signed char*)Storage->Buffer)[(2 * Index) + 1])
												/ MAX8BIT;
											break;
									}
								break;
							case eSample16bit:
								switch (WhichChannel)
									{
										default:
											EXECUTE(PRERR(ForceAbort,
												"GetSampleStorageActualValue:  bad channel selector"));
											break;
										case eLeftChannel:
											PRNGCHK(Storage->Buffer,&(((short*)Storage->Buffer)[2 * Index]),
												sizeof(short));
											Resultant = ((double)((signed short*)Storage->Buffer)[2 * Index])
												/ MAX16BIT;
											break;
										case eRightChannel:
											PRNGCHK(Storage->Buffer,&(((short*)Storage->Buffer)
												[(2 * Index) + 1]),sizeof(short));
											Resultant = ((double)((signed short*)Storage->Buffer)[(2 * Index) + 1])
												/ MAX16BIT;
											break;
									}
								break;
						}
					break;
			}
		return double2largefixed(Resultant);
	}


/* change the value of a sample frame */
void											SetSampleStorageActualValue(SampleStorageActualRec* Storage,
														long Index, ChannelType WhichChannel,
														largefixedsigned NewValue)
	{
		double									TargetValue;

		CheckPtrExistence(Storage);
		switch (Storage->MonoStereo)
			{
				default:
					EXECUTE(PRERR(ForceAbort,
						"SetSampleStorageActualValue:  bad internal num channels"));
					break;
				case eSampleMono:
					ERROR(WhichChannel != eMonoChannel,PRERR(ForceAbort,
						"SetSampleStorageActualValue:  invalid channel selector"));
					switch (Storage->NumberOfBits)
						{
							default:
								EXECUTE(PRERR(ForceAbort,
									"SetSampleStorageActualValue:  bad internal num bits"));
								break;
							case eSample8bit:
								TargetValue = roundtonearest(largefixed2double(NewValue) * MAX8BIT);
								if (TargetValue > MAX8BIT)
									{
										TargetValue = MAX8BIT;
									}
								else if (TargetValue < MIN8BIT)
									{
										TargetValue = MIN8BIT;
									}
								PRNGCHK(Storage->Buffer,&(((char*)Storage->Buffer)[Index]),
									sizeof(char));
								((char*)Storage->Buffer)[Index] = TargetValue;
								if (Index == Storage->NumFrames - 1)
									{
										PRNGCHK(Storage->Buffer,&(((char*)Storage->Buffer)
											[Storage->NumFrames]),sizeof(char));
										((char*)Storage->Buffer)[Storage->NumFrames] = TargetValue;
									}
								break;
							case eSample16bit:
								TargetValue = roundtonearest(largefixed2double(NewValue) * MAX16BIT);
								if (TargetValue > MAX16BIT)
									{
										TargetValue = MAX16BIT;
									}
								else if (TargetValue < MIN16BIT)
									{
										TargetValue = MIN16BIT;
									}
								PRNGCHK(Storage->Buffer,&(((short*)Storage->Buffer)[Index]),
									sizeof(short));
								((short*)Storage->Buffer)[Index] = TargetValue;
								if (Index == Storage->NumFrames - 1)
									{
										PRNGCHK(Storage->Buffer,&(((short*)Storage->Buffer)
											[Storage->NumFrames]),sizeof(short));
										((short*)Storage->Buffer)[Storage->NumFrames] = TargetValue;
									}
								break;
						}
					break;
				case eSampleStereo:
					switch (Storage->NumberOfBits)
						{
							default:
								EXECUTE(PRERR(ForceAbort,
									"SetSampleStorageActualValue:  bad internal num bits"));
								break;
							case eSample8bit:
								switch (WhichChannel)
									{
										default:
											EXECUTE(PRERR(ForceAbort,
												"SetSampleStorageActualValue:  bad channel selector"));
											break;
										case eLeftChannel:
											TargetValue = roundtonearest(largefixed2double(NewValue) * MAX8BIT);
											if (TargetValue > MAX8BIT)
												{
													TargetValue = MAX8BIT;
												}
											else if (TargetValue < MIN8BIT)
												{
													TargetValue = MIN8BIT;
												}
											PRNGCHK(Storage->Buffer,&(((char*)Storage->Buffer)[2 * Index]),
												sizeof(char));
											((char*)Storage->Buffer)[2 * Index] = TargetValue;
											if (Index == Storage->NumFrames - 1)
												{
													PRNGCHK(Storage->Buffer,&(((char*)Storage->Buffer)
														[2 * Storage->NumFrames]),sizeof(char));
													((char*)Storage->Buffer)[2 * Storage->NumFrames] = TargetValue;
												}
											break;
										case eRightChannel:
											TargetValue = roundtonearest(largefixed2double(NewValue) * MAX8BIT);
											if (TargetValue > MAX8BIT)
												{
													TargetValue = MAX8BIT;
												}
											else if (TargetValue < MIN8BIT)
												{
													TargetValue = MIN8BIT;
												}
											PRNGCHK(Storage->Buffer,&(((char*)Storage->Buffer)
												[(2 * Index) + 1]),sizeof(char));
											((char*)Storage->Buffer)[(2 * Index) + 1] = TargetValue;
											if (Index == Storage->NumFrames - 1)
												{
													PRNGCHK(Storage->Buffer,&(((char*)Storage->Buffer)
														[(2 * Storage->NumFrames) + 1]),sizeof(char));
													((char*)Storage->Buffer)[(2 * Storage->NumFrames) + 1]
														= TargetValue;
												}
											break;
									}
								break;
							case eSample16bit:
								switch (WhichChannel)
									{
										default:
											EXECUTE(PRERR(ForceAbort,
												"SetSampleStorageActualValue:  bad channel selector"));
											break;
										case eLeftChannel:
											TargetValue = roundtonearest(largefixed2double(NewValue) * MAX16BIT);
											if (TargetValue > MAX16BIT)
												{
													TargetValue = MAX16BIT;
												}
											else if (TargetValue < MIN16BIT)
												{
													TargetValue = MIN16BIT;
												}
											PRNGCHK(Storage->Buffer,&(((short*)Storage->Buffer)[2 * Index]),
												sizeof(short));
											((short*)Storage->Buffer)[2 * Index] = TargetValue;
											if (Index == Storage->NumFrames - 1)
												{
													PRNGCHK(Storage->Buffer,&(((short*)Storage->Buffer)
														[2 * Storage->NumFrames]),sizeof(short));
													((short*)Storage->Buffer)[2 * Storage->NumFrames]
														= TargetValue;
												}
											break;
										case eRightChannel:
											TargetValue = roundtonearest(largefixed2double(NewValue) * MAX16BIT);
											if (TargetValue > MAX16BIT)
												{
													TargetValue = MAX16BIT;
												}
											else if (TargetValue < MIN16BIT)
												{
													TargetValue = MIN16BIT;
												}
											PRNGCHK(Storage->Buffer,&(((short*)Storage->Buffer)
												[(2 * Index) + 1]),sizeof(short));
											((short*)Storage->Buffer)[(2 * Index) + 1] = TargetValue;
											if (Index == Storage->NumFrames - 1)
												{
													PRNGCHK(Storage->Buffer,&(((short*)Storage->Buffer)
														[(2 * Storage->NumFrames) + 1]),sizeof(short));
													((short*)Storage->Buffer)[(2 * Storage->NumFrames) + 1]
														= TargetValue;
												}
											break;
									}
								break;
						}
					break;
			}
	}


/* get a reference to the raw data.  The raw data will either be an array of chars */
/* (for 8-bit data) or an array of shorts (for 16-bit data).  There will be */
/* GetSampleStorageActualNumFrames + 1  frames; the extra one at the end is to */
/* make anti-aliasing more efficient, and repeats the second-last word of data */
char*											GetSampleStorageActualRawData(SampleStorageActualRec* Storage)
	{
		CheckPtrExistence(Storage);
		return Storage->Buffer;
	}


/* get a copy of a particular channel of data from the sample */
largefixedsigned*					SampleStorageActualGetChannelFixed(
														SampleStorageActualRec* Storage, ChannelType WhichChannel)
	{
		largefixedsigned*				Data;
		long										Limit;
		long										Scan;

		CheckPtrExistence(Storage);
		Limit = GetSampleStorageActualNumFrames(Storage);
		Data = (largefixedsigned*)AllocPtrCanFail(sizeof(largefixedsigned)
			* Limit,"SampleFixed");
		if (Data != NIL)
			{
				for (Scan = 0; Scan < Limit; Scan += 1)
					{
						Data[Scan] = GetSampleStorageActualValue(Storage,Scan,WhichChannel);
					}
			}
		return Data;
	}
