//////////
//
//	File:		AIFFWriter.c
//
//	Contains:	Header for dispatching a sound hardware output ('sdev') component.
//
//	Written by:	Mark Cookson, Apple Developer Technical Support
//				Based on code by Kip Olson.
//	Revised by: Tim Monroe
//
//	Copyright:	© 1993-1999 by Apple Computer, Inc., all rights reserved.
//
//	Change History (most recent first):
//
//	   <2>	 	03/05/99	rtm		further work to bring into line with existing component sample code
//	   <1>	 	02/25/99	rtm		first inherited file; changed some compiler conditionals
//									to support Windows compiles
//
//	You may incorporate this sample code into your applications without
//	restriction, though the sample code has been provided "AS IS" and the
//	responsibility for its operation is 100% yours. However, what you are
//	not permitted to do is to redistribute the source as "Apple Sample
//	Code" after having made changes. If you're going to re-distribute the
//	source, we require that you make it clear in the source that the code
//	was descended from Apple Sample Code, but that you've made changes.
//
//////////

#ifndef __AIFFWRITER__
#define __AIFFWRITER__


//////////
//
// header files
//
//////////

#ifndef __COMPONENTS__
#include <Components.h>
#endif

#ifndef __MACMEMORY__
#include <MacMemory.h>
#endif

#ifndef __MACERRORS__
#include <MacErrors.h>
#endif

#ifndef __SOUND__
#include <Sound.h>
#endif

#ifndef __TIMER__
#include <Timer.h>
#endif

#ifndef __FIXMATH__
#include <FixMath.h>
#endif

#ifndef __SCRIPT__
#include <Script.h>
#endif


//////////
//
// compiler flags
//
//////////

#define FakeInterrupts					1


//////////
//
// constants
//
//////////

#define kSoundComponentVersion			0x00010000				// version for this component
#define kSndFileName					"\pAIFF Writer output"	// name for sound file data is written to
#define kAIFFWriterName					"\pAIFF Writer"

#define kSecondsInIOBuffer				1						// number of seconds of audio data in each I/O buffer

#define kAIFFWriterSifterID				128
#define kAIFFWriterPanelID				130
#define kAIFFWriterSubType				'hack'					// OS Type for component
#define kAIFFCreator					FOUR_CHAR_CODE('TVOD')
#define kAIFFFileType					FOUR_CHAR_CODE('AIFF')

enum {
	kHardwareVolumeSteps 				= 16,							// levels of volume adjustments
	kHardwareSampleCount 				= 1024,							// number of samples in hardware buffer
	kHardwareByteSize 					= kHardwareSampleCount * 2 * 2	// enough for 16 bit stereo
};


//////////
//
// data types
//
//////////

#if PRAGMA_STRUCT_ALIGN
	#pragma options align=mac68k
#elif PRAGMA_STRUCT_PACKPUSH
	#pragma pack(push, 2)
#elif PRAGMA_STRUCT_PACK
	#pragma pack(2)
#endif

typedef struct myTMTask myTMTask;
typedef struct myTMTask *myTMTaskPtr;

typedef struct SoundOutputGlobals SoundOutputGlobals;
typedef struct SoundOutputGlobals *SoundOutputGlobalsPtr;

// a Time Manager task record that includes space for my globals at the end
struct myTMTask {
	TMTask							task;
	SoundOutputGlobalsPtr			globals;
};

// i/o buffer structure
typedef struct IOBuffer {
	ParamBlockRec					iopb;						// i/o parameter block
	long							byteCount;					// no. bytes in buffer
	Ptr								buffer;						// buffer for data
} IOBuffer, *IOBufferPtr;

typedef struct HardwarePreferences {
	long							size;						// size of current preferences
	Fixed							version;					// 16.16 value for version
	unsigned long					volume;						// current hardware volume
	UnsignedFixed					sampleRate;					// current sample rate
	short							sampleSize;					// current sample size
	short							numChannels;				// current num channels
	short							muteState;					// current hardware mute state
	short							sampleCount;				// size of interrupt buffer in samples
} HardwarePreferences, *HardwarePrefsPtr, **HardwarePrefsHandle;

struct SoundOutputGlobals {
	// component stuff
	ComponentInstance				sourceComponent;			// component to call when hardware needs more data
	SoundComponentDataPtr			sourceDataPtr;				// pointer to source data structure
	SoundComponentData				outputData;
	ComponentInstance				self;						// our component's instance

	// fields specific to this component implementation
	HardwarePrefsHandle				prefs;						// user's preferences
	Boolean							prefsChanged;				// true if prefs need to be saved to disk
	Boolean							hardwareOn;					// true if hardware is on, false if it is off
	Boolean							inAppHeap;					// true if loaded into an app's heap
	Boolean							padding1;					// padding to align struct
	unsigned long					ioBufferSize;				// size of i/o buffer in bytes
	long							interruptInterval;			// time in microseconds between interrupts
	myTMTask						tmTask;						// time manager task record used to trigger interrupt
	long							headerLen;					// length of AIFF header written to beginning of file
	short							fRefNum;					// fref of file to save data into
	short							currentIndex;				// current buffer to save data into
	IOBuffer						ioBuffers[2];				// buffer descriptions

#ifdef FakeInterrupts
	unsigned long					nextTime;					// next time to interrupt
#endif
};

