/* PlayAIFFFile.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 "PlayAIFFFile.h"
#include "BufferedFileInput.h"
#include "Files.h"
#include "Alert.h"
#include "SampleConsts.h"
#include "SoundOutput.h"
#include "DataMunging.h"
#include "EventLoop.h"
#include "Memory.h"
#include "SynthProgressWindow.h"


#define FRAMESPERBUFFER (8192L)
#define NUMBEROFBUFFERS (16)
#define INITIALNUMBUFFERS (3)


/* AIFF/AIFF-C File Format: */
/*     "FORM" */
/*     4-byte big endian form chunk length descriptor (minus 8 for "FORM" & this) */
/*     4-byte type */
/*        "AIFF" = AIFF format file */
/*        "AIFC" = AIFF-C format file */
/* in any order, these chunks can occur: */
/*   Version Chunk (this only occurs in AIFF-C files) */
/*     "FVER" */
/*     4-byte big endian length, which should always be the value 4 (four) */
/*     4-byte date code.  this is probably 0xA2805140 (stored big endian), but it */
/*          probably doesn't matter. */
/*   Common Chunk for AIFF files */
/*     "COMM" */
/*     4-byte big endian length. */
/*        always 18 for AIFF files */
/*     2-byte big endian number of channels */
/*     4-byte big endian number of sample frames */
/*     2-byte big endian number of bits per sample */
/*        a value in the domain 1..32 */
/*     10-byte extended precision number of frames per second */
/*   Common Chunk for AIFF-C files */
/*     "COMM" */
/*     4-byte big endian length. */
/*        22 + compression method string length for AIFF-C files */
/*     2-byte big endian number of channels */
/*     4-byte big endian number of sample frames */
/*     2-byte big endian number of bits per sample */
/*        a value in the domain 1..32 */
/*     10-byte extended precision number of frames per second */
/*     4-byte character code ID for the compression method */
/*        "NONE" means there is no compression method used */
/*     some characters in a string identifying the compression method */
/*        this must be padded to an even number of bytes, but the pad is */
/*        NOT included in the length descriptor for the chunk. */
/*        for uncompressed data, the string should be */
/*        "\x0enot compressed\x00", including the null, for 16 bytes. */
/*        the total chunk length is thus 38 bytes. */
/*   Sound Data Chunk */
/*     "SSND" */
/*     4-byte big endian number of bytes in sample data array */
/*     4-byte big endian offset to the first byte of sample data in the array */
/*     4-byte big endian number of bytes to which the sound data is aligned. */
/*     any length vector of raw sound data. */
/*        this must be padded to an even number of bytes, but the pad is */
/*        NOT included in the length descriptor for the chunk. */
/*        Samples are stored in an integral number of bytes, the smallest that */
/*        is required for the specified number of bits.  If this is not an even */
/*        multiple of 8, then the data is shifted left and the low bits are zeroed */
/*        Multichannel sound is interleaved with the left channel first. */
static void						PlayTheFile(BufferedInputRec* File, SynthWinRec* Window)
	{
		char								CharBuff[4];
		MyBoolean						IsAnAIFFCFile;
		long								FormChunkLength;
		long								NumBits;
		MyBoolean						NumBitsIsValid = False;
		NumChannelsType			NumChannels;
		MyBoolean						NumChannelsIsValid = False;
		long								SamplingRate;
		MyBoolean						SamplingRateIsValid = False;
		unsigned long				NumSampleFrames;
		MyBoolean						NumSampleFramesIsValid = False;
		MyBoolean						DiskErrorOccurred;

		/*     "FORM" */
		if (!ReadBufferedInput(File,4,CharBuff))
			{
			 DiskErrorPoint1:
				AlertHalt("Unable to read data from the file.",NIL);
			 ExitTheProcedureErrorPoint:
				return;
			}
		if (!MemEqu(CharBuff,"FORM",4))
			{
			 BadFilePoint1:
				AlertHalt("The file is not an AIFF or AIFF-C file.",NIL);
				goto ExitTheProcedureErrorPoint;
			}

		/*     4-byte big endian form chunk length descriptor (minus 8 for "FORM" & this) */
		if (!ReadBufferedUnsignedLongBigEndian(File,(unsigned long*)&FormChunkLength))
			{
				goto DiskErrorPoint1;
			}

		/*     4-byte type */
		/*        "AIFF" = AIFF format file */
		/*        "AIFC" = AIFF-C format file */
		if (!ReadBufferedInput(File,4,CharBuff))
			{
				goto DiskErrorPoint1;
			}
		if (MemEqu(CharBuff,"AIFF",4))
			{
				IsAnAIFFCFile = False;
			}
		else if (MemEqu(CharBuff,"AIFC",4))
			{
				IsAnAIFFCFile = True;
			}
		else
			{
			 UnknownAIFFFilePoint:
				AlertHalt("The file is not a variant of AIFF or AIFF-C file that I can deal with.",NIL);
				goto ExitTheProcedureErrorPoint;
			}
		FormChunkLength -= 4;

		/* now, read in chunks until we die */
		DiskErrorOccurred = False;
		while (!DiskErrorOccurred && (FormChunkLength > 0))
			{
				long							LocalChunkLength;

				/* get the chunk type */
				if (!ReadBufferedInput(File,4,CharBuff))
					{
						goto DiskErrorPoint1;
					}
				/* get the chunk length */
				if (!ReadBufferedUnsignedLongBigEndian(File,(unsigned long*)&LocalChunkLength))
					{
						goto DiskErrorPoint1;
					}
				FormChunkLength -= 8;
				/* adjust for even alignment */
				if ((LocalChunkLength % 2) != 0)
					{
						LocalChunkLength += 1;
					}
				FormChunkLength -= LocalChunkLength;

				/* decode the chunk */
				if (MemEqu(CharBuff,"COMM",4))
					{
						unsigned long				Exponent;
						unsigned long				Mantissa;
						char								StupidExtendedThang[10];

						if (!IsAnAIFFCFile)
							{
								unsigned short		ShortInteger;

								/*   Common Chunk for AIFF files */
								/*     "COMM" */
								/*     4-byte big endian length. */
								/*        always 18 for AIFF files */
								if (LocalChunkLength != 18)
									{
										goto UnknownAIFFFilePoint;
									}

								/*     2-byte big endian number of channels */
								if (!ReadBufferedUnsignedShortBigEndian(File,&ShortInteger))
									{
										goto DiskErrorPoint1;
									}
								switch (ShortInteger)
									{
										default:
											goto UnknownAIFFFilePoint;
										case 1:
											NumChannels = eSampleMono;
											NumChannelsIsValid = True;
											break;
										case 2:
											NumChannels = eSampleStereo;
											NumChannelsIsValid = True;
											break;
									}

								/*     4-byte big endian number of sample frames */
								if (!ReadBufferedUnsignedLongBigEndian(File,&NumSampleFrames))
									{
										goto DiskErrorPoint1;
									}
								NumSampleFramesIsValid = True;

								/*     2-byte big endian number of bits per sample */
								/*        a value in the domain 1..32 */
								if (!ReadBufferedUnsignedShortBigEndian(File,&ShortInteger))
									{
										goto DiskErrorPoint1;
									}
								switch (ShortInteger)
									{
										default:
											goto UnknownAIFFFilePoint;
										case 1: case 2: case 3: case 4:
										case 5: case 6: case 7: case 8:
											NumBits = 8;
											NumBitsIsValid = True;
											break;
										case 9: case 10: case 11: case 12:
										case 13: case 14: case 15: case 16:
											NumBits = 16;
											NumBitsIsValid = True;
											break;
									}

								/*     10-byte extended precision number of frames per second */
								if (!ReadBufferedInput(File,10,StupidExtendedThang))
									{
										goto DiskErrorPoint1;
									}
							}
						 else
							{
								unsigned short			ShortInteger;
								long								Dumper;

								/*   Common Chunk for AIFF-C files */
								/*     "COMM" */
								/*     4-byte big endian length. */
								/*        always 18 for AIFF files */
								if (LocalChunkLength < 22)
									{
										goto UnknownAIFFFilePoint;
									}

								/*     2-byte big endian number of channels */
								if (!ReadBufferedUnsignedShortBigEndian(File,&ShortInteger))
									{
										goto DiskErrorPoint1;
									}
								switch (ShortInteger)
									{
										default:
											goto UnknownAIFFFilePoint;
										case 1:
											NumChannels = eSampleMono;
											NumChannelsIsValid = True;
											break;
										case 2:
											NumChannels = eSampleStereo;
											NumChannelsIsValid = True;
											break;
									}

								/*     4-byte big endian number of sample frames */
								if (!ReadBufferedUnsignedLongBigEndian(File,&NumSampleFrames))
									{
										goto DiskErrorPoint1;
									}
								NumSampleFramesIsValid = True;

								/*     2-byte big endian number of bits per sample */
								/*        a value in the domain 1..32 */
								if (!ReadBufferedUnsignedShortBigEndian(File,&ShortInteger))
									{
										goto DiskErrorPoint1;
									}
								switch (ShortInteger)
									{
										default:
											goto UnknownAIFFFilePoint;
										case 1: case 2: case 3: case 4:
										case 5: case 6: case 7: case 8:
											NumBits = 8;
											NumBitsIsValid = True;
											break;
										case 9: case 10: case 11: case 12:
										case 13: case 14: case 15: case 16:
											NumBits = 16;
											NumBitsIsValid = True;
											break;
									}

								/*     10-byte extended precision number of frames per second */
								if (!ReadBufferedInput(File,10,StupidExtendedThang))
									{
										goto DiskErrorPoint1;
									}

								/*     4-byte character code ID for the compression method */
								/*        "NONE" means there is no compression method used */
								if (!ReadBufferedInput(File,4,CharBuff))
									{
										goto DiskErrorPoint1;
									}
								if (!MemEqu(CharBuff,"NONE",4))
									{
										goto UnknownAIFFFilePoint;
									}

								/*     some characters in a string identifying the compression method */
								/*        this must be padded to an even number of bytes, but the pad is */
								/*        NOT included in the length descriptor for the chunk. */
								/*        for uncompressed data, the string should be */
								/*        "\x0enot compressed\x00", including the null, for 16 bytes. */
								/*        the total chunk length is thus 38 bytes. */
								for (Dumper = 0; Dumper < LocalChunkLength - 22; Dumper += 1)
									{
										unsigned char				Stupid;

										if (!ReadBufferedUnsignedChar(File,&Stupid))
											{
												goto DiskErrorPoint1;
											}
									}
							}

						/* extended 22050 = 400D AC44000000000000 */
						/* extended 22051 = 400D AC46000000000000 */
						/* extended 44100 = 400E AC44000000000000 */
						/* extended 44101 = 400E AC45000000000000 */
						Exponent = (((long)StupidExtendedThang[0] & 0xff) << 8)
							| ((long)StupidExtendedThang[1] & 0xff);
						Mantissa = (((long)StupidExtendedThang[2] & 0xff) << 24)
							| (((long)StupidExtendedThang[3] & 0xff) << 16)
							| (((long)StupidExtendedThang[4] & 0xff) << 8)
							| ((long)StupidExtendedThang[5] & 0xff);
						SamplingRate = (Mantissa >> (0x401e - Exponent));
						if (SamplingRate < MINSAMPLINGRATE)
							{
								SamplingRate = MINSAMPLINGRATE;
							}
						if (SamplingRate > MAXSAMPLINGRATE)
							{
								SamplingRate = MAXSAMPLINGRATE;
							}
						SamplingRateIsValid = True;
					}
				else if (MemEqu(CharBuff,"SSND",4))
					{
						unsigned long				AlignmentFactor;
						unsigned long				OffsetToFirstByte;
						MyBoolean						OddNumberOfSoundBytes;
						MyBoolean						Cancelling = False;

						/*   Sound Data Chunk */
						/*     "SSND" */
						/*     4-byte big endian number of bytes in sample data array */

						/*     4-byte big endian offset to the first byte of sample data in the array */
						if (!ReadBufferedUnsignedLongBigEndian(File,&OffsetToFirstByte))
							{
								goto DiskErrorPoint1;
							}
						if (OffsetToFirstByte != 0)
							{
								goto UnknownAIFFFilePoint;
							}

						/*     4-byte big endian number of bytes to which the sound data is aligned. */
						if (!ReadBufferedUnsignedLongBigEndian(File,&AlignmentFactor))
							{
								goto DiskErrorPoint1;
							}
						if (AlignmentFactor != 0)
							{
								goto UnknownAIFFFilePoint;
							}

						/*     any length vector of raw sound data. */
						/*        this must be padded to an even number of bytes, but the pad is */
						/*        NOT included in the length descriptor for the chunk. */
						/*        Samples are stored in an integral number of bytes, the smallest that */
						/*        is required for the specified number of bits.  If this is not an even */
						/*        multiple of 8, then the data is shifted left and the low bits are zeroed */
						/*        Multichannel sound is interleaved with the left channel first. */
						/* now, deal with the stuff we just got */
						if (!NumBitsIsValid || !NumChannelsIsValid
							|| !SamplingRateIsValid || !NumSampleFramesIsValid)
							{
								goto UnknownAIFFFilePoint;
							}
						LocalChunkLength -= 8; /* chop off alignment & offset from length */
						OddNumberOfSoundBytes = ((LocalChunkLength & 1) != 0);
						if (!OpenSoundChannel(SamplingRate,
							(NumChannels == eSampleStereo) ? eStereo : eMono,
							(NumBits == 8) ? e8bit : e16bit,
							FRAMESPERBUFFER,NUMBEROFBUFFERS,INITIALNUMBUFFERS))
							{
								AlertHalt("Unable to open the sound output device.",NIL);
							}
						 else
							{
								void*							Buffer;
								long							UsedBufferFrames;
								long							FrameCount;
								void*							RawData;

								Buffer = NIL;
								FrameCount = 0;
								switch (NumChannels)
									{
										default:
											EXECUTE(PRERR(ForceAbort,"PlayAIFFFile:  bad num channels"));
											break;
										case eSampleMono:
											switch (NumBits)
												{
													default:
														EXECUTE(PRERR(ForceAbort,"PlayAIFFFile:  bad num bits"));
														break;
													case 8:
														RawData = AllocPtrCanFail(FRAMESPERBUFFER,
															"PlayAIFFFile Temp Buffer");
														if (RawData == NIL)
															{
																AlertHalt("There is not enough memory available "
																	"to play the file.",NIL);
																return;
															}
														while (LocalChunkLength > 0)
															{
																long						BytesThisTime;

																/* make sure there is a buffer to use */
																while (Buffer == NIL)
																	{
																		Buffer = CheckOutSoundBuffer();
																		if (((Buffer != NIL)
																			&& RelinquishCPUJudiciouslyCheckCancel())
																			|| ((Buffer == NIL)
																			&& RelinquishCPUCheckCancel()))
																			{
																				Cancelling = True;
																				goto CancelPoint;
																			}
																	}
																/* fill the buffer in with data */
																if (LocalChunkLength < FRAMESPERBUFFER)
																	{
																		BytesThisTime = LocalChunkLength;
																	}
																 else
																	{
																		BytesThisTime = FRAMESPERBUFFER;
																	}
																LocalChunkLength -= BytesThisTime;
																PRNGCHK(RawData,RawData,BytesThisTime);
																if (!ReadBufferedInput(File,BytesThisTime,(char*)RawData))
																	{
																	 DiskErrorDuringPlaybackPoint:
																		AlertHalt("A disk error occurred during playback.",NIL);
																		DiskErrorOccurred = True;
																		Cancelling = True;
																		goto CancelPoint;
																	}
																UsedBufferFrames = 0;
																while (UsedBufferFrames < BytesThisTime)
																	{
																		PRNGCHK(Buffer,&(((signed char*)Buffer)[UsedBufferFrames]),
																			sizeof(((signed char*)Buffer)[UsedBufferFrames]));
																		((signed char*)Buffer)[UsedBufferFrames]
																			= ((signed char*)RawData)[UsedBufferFrames];
																		UsedBufferFrames += 1;
																	}
																/* submit the buffer */
																FrameCount += UsedBufferFrames;
																UpdateSynthWindow(Window,SamplingRate,FrameCount,0,False);
																SubmitBuffer((char*)Buffer,UsedBufferFrames,NIL,NIL);
																Buffer = NIL;
															}
														break;
													case 16:
														RawData = AllocPtrCanFail(FRAMESPERBUFFER * 2,
															"PlayAIFFFile Temp Buffer");
														if (RawData == NIL)
															{
																AlertHalt("There is not enough memory available "
																	"to play the file.",NIL);
																return;
															}
														while (LocalChunkLength > 0)
															{
																long						BytesThisTime;

																/* make sure there is a buffer to use */
																while (Buffer == NIL)
																	{
																		Buffer = CheckOutSoundBuffer();
																		if (((Buffer != NIL)
																			&& RelinquishCPUJudiciouslyCheckCancel())
																			|| ((Buffer == NIL)
																			&& RelinquishCPUCheckCancel()))
																			{
																				Cancelling = True;
																				goto CancelPoint;
																			}
																	}
																/* fill the buffer in with data */
																if (LocalChunkLength < FRAMESPERBUFFER * 2)
																	{
																		BytesThisTime = LocalChunkLength;
																	}
																 else
																	{
																		BytesThisTime = FRAMESPERBUFFER * 2;
																	}
																LocalChunkLength -= BytesThisTime;
																PRNGCHK(RawData,RawData,BytesThisTime);
																if (!ReadBufferedInput(File,BytesThisTime,(char*)RawData))
																	{
																		goto DiskErrorDuringPlaybackPoint;
																	}
																UsedBufferFrames = 0;
																while (UsedBufferFrames < BytesThisTime / 2)
																	{
																		PRNGCHK(Buffer,&(((signed short*)Buffer)[UsedBufferFrames]),
																			sizeof(((signed short*)Buffer)[UsedBufferFrames]));
																		((signed short*)Buffer)[UsedBufferFrames]
																			= (((short)((signed char*)RawData)[2 * UsedBufferFrames] & 0xff) << 8)
																			| (((signed char*)RawData)[2 * UsedBufferFrames + 1] & 0xff);
																		UsedBufferFrames += 1;
																	}
																/* submit the buffer */
																FrameCount += UsedBufferFrames;
																UpdateSynthWindow(Window,SamplingRate,FrameCount,0,False);
																SubmitBuffer((char*)Buffer,UsedBufferFrames,NIL,NIL);
																Buffer = NIL;
															}
														break;
												}
											break;
										case eSampleStereo:
											switch (NumBits)
												{
													default:
														EXECUTE(PRERR(ForceAbort,"PlayAIFFFile:  bad num bits"));
														break;
													case 8:
														RawData = AllocPtrCanFail(FRAMESPERBUFFER * 2,
															"PlayAIFFFile Temp Buffer");
														if (RawData == NIL)
															{
																AlertHalt("There is not enough memory available "
																	"to play the file.",NIL);
																return;
															}
														while (LocalChunkLength > 0)
															{
																long						BytesThisTime;

																/* make sure there is a buffer to use */
																while (Buffer == NIL)
																	{
																		Buffer = CheckOutSoundBuffer();
																		if (((Buffer != NIL)
																			&& RelinquishCPUJudiciouslyCheckCancel())
																			|| ((Buffer == NIL)
																			&& RelinquishCPUCheckCancel()))
																			{
																				Cancelling = True;
																				goto CancelPoint;
																			}
																	}
																/* fill the buffer in with data */
																if (LocalChunkLength < FRAMESPERBUFFER * 2)
																	{
																		BytesThisTime = LocalChunkLength;
																	}
																 else
																	{
																		BytesThisTime = FRAMESPERBUFFER * 2;
																	}
																LocalChunkLength -= BytesThisTime;
																PRNGCHK(RawData,RawData,BytesThisTime);
																if (!ReadBufferedInput(File,BytesThisTime,(char*)RawData))
																	{
																		goto DiskErrorDuringPlaybackPoint;
																	}
																UsedBufferFrames = 0;
																while (UsedBufferFrames < BytesThisTime)
																	{
																		PRNGCHK(Buffer,&(((signed char*)Buffer)[UsedBufferFrames]),
																			sizeof(((signed char*)Buffer)[UsedBufferFrames]));
																		((signed char*)Buffer)[UsedBufferFrames]
																			= ((signed char*)RawData)[UsedBufferFrames];
																		UsedBufferFrames += 1;
																	}
																/* submit the buffer */
																FrameCount += UsedBufferFrames / 2;
																UpdateSynthWindow(Window,SamplingRate,FrameCount,0,False);
																SubmitBuffer((char*)Buffer,UsedBufferFrames / 2,NIL,NIL);
																Buffer = NIL;
															}
														break;
													case 16:
														RawData = AllocPtrCanFail(FRAMESPERBUFFER * 4,
															"PlayAIFFFile Temp Buffer");
														if (RawData == NIL)
															{
																AlertHalt("There is not enough memory available "
																	"to play the file.",NIL);
																return;
															}
														while (LocalChunkLength > 0)
															{
																long						BytesThisTime;

																/* make sure there is a buffer to use */
																while (Buffer == NIL)
																	{
																		Buffer = CheckOutSoundBuffer();
																		if (((Buffer != NIL)
																			&& RelinquishCPUJudiciouslyCheckCancel())
																			|| ((Buffer == NIL)
																			&& RelinquishCPUCheckCancel()))
																			{
																				Cancelling = True;
																				goto CancelPoint;
																			}
																	}
																/* fill the buffer in with data */
																if (LocalChunkLength < FRAMESPERBUFFER * 4)
																	{
																		BytesThisTime = LocalChunkLength;
																	}
																 else
																	{
																		BytesThisTime = FRAMESPERBUFFER * 4;
																	}
																LocalChunkLength -= BytesThisTime;
																PRNGCHK(RawData,RawData,BytesThisTime);
																if (!ReadBufferedInput(File,BytesThisTime,(char*)RawData))
																	{
																		goto DiskErrorDuringPlaybackPoint;
																	}
																UsedBufferFrames = 0;
																while (UsedBufferFrames < BytesThisTime / 2)
																	{
																		PRNGCHK(Buffer,&(((signed short*)Buffer)[UsedBufferFrames]),
																			sizeof(((signed short*)Buffer)[UsedBufferFrames]));
																		((signed short*)Buffer)[UsedBufferFrames]
																			= (((short)((signed char*)RawData)[2 * UsedBufferFrames] & 0xff) << 8)
																			| (((signed char*)RawData)[2 * UsedBufferFrames + 1] & 0xff);
																		UsedBufferFrames += 1;
																	}
																/* submit the buffer */
																FrameCount += UsedBufferFrames / 2;
																UpdateSynthWindow(Window,SamplingRate,FrameCount,0,False);
																SubmitBuffer((char*)Buffer,UsedBufferFrames / 2,NIL,NIL);
																Buffer = NIL;
															}
														break;
												}
											break;
									}
								if (Buffer != NIL)
									{
										SubmitBuffer((char*)Buffer,UsedBufferFrames,NIL,NIL);
										Buffer = NIL;
									}
								if (OddNumberOfSoundBytes)
									{
										unsigned char					StupidChar;

										if (!ReadBufferedUnsignedChar(File,&StupidChar))
											{
												DiskErrorOccurred = True;
											}
									}
							 CancelPoint: /* jump here to cancel playback */
								ReleasePtr((char*)RawData);
								if (Cancelling)
									{
										KillSoundChannel(); /* don't wait */
									}
								 else
									{
										CloseSoundChannel(NIL,NIL);
									}
							}
					}
				else
					{
						/* just read the data & get rid of it */
						while (LocalChunkLength > 0)
							{
								unsigned char				Stupid;

								if (!ReadBufferedUnsignedChar(File,&Stupid))
									{
										goto DiskErrorPoint1;
									}
								LocalChunkLength -= 1;
							}
					}
			}
		if (DiskErrorOccurred)
			{
				goto DiskErrorPoint1;
			}
	}


