/*
**	$Id: Beep.c,v 1.3 92/05/01 12:52:01 olsen Sta Locker: olsen $
**	$Revision: 1.3 $
**	$Date: 92/05/01 12:52:01 $
**
**	Sound support routines
**
**	Copyright © 1990-1992 by Olaf `Olsen' Barthel & MXM
**		All Rights Reserved
*/

#include "termGlobal.h"

	/* IFF Sound `8SVX' voice header. */

struct Voice8Header
{
	ULONG	oneShotHiSamples,	/* # samples in the high octave 1-shot part */
		repeatHiSamples,	/* # samples in the high octave repeat part */
		samplesPerHiCycle;	/* # samples/cycle in high octave, else 0 */
	UWORD	samplesPerSec;		/* data sampling rate */
	UBYTE	ctOctave,		/* # of octaves of waveforms */
		sCompression;		/* data compression technique used */
	LONG	volume;			/* playback nominal volume from 0 to Unity
					 * (full volume). Map this value into
					 * the output hardware's dynamic range.
					 */
};

	/* Small sound information. */

struct SoundInfo
{
	APTR	Data;		/* Data pointer. */

	LONG	Length;		/* Real length. */
	ULONG	Rate;		/* Recording rate. */
	WORD	Volume;		/* Sound volume. */
};

	/* Local sound info. */

STATIC struct SoundInfo	SoundInfo;
STATIC BYTE		HasSound	= FALSE,
			SoundPlayed	= FALSE;

	/* CreateBeep():
	 *
	 *	Set up the audio.device for a decent beep sound.
	 */

BYTE
CreateBeep()
{
		/* Do we already have the resources we need? */

	if(!AudioBlock)
	{
		struct MsgPort *AudioPort;

			/* No sound so far. */

		SoundPlayed = FALSE;

			/* Create the IO reply port. */

		if(AudioPort = (struct MsgPort *)CreateMsgPort())
		{
				/* Create the audio IO info. */

			if(AudioBlock = (struct IOAudio *)CreateIORequest(AudioPort,sizeof(struct IOAudio)))
			{
					/* Open audio.device */

				if(!OpenDevice(AUDIONAME,0,AudioBlock,0))
					return(TRUE);

				DeleteIORequest(AudioBlock);
			}

			DeleteMsgPort(AudioPort);
		}

		AudioBlock = NULL;

		return(FALSE);
	}
	else
		return(TRUE);
}

	/* DeleteBeep():
	 *
	 *	Remove the data allocated for the beep sound.
	 */

VOID
DeleteBeep()
{
	if(AudioBlock)
	{
		if(AudioBlock -> ioa_Request . io_Device)
			CloseDevice(AudioBlock);

		if(AudioBlock -> ioa_Request . io_Message . mn_ReplyPort)
			DeleteMsgPort(AudioBlock -> ioa_Request . io_Message . mn_ReplyPort);

		DeleteIORequest(AudioBlock);

		AudioBlock = NULL;
	}

	if(HasSound)
	{
		FreeVec(SoundInfo . Data);

		HasSound = FALSE;
	}
}

	/* ClearAudio():
	 *
	 *	Clear the audio control block for reuse.
	 */

VOID
ClearAudio()
{
		/* Remove the request. */

	WaitIO(AudioBlock);

		/* Clear the signal bit. */

	SetSignal(0,SIG_AUDIO);

		/* Free the channels we had allocated. */

	AudioBlock -> ioa_Request . io_Command = ADCMD_FREE;

	DoIO(AudioBlock);

		/* No sound running. */

	SoundPlayed = FALSE;
}

	/* Beep():
	 *
	 *	Produce a decent beep sound.
	 */