#if PRAGMA_STRUCT_ALIGN
	#pragma options align=reset
#elif PRAGMA_STRUCT_PACKPUSH
	#pragma pack(pop)
#elif PRAGMA_STRUCT_PACK
	#pragma pack()
#endif


//////////
//
// function prototypes
//
//////////

OSErr					SetupHardware (SoundOutputGlobalsPtr globals);
void					ReleaseHardware (SoundOutputGlobalsPtr globals);
void					StartHardware (SoundOutputGlobalsPtr globals);
void					StopHardware (SoundOutputGlobalsPtr globals);
void					ResumeHardware (SoundOutputGlobalsPtr globals);
void					CopySamplesToHardware (SoundOutputGlobalsPtr globals, SoundComponentDataPtr siftPtr);
void					OutputToFile (SoundComponentDataPtr siftPtr, void *dest, long sampleCount);
Boolean					FakeInterrupt (void);
void					InterruptRoutine (SoundOutputGlobalsPtr globals);
SoundComponentDataPtr	GetMoreSource (SoundOutputGlobalsPtr globals);
OSErr					SetupOutputFile (SoundOutputGlobalsPtr globals);
OSErr					CloseOutputFile (SoundOutputGlobalsPtr globals);
OSErr					GetPreferences (SoundOutputGlobalsPtr globals);
OSErr					InitFromPreferences (SoundOutputGlobalsPtr globals);

//#if TARGET_CPU_68K
//void					TMInterrupt (myTMTaskPtr taskPtr:__a1);
//#else
void					TMInterrupt (myTMTaskPtr taskPtr);
//#endif

unsigned long			MicroSeconds (void) ONEWORDINLINE(0xA193);
extern Ptr				GetRegisterA1 (void) ONEWORDINLINE(0x2009);	// MOVE.L	A1,D0


//////////
//
// failure handling macros
//
//////////

/*
	Some macros used to check for errors and also to allow for
	handling them by using a goto statement.  This makes the source
	code easier to read.  It will break into the debugger with a
	message showing the condition that caused the failure.  In some
	of the macros the debug message is removed but goto remains.  In
	other macros all of it is removed when doing a non-debug build.

    Note that these macros use the "\p" construct for creating
	Pascal strings at compile time.  Most non-Mac compilers do
	not recognize this, give a warning, and put 'p' as the first
	character of the string.  You can ignore the warning because
	the non-Mac version of DebugStr deals with this just fine.
	For Microsoft's Visual C++, we suppress the warning below.
*/

#if defined(_MSC_VER) && !defined(__MWERKS__)
	// Visual C++ from Microsoft
	#pragma warning(disable:4129) 							// unrecognized character escape sequence
#endif

// This checks for the exception, and if true then goto handler
#ifdef _DEBUG
#define FailIf(cond, handler)								\
	if (cond) {												\
		DebugStr((ConstStr255Param)"\p"#cond " goto " #handler);	\
		goto handler;										\
	} else 0
#else
#define FailIf(cond, handler)								\
	if (cond) {												\
		goto handler;										\
	} else 0
#endif

// This checks for the exception, and if true do the action and goto handler
#ifdef _DEBUG
#define FailWithAction(cond, action, handler)				\
	if (cond) {												\
		DebugStr((ConstStr255Param)"\p"#cond " goto " #handler);	\
		{ action; }											\
		goto handler;										\
	} else 0
#else
#define FailWithAction(cond, action, handler)				\
	if (cond) {												\
		{ action; }											\
		goto handler;										\
	} else 0
#endif

// This will insert debugging code in the application to check conditions
// and displays the condition in the debugger if true.  This code is
// completely removed in non-debug builds.
#ifdef _DEBUG
#define FailMessage(cond)									\
	if (cond)												\
		DebugStr((ConstStr255Param)"\p"#cond);				\
	else 0
#else
#define FailMessage(cond)
#endif

// This allows you to test for the result of a condition (i.e. CloseComponent)
// and break if it returns a non zero result, otherwise it ignores the result.
// When a non-debug build is done, the result is ignored.
#ifdef _DEBUG
#define ErrorMessage(cond)									\
	if (cond)												\
		DebugStr((ConstStr255Param)"\p"#cond);				\
	else 0
#else
#define ErrorMessage(cond)		cond
#endif

// This will display a given message in the debugger; this code is completely
// removed in non-debug builds.
#ifdef _DEBUG
#define DebugMessage(s)			DebugString((ConstStr255Param)s)
#else
#define DebugMessage(s)
#endif


#endif // __AIFFWRITER__
