/* SampleTestPlay.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 "SampleTestPlay.h"
#include "SoundOutput.h"
#include "Memory.h"
#include "SampleWindow.h"
#include "SampleConsts.h"
#include "FixedPoint.h"
#include "Alert.h"


#define FRAMESPERBUFFER (8192)
#define MAXBUFFERS (3)
#define INITIALBUFFERS (MAXBUFFERS)

#define SIXTEENBITSHIFTFACTOR (10)
#define EIGHTBITSHIFTFACTOR (18)


struct SampleTestRec
	{
		largefixedsigned*			Data;
		long									LoopStart;
		long									LoopEnd;
		long									CurrentIndex;
		long									EndOfChannel;
		NumBitsType						NumBits;
		NumChannelsType				NumChannels;
		MyBoolean							PlaybackInProgress;
		MyBoolean							Looping;
		MyBoolean							SingleCycleDeference;
	};


/* set up sample test play */
SampleTestRec*		NewSampleTestPlayer(struct SampleWindowRec* Window)
	{
		SampleTestRec*	Player;

		CheckPtrExistence(Window);
		SampleWindowFinalizeCurrentEdit(Window);
		Player = (SampleTestRec*)AllocPtrCanFail(sizeof(SampleTestRec),"SampleTestRec");
		if (Player == NIL)
			{
				AlertHalt("There is not enough memory available to play the sample.",NIL);
			 FailurePoint1:
				return NIL;
			}
		Player->PlaybackInProgress = True;
		Player->SingleCycleDeference = True;
		Player->Data = SampleWindowGetRawFixedPointData(Window);
		if (Player->Data == NIL)
			{
				AlertHalt("There is not enough memory available to play the sample.",NIL);
			 FailurePoint2:
				ReleasePtr((char*)Player);
				goto FailurePoint1;
			}
		Player->LoopStart = SampleWindowGetLoopStart(Window);
		Player->LoopEnd = SampleWindowGetLoopEnd(Window);
		Player->CurrentIndex = 0;
		Player->NumBits = SampleWindowGetNumBits(Window);
		Player->NumChannels = SampleWindowGetNumChannels(Window);
		if (!OpenSoundChannel(SampleWindowGetSamplingRate(Window),
			(Player->NumChannels == eSampleStereo ? eStereo : eMono),
			(Player->NumBits == eSample16bit ? e16bit : e8bit),
			FRAMESPERBUFFER,MAXBUFFERS,INITIALBUFFERS))
			{
				AlertHalt("Unable to open sound device.",NIL);
			 FailurePoint3:
				goto FailurePoint2;
			}
		Player->Looping = (Player->LoopEnd != Player->LoopStart);
		Player->EndOfChannel = PtrSize((char*)Player->Data) / sizeof(largefixedsigned);
		if (Player->NumChannels == eSampleStereo)
			{
				Player->EndOfChannel = Player->EndOfChannel / 2;
			}
		return Player;
	}


/* tell the thing to stop looping, and play out rest of sample */
void							FinishAndDisposeSampleTestPlayer(SampleTestRec* Player)
	{
		CheckPtrExistence(Player);

		/* finish sample */
		while (Player->PlaybackInProgress)
			{
				Player->Looping = False;
				SampleTestPlayCallback(Player,False);
			}

		CloseSoundChannel(NIL,NIL);
		ReleasePtr((char*)Player);
	}