VOID
Beep()
{
	if(AudioBlock)
	{
		BYTE PlayItAgainSam;	/* Note: `Sam' is Sam Dicker, the author of
					 *       the original audio.device implementation.
					 */

			/* AudioRequest has returned. */

		if(SetSignal(0,0) & SIG_AUDIO)
			ClearAudio();

			/* Check whether we are to play the sound or not. */

		if(!SoundPlayed)
			PlayItAgainSam = TRUE;
		else
		{
			if(CheckIO(AudioBlock))
				PlayItAgainSam = TRUE;
			else
				PlayItAgainSam = FALSE;
		}

			/* May we play the sound? */

		if(PlayItAgainSam)
		{
				/* Allocate a sound channel, we don't want to
				 * wait for it, the `beep' sound is to played
				 * right now.
				 */

			AudioBlock -> ioa_Request . io_Command				= ADCMD_ALLOCATE;
			AudioBlock -> ioa_Request . io_Flags				= ADIOF_NOWAIT | IOF_QUICK;
			AudioBlock -> ioa_Request . io_Message . mn_Node . ln_Pri	= 80;
			AudioBlock -> ioa_Data						= AnyChannel;
			AudioBlock -> ioa_Length					= 4;

				/* Try the allocation. */

			BeginIO(AudioBlock);

				/* If still in progress, no channel is available yet. */

			if(!CheckIO(AudioBlock))
			{
					/* Abort the allocation. */

				AbortIO(AudioBlock);

					/* Wait for request to be returned. */

				WaitIO(AudioBlock);
			}
			else
			{
					/* Wait for request to be returned. */

				if(!WaitIO(AudioBlock))
				{
						/* Set up the sound IO data. */

					if(HasSound)
					{
						AudioBlock -> ioa_Period	= SoundInfo . Rate;
						AudioBlock -> ioa_Volume	= SoundInfo . Volume;
						AudioBlock -> ioa_Cycles	= 1;
						AudioBlock -> ioa_Data		= SoundInfo . Data;
						AudioBlock -> ioa_Length	= SoundInfo . Length;
					}
					else
					{
						AudioBlock -> ioa_Period	= 223;
						AudioBlock -> ioa_Volume	= 64 / 2;
						AudioBlock -> ioa_Cycles	= 150;
						AudioBlock -> ioa_Data		= &SineWave[0];
						AudioBlock -> ioa_Length	= 8;
					}

					AudioBlock -> ioa_Request . io_Command	= CMD_WRITE;
					AudioBlock -> ioa_Request . io_Flags	= ADIOF_PERVOL;

						/* Start the sound. */

					BeginIO(AudioBlock);

					SoundPlayed = TRUE;
				}
			}
		}
	}
}

	/* OpenSound(UBYTE *Name):
	 *
	 *	Load an IFF-8SVX sound file instead of the standard
	 *	beep (yes, this is a zany feature nobody will ever need!).
	 */

BYTE
OpenSound(UBYTE *Name)
{
	STATIC ULONG SoundProps[] = { '8SVX', 'VHDR' };

	struct IFFHandle	*Handle;
	struct StoredProperty	*Prop;
	struct Voice8Header	*VoiceHeader;

	BYTE			 Success = FALSE;

		/* Allocate an IFF handle. */

	if(Handle = AllocIFF())
	{
			/* Open the file. */

		if(Handle -> iff_Stream = Open(Name,MODE_OLDFILE))
		{
				/* Say it's a DOS file. */

			InitIFFasDOS(Handle);

				/* Open the file for reading. */

			if(!OpenIFF(Handle,IFFF_READ))
			{
					/* Remember VHDR-chunks encountered. */

				if(!PropChunks(Handle,&SoundProps[0],1))
				{
						/* Stop at the body chunk. */

					if(!StopChunk(Handle,'8SVX','BODY'))
					{
							/* Start scanning... */

						if(!ParseIFF(Handle,IFFPARSE_SCAN))
						{
								/* Obtain the stored VHDR-chunk. */

							if(Prop = FindProp(Handle,'8SVX','VHDR'))
							{
								VoiceHeader = (struct Voice8Header *)Prop -> sp_Data;

									/* Compressed data not supported so far. */

								if(!VoiceHeader -> sCompression)
								{
									struct ContextNode *ContextNode;

										/* Inquire current chunk. */

									if(ContextNode = CurrentChunk(Handle))
									{
											/* We don't support double-buffering, this
											 * is only a *simple* example.
											 */

										if(ContextNode -> cn_Size < 102400)
										{
												/* Allocate the data storage. */

											if(SoundInfo . Data = AllocVec(ContextNode -> cn_Size,MEMF_CHIP))
											{
													/* Play either the one-shot or the continuous
													 * part, not both.
													 */

												SoundInfo . Length	= VoiceHeader -> oneShotHiSamples ? VoiceHeader -> oneShotHiSamples : VoiceHeader -> repeatHiSamples;

													/* Calculate recording rate. */

												SoundInfo . Rate	= (GfxBase -> DisplayFlags & PAL ? 3546895 : 3579545) / VoiceHeader -> samplesPerSec;

													/* Calculate volume. */

												SoundInfo . Volume	= (VoiceHeader -> volume * 64) / 0x10000;

													/* Read the data. */

												if(ReadChunkBytes(Handle,SoundInfo . Data,ContextNode -> cn_Size))
													Success = TRUE;
												else
													FreeVec(SoundInfo . Data);
											}
										}
									}
								}
							}
						}
					}
				}

					/* Make iffparse clean up after us. */

				CloseIFF(Handle);
			}

				/* Close the DOS handle. */

			Close(Handle -> iff_Stream);
		}

			/* Free the IFF handle. */

		FreeIFF(Handle);
	}

		/* Remember success/failure. */

	HasSound = Success;

	return(Success);
}