/* ask the user for a file, and then try to play back its contents */
void									PlayAIFFFile(void)
	{
		FileSpec*					WhereIsTheFile;

		WhereIsTheFile = GetFileAny();
		if (WhereIsTheFile != NIL)
			{
				PlayAIFFFileSpec(WhereIsTheFile);
				DisposeFileSpec(WhereIsTheFile);
			}
	}


/* play specified AIFF file */
void									PlayAIFFFileSpec(struct FileSpec* Where)
	{
		FileType*					FileDesc;

		CheckPtrExistence(Where);
		if (OpenFile(Where,&FileDesc,eReadOnly))
			{
				BufferedInputRec*		File;

				File = NewBufferedInput(FileDesc);
				if (File != NIL)
					{
						SynthWinRec*				Window;

						Window = NewSynthWindow(4,False/*don't show clipping*/);
						if (Window != NIL)
							{
								PlayTheFile(File,Window);
								DisposeSynthWindow(Window);
							}
						 else
							{
								AlertHalt("There is not enough memory available to play the file.",NIL);
							}
						EndBufferedInput(File);
					}
				 else
					{
						AlertHalt("There is not enough memory available to play the file.",NIL);
					}
				CloseFile(FileDesc);
			}
		 else
			{
				AlertHalt("Unable to open the file for reading.",NIL);
			}
	}