/* call back to be done while mouse button is held down.  continually plays */
/* the loop while buffer space is available. */
void							SampleTestPlayCallback(SampleTestRec* Player, MyBoolean IgnoreMe)
	{
		void*							Buffer;
		long							Scan;
		long							LocalIndex;
		long							LocalEnd;
		largefixedsigned*	LocalData;
		long							Limit;

		CheckPtrExistence(Player);

		/* if this is the first cycle through, then exit */
		if (Player->SingleCycleDeference)
			{
				Player->SingleCycleDeference = False;
				return;
			}

		/* if playback terminated, then exit */
		if (!Player->PlaybackInProgress)
			{
				return;
			}

		/* set up local variable values */
		Buffer = CheckOutSoundBuffer();
		if (Buffer == NIL)
			{
				return;
			}
		Scan = 0;
		LocalIndex = Player->CurrentIndex;
		LocalData = Player->Data;
		Limit = FRAMESPERBUFFER;

		/* use one of the 8 synthesis techniques */
		if (!Player->Looping)
			{
				LocalEnd = Player->EndOfChannel; /* for no loop, the end is the real end */
				if (Player->NumChannels == eSampleStereo)
					{
						if (Player->NumBits == eSample16bit)
							{
								/* stereo, 16-bit, no loop */
								PRNGCHK(Buffer,Buffer,sizeof(short) * Limit * 2);
								while ((Scan < Limit) && (LocalIndex < LocalEnd))
									{
										((signed short*)Buffer)[Scan * 2 + 0]
											= LocalData[LocalIndex * 2 + 0] >> SIXTEENBITSHIFTFACTOR;
										((signed short*)Buffer)[Scan * 2 + 1]
											= LocalData[LocalIndex * 2 + 1] >> SIXTEENBITSHIFTFACTOR;
										LocalIndex += 1;
										Scan += 1;
									}
							}
						 else
							{
								/* stereo, 8-bit, no loop */
								PRNGCHK(Buffer,Buffer,sizeof(char) * Limit * 2);
								while ((Scan < Limit) && (LocalIndex < LocalEnd))
									{
										((signed char*)Buffer)[Scan * 2 + 0]
											= LocalData[LocalIndex * 2 + 0] >> EIGHTBITSHIFTFACTOR;
										((signed char*)Buffer)[Scan * 2 + 1]
											= LocalData[LocalIndex * 2 + 1] >> EIGHTBITSHIFTFACTOR;
										LocalIndex += 1;
										Scan += 1;
									}
							}
					}
				 else
					{
						if (Player->NumBits == eSample16bit)
							{
								/* mono, 16-bit, no loop */
								PRNGCHK(Buffer,Buffer,sizeof(short) * Limit);
								while ((Scan < Limit) && (LocalIndex < LocalEnd))
									{
										((signed short*)Buffer)[Scan]
											= LocalData[LocalIndex] >> SIXTEENBITSHIFTFACTOR;
										LocalIndex += 1;
										Scan += 1;
									}
							}
						 else
							{
								/* mono, 8-bit, no loop */
								PRNGCHK(Buffer,Buffer,sizeof(char) * Limit);
								while ((Scan < Limit) && (LocalIndex < LocalEnd))
									{
										((signed char*)Buffer)[Scan]
											= LocalData[LocalIndex] >> EIGHTBITSHIFTFACTOR;
										LocalIndex += 1;
										Scan += 1;
									}
							}
					}
				if (LocalIndex >= LocalEnd)
					{
						/* end has been reached, so halt playback */
						Player->PlaybackInProgress = False;
						Limit = Scan;
					}
			}
		 else
			{
				LocalEnd = Player->LoopEnd; /* for loop, this is what the end is */
				if (Player->NumChannels == eSampleStereo)
					{
						if (Player->NumBits == eSample16bit)
							{
								/* stereo, 16-bit, yes loop */
								PRNGCHK(Buffer,Buffer,sizeof(short) * Limit * 2);
								while (Scan < Limit)
									{
										if (LocalIndex == LocalEnd)
											{
												LocalIndex = Player->LoopStart;
											}
										((signed short*)Buffer)[Scan * 2 + 0]
											= LocalData[LocalIndex * 2 + 0] >> SIXTEENBITSHIFTFACTOR;
										((signed short*)Buffer)[Scan * 2 + 1]
											= LocalData[LocalIndex * 2 + 1] >> SIXTEENBITSHIFTFACTOR;
										LocalIndex += 1;
										Scan += 1;
									}
							}
						 else
							{
								/* stereo, 8-bit, yes loop */
								PRNGCHK(Buffer,Buffer,sizeof(char) * Limit * 2);
								while (Scan < Limit)
									{
										if (LocalIndex == LocalEnd)
											{
												LocalIndex = Player->LoopStart;
											}
										((signed char*)Buffer)[Scan * 2 + 0]
											= LocalData[LocalIndex * 2 + 0] >> EIGHTBITSHIFTFACTOR;
										((signed char*)Buffer)[Scan * 2 + 1]
											= LocalData[LocalIndex * 2 + 1] >> EIGHTBITSHIFTFACTOR;
										LocalIndex += 1;
										Scan += 1;
									}
							}
					}
				 else
					{
						if (Player->NumBits == eSample16bit)
							{
								/* mono, 16-bit, yes loop */
								PRNGCHK(Buffer,Buffer,sizeof(short) * Limit);
								while (Scan < Limit)
									{
										if (LocalIndex == LocalEnd)
											{
												LocalIndex = Player->LoopStart;
											}
										((signed short*)Buffer)[Scan]
											= LocalData[LocalIndex] >> SIXTEENBITSHIFTFACTOR;
										LocalIndex += 1;
										Scan += 1;
									}
							}
						 else
							{
								/* mono, 8-bit, yes loop */
								PRNGCHK(Buffer,Buffer,sizeof(char) * Limit);
								while (Scan < Limit)
									{
										if (LocalIndex == LocalEnd)
											{
												LocalIndex = Player->LoopStart;
											}
										((signed char*)Buffer)[Scan]
											= LocalData[LocalIndex] >> EIGHTBITSHIFTFACTOR;
										LocalIndex += 1;
										Scan += 1;
									}
							}
					}
			}

		/* play the data and write changed values out */
		SubmitBuffer((char*)Buffer,Limit,NIL,NIL);
		Player->CurrentIndex = LocalIndex;
	}
