//////////
//
//	File:		AudioConvert.c
//
//	Contains:	Sample code showing how to use the Sound Manager's SoundConverter routines. 
//
//	Written by:	Jim Reekes
//	Revised by: Tim Monroe
//
//	Copyright:	© 1999 by Apple Computer, Inc., all rights reserved.
//
//	Change History (most recent first):
//
//	   <5>	 	03/29/00	rtm		made changes to get things running under CarbonLib
//	   <4>	 	07/13/99	rtm		final adjustments; fixed cross-platform problems resulting from using
//									kSoundNotCompressed ('NONE') format (thanks to EJ Campbell!); see NOTE below
//	   <3>	 	07/07/99	rtm		further work; revised coding style; added some comments
//	   <2>	 	03/12/99	rtm		added (theDialog == NULL) check to AudConv_SFGetDialogHook to prevent
//									crashing on Windows; simplified UPP handling
//	   <1>	 	03/09/99	rtm		first file from jr; added some comments; added Windows-specific code
//									and endian macros
//
//	This code shows how to use the SoundConverter routines (introduced in Sound Manager version 3.2). It
//	provides routines that convert an AIFF or AIFF-C file into either a QuickTime movie or into an AIFF or
//	AIFF-C file. The user can select the desired target format and settings in a dialog box displayed by
//	a call to SCRequestImageSettings.
//
//	You should note in particular that this sample code shows how to handle the compression parameters chunk
//	that might be present in an AIFF or AIFF-C file. The compression parameters chunk contains parameters
//	that are required to decompress the audio data in the file.
//
//	NOTE: Do not use kSoundNotCompressed as an output format for multi-byte audio data if you want to create
//	movies that are to be shared across platforms. QuickTime needs to know the endianness of the sound data,
//	and kSoundNotCompressed does not indicate whether the data is big- or little-endian. Since in this sample
//	code we are reading our data from an AIFF file, we specify that any non-compressed multi-byte data is of
//	the format k16BitBigEndianFormat. 
//
//////////

#include "AudioConvert.h"


//////////
//
// main/WinMain 
// The main function for this application.
//
//////////

#if TARGET_OS_MAC
void main (void)
#elif TARGET_OS_WIN32
int CALLBACK WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR theCmdLine, int nCmdShow)
#endif
{
	SoundComponentData		myDestInfo;
#if TARGET_API_MAC_CARBON
	NavReplyRecord			myReply;
	NavDialogOptions		myDialogOptions;
	AEKeyword				myKeyword;
	DescType				myActualType;
	Size					myActualSize = 0;
	NavEventUPP				myEventUPP = NewNavEventUPP(AudConv_HandleNavEvent);
	NavObjectFilterUPP		myFilterProc = NewNavObjectFilterUPP(AudConv_NavObjectFilterProc);
#else
	OSType					myTypeList[2] = {kQTFileTypeAIFF, kQTFileTypeAIFC};
	StandardFileReply		myReply;
#endif
	FSSpec					myInputFSSpec;
	FSSpec					myOutputFSSpec;
	Boolean					myOutputAIFF;
	Str255					myAppName;
	OSErr					myErr = noErr;

#if TARGET_OS_WIN32
	char					myFileName[MAX_PATH];
	DWORD					myLength;
	short 					myAppResFile = -1;					// file reference number for this application's resource file
	
	InitializeQTML(0L);											// initialize QuickTime Media Layer
#endif	

#if TARGET_OS_MAC
#if !TARGET_API_MAC_CARBON	
	MaxApplZone();												// init everything
	InitGraf(&qd.thePort);
	InitFonts();
	FlushEvents(everyEvent,0);
	InitWindows();
	InitMenus();
	InitDialogs(NULL);
	TEInit();
#endif	
	InitCursor();
#endif	
	
	myErr = EnterMovies();
	FailIf(myErr != noErr, Exit);

#if TARGET_OS_WIN32
	// open the application's resource file, if it exists
	myLength = GetModuleFileName(NULL, myFileName, MAX_PATH);	// NULL means: the current process
	if (myLength != 0) {
		FSSpec				myFSSpec;
		
		NativePathNameToFSSpec(myFileName, &myFSSpec, 0L);

		myAppResFile = FSpOpenResFile(&myFSSpec, fsRdWrPerm);
		if (myAppResFile != -1)
			UseResFile(myAppResFile);
	}
#endif
		
	GetIndString(myAppName, rStringsResID, rAppNameStringIndex);
	myOutputAIFF = false;										// set default file format to 'MooV' file

#if TARGET_API_MAC_CARBON
	// we are running under Carbon; use Navigation Services
	
	//////////
	//
	// elicit an AIFF or an AIFC file from the user
	//
	//////////
	
	// specify the options for the dialog box
	NavGetDefaultDialogOptions(&myDialogOptions);
	myDialogOptions.dialogOptionFlags -= kNavNoTypePopup;
	myDialogOptions.dialogOptionFlags -= kNavAllowMultipleFiles;
	BlockMoveData(myAppName, myDialogOptions.clientName, myAppName[0] + 1);

	myErr = NavGetFile(NULL, &myReply, &myDialogOptions, myEventUPP, NULL, (NavObjectFilterUPP)myFilterProc, NULL, NULL);
	if ((myErr == noErr) && myReply.validRecord) {
		StringPtr 				myPrompt = AudConv_ConvertCToPascalString(kACSavePrompt);
		StringPtr 				myFileName = AudConv_ConvertCToPascalString(kMovieFileSaveName);
		NavMenuItemSpecHandle	myMenuItemHandle = NULL;
		
		// get the FSSpec for the selected file
		myErr = AEGetNthPtr(&(myReply.selection), 1, typeFSS, &myKeyword, &myActualType, &myInputFSSpec, sizeof(FSSpec), &myActualSize);
		NavDisposeReply(&myReply);

		// elicit a file to save the converted sound data into;
		// we want to allow the user to save as a movie or as an AIFF file
		myMenuItemHandle = (NavMenuItemSpecHandle)NewHandleClear(sizeof(NavMenuItemSpec));
		if (myMenuItemHandle != NULL) {
			(**myMenuItemHandle).version = kNavMenuItemSpecVersion;
			(**myMenuItemHandle).menuCreator = FOUR_CHAR_CODE('AuCo');
			(**myMenuItemHandle).menuType = kQTFileTypeAIFF;
			GetIndString((**myMenuItemHandle).menuItemName, rStringsResID, rAIFFMenuLabelIndex);
		}
		
		NavGetDefaultDialogOptions(&myDialogOptions);
		myDialogOptions.dialogOptionFlags -= kNavAllowStationery;
		myDialogOptions.dialogOptionFlags += kNavDontAutoTranslate;
		myDialogOptions.popupExtension = myMenuItemHandle;
		BlockMoveData(myFileName, myDialogOptions.savedFileName, myFileName[0] + 1);
		BlockMoveData(myAppName, myDialogOptions.clientName, myAppName[0] + 1);
		BlockMoveData(myPrompt, myDialogOptions.message, myPrompt[0] + 1);
		
		myErr = NavPutFile(NULL, &myReply, &myDialogOptions, myEventUPP, MovieFileType, sigMoviePlayer, &myOutputAIFF);
		if ((myErr == noErr) && myReply.validRecord) {
			
			// get the FSSpec for the selected file
			myErr = AEGetNthPtr(&(myReply.selection), 1, typeFSS, &myKeyword, &myActualType, &myOutputFSSpec, sizeof(FSSpec), &myActualSize);
			
			if (myReply.replacing) {
				myErr = FSpDelete(&myOutputFSSpec);
				FailIf(myErr != noErr, Failure);
			}

			myErr = AudConv_GetOutputFormat(&myDestInfo);
			if (myErr == noErr) {
				if (myOutputAIFF)
					myErr = AudConv_ConvertToAIFF(&myInputFSSpec, &myOutputFSSpec, &myDestInfo);
				else
					myErr = AudConv_ConvertToMovie(&myInputFSSpec, &myOutputFSSpec, &myDestInfo);
				FailIf(myErr != noErr, Failure);
			}
			
			if (myErr == userCanceledErr)
				myErr = noErr;
			FailIf(myErr != noErr, Failure);
			
			NavDisposeReply(&myReply);
		}

		free(myPrompt);
		free(myFileName);
	}
	
	DisposeNavEventUPP(myEventUPP);
	DisposeNavObjectFilterUPP(myFilterProc);

#else
	// we are NOT running under Carbon; use Standard File Package
	
	//////////
	//
	// elicit an AIFF or an AIFC file from the user
	//
	//////////

	StandardGetFile(NULL, 2, myTypeList, &myReply);
	if (myReply.sfGood) {
		DlgHookYDUPP		myDlgHookUPP = NULL;
		Point				myTopLeft;
		
		myDlgHookUPP = NewDlgHookYDProc(AudConv_SFGetDialogHook);
		
		myInputFSSpec = myReply.sfFile;							// save input file spec

		// elicit a file to save the converted sound data into
		myTopLeft.v = -1;										// -1,-1 means to center the dialog
		myTopLeft.h = -1;
		CustomPutFile(NULL, c2pstr(kAIFFFileSaveName), &myReply, rCustomGetFileDialog, myTopLeft, myDlgHookUPP, NULL, NULL, NULL, &myOutputAIFF);
		if (myReply.sfGood) {
			myOutputFSSpec = myReply.sfFile;						// save output file spec
			
			if (myReply.sfReplacing) {
				myErr = FSpDelete(&myOutputFSSpec);
				FailIf(myErr != noErr, Failure);
			}

			myErr = AudConv_GetOutputFormat(&myDestInfo);
			if (myErr == noErr) {
				if (myOutputAIFF)
					myErr = AudConv_ConvertToAIFF(&myInputFSSpec, &myOutputFSSpec, &myDestInfo);
				else
					myErr = AudConv_ConvertToMovie(&myInputFSSpec, &myOutputFSSpec, &myDestInfo);
				FailIf(myErr != noErr, Failure);
			}
			
			if (myErr == userCanceledErr)
				myErr = noErr;
			FailIf(myErr != noErr, Failure);
		}
	}
#endif

Failure:
	ExitMovies();
Exit:

#if TARGET_OS_WIN32
	// terminate the QuickTime Media Layer
	TerminateQTML();
	return(1);
#endif	

#if TARGET_OS_MAC
	return;
#endif	
}


//////////
//
// AudConv_ConvertToAIFF 
// Read the source AIFF file and convert to the destination format; output a new AIFF file.
// The new file must include the compression parameters chunk, if used.
//
//////////

OSErr AudConv_ConvertToAIFF (ConstFSSpecPtr theInputFSSpec, ConstFSSpecPtr theOutputFSSpec, SoundComponentDataPtr theDestInfo)
{
	SoundComponentData		mySourceInfo;
	Handle					mySourceCompParamsHandle = NULL;
	Handle					myDestHandle = NULL;
	Handle					myDestCompParamsHandle = NULL;
	long					mySourceOffset;
	long					mySourceSize;
	short					myInputFile;
	short					myOutputFile;
	long					myCount;
	OSErr					myErr = noErr;

	// open the source file and parse it
	myErr = FSpOpenDF(theInputFSSpec, fsRdPerm, &myInputFile);
	FailIf(myErr != noErr, NoInputFile);

	myErr = AudConv_GetDataFromAIFF(myInputFile, &mySourceInfo, &mySourceOffset, &mySourceSize, &mySourceCompParamsHandle);
	FailIf(myErr != noErr, NoData);

	// create the buffer into which we read the source data
	mySourceInfo.buffer = (Byte *)NewPtrClear(mySourceSize);
	FailWithAction(mySourceInfo.buffer == NULL, myErr = MemError(), NoSource);

	// create the buffer to hold the converted data
	myDestHandle = NewHandle(0);
	FailWithAction(myDestHandle == NULL, myErr = MemError(), NoDest);

	// read the source data from the source file into the source data buffer
	myErr = SetFPos(myInputFile, fsFromStart, mySourceOffset);
	FailIf(myErr != noErr, InputFileErr);

	myErr = FSRead(myInputFile, &mySourceSize, mySourceInfo.buffer);
	FailIf(myErr != noErr, InputFileErr);

	// create the output AIFF file, to hold the converted data
	myErr = FSpCreate(theOutputFSSpec, sigMoviePlayer, AIFFID, smSystemScript);
	FailIf(myErr != noErr, CreateOutputErr);

	myErr = FSpOpenDF(theOutputFSSpec, fsRdWrPerm, &myOutputFile);
	FailIf(myErr != noErr, OutputFileErr);

	// convert the source data into the output data
	myErr = AudConv_ConvertAudioIntoHandle(&mySourceInfo, mySourceCompParamsHandle, myDestHandle, theDestInfo, &myDestCompParamsHandle, NULL);
	FailIf(myErr != noErr, ConvertErr);

	// put the output data into the output AIFF file
	myErr = AudConv_PrepFileAsAIFF(myOutputFile, theDestInfo, myDestCompParamsHandle);
	FailIf(myErr != noErr, ConvertErr);

	HLock(myDestHandle);
	myCount = GetHandleSize(myDestHandle);
	myErr = FSWrite(myOutputFile, &myCount, *myDestHandle);
	HUnlock(myDestHandle);
	FailIf(myErr != noErr, ConvertErr);

	myErr = AudConv_FinishFileAsAIFF(myOutputFile, theDestInfo->sampleCount, myCount);
	FailIf(myErr != noErr, ConvertErr);

	// close the input and output files
	myErr = FSClose(myInputFile);
	FailIf(myErr != noErr, ConvertErr);

	myErr = FSClose(myOutputFile);
	FailIf(myErr != noErr, ConvertErr);

	// dispose of any storage we allocated and no longer need
	if (mySourceInfo.buffer != NULL)
		DisposePtr((Ptr)mySourceInfo.buffer);
	
	if (myDestHandle != NULL)
		DisposeHandle(myDestHandle);
		
	if (mySourceCompParamsHandle != NULL)
		DisposeHandle(mySourceCompParamsHandle);
		
	if (myDestCompParamsHandle != NULL)
		DisposeHandle(myDestCompParamsHandle);
	
	return(noErr);

ConvertErr:
	FSClose(myOutputFile);
	
OutputFileErr:
	FSpDelete(theOutputFSSpec);
	
CreateOutputErr:
InputFileErr:
	if (myDestHandle != NULL)
		DisposeHandle(myDestHandle);
	
NoDest:
	if (mySourceInfo.buffer != NULL)
		DisposePtr((Ptr)mySourceInfo.buffer);
	
NoSource:
	if (mySourceCompParamsHandle != NULL)
		DisposeHandle(mySourceCompParamsHandle);
		
NoData:
	FSClose(myInputFile);
	
NoInputFile:
	return(myErr);
}


//////////
//
// AudConv_ConvertToMovie
// Read the source AIFF file and convert to the destination format; output a new movie file.
// The new file must include the compression parameters chunk, if used.
//
//////////

OSErr AudConv_ConvertToMovie (ConstFSSpecPtr theInputFSSpec, ConstFSSpecPtr theOutputFSSpec, SoundComponentDataPtr theDestInfo)
{
	SoundComponentData		mySourceInfo;
	Handle					mySourceCompParamsHandle = NULL;
	Handle					myDestHandle = NULL;
	Handle					myDestCompParamsHandle = NULL;
	long					mySourceOffset;
	long					mySourceSize;
	short					myInputFile;
	CompressionInfo			myDestCompInfo;
	Movie					myMovie;
	Track					myTrack;
	short					myResRefNum = -1;
	short					myResID = movieInDataForkResID;
	long					myFlags = createMovieFileDeleteCurFile | createMovieFileDontCreateResFile;
	OSErr					myErr = noErr;

	// open the source file and parse it
	myErr = FSpOpenDF(theInputFSSpec, fsRdPerm, &myInputFile);
	FailIf(myErr != noErr, NoInputFile);

	myErr = AudConv_GetDataFromAIFF(myInputFile, &mySourceInfo, &mySourceOffset, &mySourceSize, &mySourceCompParamsHandle);
	FailIf(myErr != noErr, NoData);

	// create the buffer into which we read the source data
	mySourceInfo.buffer = (Byte *)NewPtrClear(mySourceSize);
	FailWithAction(mySourceInfo.buffer == NULL, myErr = MemError(), NoSource);

	// create the buffer to hold the converted data
	myDestHandle = NewHandle(0);
	FailWithAction(myDestHandle == NULL, myErr = MemError(), NoDest);

	// read the source data from the source file into the source data buffer
	myErr = SetFPos(myInputFile, fsFromStart, mySourceOffset);
	FailIf(myErr != noErr, InputFileErr);

	myErr = FSRead(myInputFile, &mySourceSize, mySourceInfo.buffer);
	FailIf(myErr != noErr, InputFileErr);

	// create the output movie file, to hold the converted data
	myErr = CreateMovieFile(theOutputFSSpec, sigMoviePlayer, smCurrentScript, myFlags, &myResRefNum, &myMovie);
	FailIf(myErr != noErr, CreateOutputErr);

	// convert the source data into the output data
	myErr = AudConv_ConvertAudioIntoHandle(&mySourceInfo, mySourceCompParamsHandle, myDestHandle, theDestInfo, &myDestCompParamsHandle, &myDestCompInfo);
	FailIf(myErr != noErr, CompressErr);

	// put the output data into a track in the output movie file
	myTrack = NewMovieTrack(myMovie, 0, 0, kFullVolume);
	myErr = GetMoviesError();
	FailIf(myErr != noErr, CompressErr);

	myErr = AudConv_PutAudioIntoTrack(myTrack, myDestHandle, theDestInfo, myDestCompParamsHandle, &myDestCompInfo);
	FailIf(myErr != noErr, CompressErr);

	myErr = AddMovieResource(myMovie, myResRefNum, &myResID, NULL);
	FailIf(myErr != noErr, CompressErr);

	// close the movie and movie file
	if (myResRefNum != -1)
		CloseMovieFile(myResRefNum);
	
	if (myMovie != NULL)
		DisposeMovie(myMovie);

	// dispose of any storage we allocated and no longer need
	if (mySourceInfo.buffer != NULL)
		DisposePtr((Ptr)mySourceInfo.buffer);
	
	if (myDestHandle != NULL)
		DisposeHandle(myDestHandle);
		
	if (mySourceCompParamsHandle != NULL)
		DisposeHandle(mySourceCompParamsHandle);
		
	if (myDestCompParamsHandle != NULL)
		DisposeHandle(myDestCompParamsHandle);
		
	return(noErr);

CompressErr:
	if (myMovie != NULL)
		DisposeMovie(myMovie);
		
	if (myResRefNum != -1)
		CloseMovieFile(myResRefNum);
		
CreateOutputErr:
InputFileErr:
	if (mySourceCompParamsHandle != NULL)
		DisposeHandle(mySourceCompParamsHandle);
		
NoDest:
	if (mySourceInfo.buffer != NULL)
		DisposePtr((Ptr)mySourceInfo.buffer);
	
NoSource:
	if (mySourceCompParamsHandle != NULL)
		DisposeHandle(mySourceCompParamsHandle);
		
NoData:
	FSClose(myInputFile);
	
NoInputFile:
	return(myErr);
}


//////////
//
// AudConv_GetDataFromAIFF
// Parse the specified file and read the data in the required chunks in it.
//
// This is a very simple AIFF parser which will read three basic chunks: the
// main ContainerChunk, the Common or ExtCommonChunk, and the SoundDataChunk.
// That's all that's required. One last chunk is read if found, which contains the
// decompression parameters required to decompress this audio. It's a simple
// chunk with unknown data contained within. Copy this data into a handle, and
// it becomes the audio atoms used with the siDecompressionParams selector.
//
// Keep in mind that the data in an AIFF file is always big-endian.
//
//////////

OSErr AudConv_GetDataFromAIFF (short theRefNum, SoundComponentDataPtr theSourceInfo, long *theSourceOffset, long *theSourceSize, Handle *theDestCompParams)
{
	union {
		ContainerChunk	cnt;
		CommonChunk		com;
		ExtCommonChunk	ext;
		SoundDataChunk	snd;
	} 					myChunk;
	ChunkHeader			myCHeader;
	Handle				myDestCompParamsHandle;
	long double			mySampleRate;
	long				myOffset;
	long				myDataSize;
	long				myByteCount;
	OSErr				myErr = noErr;

	myErr = SetFPos(theRefNum, fsFromStart, 0);		// we have to start at the beginning
	FailIf(myErr != noErr, Failure);

	// everything is contained in a container chunk, which therefore is the first chunk in the file;
	// read the beginning of the container chunk into memory
	myByteCount = sizeof(ContainerChunk);
	myErr = FSRead(theRefNum, &myByteCount, &myChunk);
	FailIf(myErr != noErr, Failure);

	// make sure it's a FORM chunk
	FailWithAction(EndianU32_BtoN(myChunk.cnt.ckID) != FORMID, myErr = badFileFormat, Failure);

	// find the CommonChunk or ExtCommonChunk
	myErr = AudConv_SetFPosToChunk(theRefNum, CommonID);
	FailIf(myErr != noErr, Failure);

	switch (EndianU32_BtoN(myChunk.cnt.formType)) {
		case AIFFID:
			myByteCount = sizeof(CommonChunk);
			myErr = FSRead(theRefNum, &myByteCount, &myChunk);
			FailIf(myErr != noErr, Failure);

			theSourceInfo->format = k16BitBigEndianFormat;
			break;

		case AIFCID:
			myByteCount = offsetof(ExtCommonChunk, compressionName);
			myErr = FSRead(theRefNum, &myByteCount, &myChunk);
			FailIf(myErr != noErr, Failure);

			theSourceInfo->format = EndianU32_BtoN(myChunk.ext.compressionType);
			break;

		default:
			FailWithAction(badFileFormat, myErr = badFileFormat, Failure);
			break;
	}
	
	theSourceInfo->flags = 0L;
	theSourceInfo->numChannels = EndianS16_BtoN(myChunk.com.numChannels);
	theSourceInfo->sampleSize = EndianS16_BtoN(myChunk.com.sampleSize);
#if TARGET_CPU_68K
	mySampleRate = myChunk.com.sampleRate;
#else
	x80told(&myChunk.com.sampleRate, &mySampleRate);
#endif
	theSourceInfo->sampleRate = (unsigned long)(mySampleRate * 65536.0);
	theSourceInfo->sampleCount = EndianU32_BtoN(myChunk.com.numSampleFrames);
	theSourceInfo->buffer = NULL;
	theSourceInfo->reserved = 0;

	myErr = AudConv_SetFPosToChunk(theRefNum, SoundDataID);
	FailIf(myErr != noErr, Failure);

	myByteCount = sizeof(SoundDataChunk);
	myErr = FSRead(theRefNum, &myByteCount, &myChunk);
	FailIf(myErr != noErr, Failure);

	myDataSize = EndianS32_BtoN(myChunk.snd.ckSize) - sizeof(SoundDataChunk);	// size of source data
	myErr = GetFPos(theRefNum, &myOffset);
	FailIf(myErr != noErr, Failure);

	// return NULL if no siDecompressionParams
	myDestCompParamsHandle = NULL;
	myErr = AudConv_SetFPosToChunk(theRefNum, siDecompressionParams);
	if (myErr == noErr) {
		myByteCount = sizeof(myCHeader);
		myErr = FSRead(theRefNum, &myByteCount, &myCHeader);
		FailIf(myErr != noErr, Failure);

		myByteCount = EndianS32_BtoN(myCHeader.ckSize);
		myDestCompParamsHandle = NewHandle(myByteCount);
		FailIf(myDestCompParamsHandle == NULL, Failure);

		HLock(myDestCompParamsHandle);
		myErr = FSRead(theRefNum, &myByteCount, *myDestCompParamsHandle);
		HUnlock(myDestCompParamsHandle);
		FailIf(myErr != noErr, ReadParamsErr);
	}
	
	*theSourceOffset = myOffset;						// offset from start of file to audio samples
	*theSourceSize = myDataSize;						// size in bytes of the audio samples
	*theDestCompParams = myDestCompParamsHandle;		// possible handle to the decompression parameters
	return(noErr);

ReadParamsErr:
	if (myDestCompParamsHandle != NULL)
		DisposeHandle(myDestCompParamsHandle);
		
Failure:
	return(myErr);
}


//////////
//
// AudConv_ConvertAudioIntoHandle
// Convert the specified sound data.
//
//////////

OSErr AudConv_ConvertAudioIntoHandle (	SoundComponentDataPtr theSourceInfo, 
										Handle theSourceCompParamsHandle,
										Handle theDestHandle, 
										SoundComponentDataPtr theDestInfo,
										Handle *theDestCompParamsHandle, 
										CompressionInfoPtr theDestCompInfo)
{
	SoundConverter		myConverter;
	Handle				myDestCompParamsHandle = NULL;			
	CompressionInfo		myCompressionInfo;
	unsigned short		mySourceBytesPerFrame;
	UInt16				hasOptionsDialog = 0;
	OSErr				myErr = noErr;

	// perform in non-real time for best quality
	theDestInfo->flags |= kNoRealtimeProcessing;			

	//////////
	//				
	// open a sound converter
	//				
	//////////

	myErr = SoundConverterOpen(theSourceInfo, theDestInfo, &myConverter);
	FailIf(myErr != noErr, Exit);

	//////////
	//				
	// display the options dialog for the sound converter
	//
	// the GetInfo call isn't required; you can just call SetInfo and it will show the dialog
	// or return the same error that GetInfo would have; you might call GetInfo to determine
	// whether the options dialog is supported, perhaps because you want to show a button that
	// would bring up this dialog
	//				
	//////////

	myErr = SoundConverterGetInfo(myConverter, siOptionsDialog, &hasOptionsDialog);
	if ((myErr == noErr) && (hasOptionsDialog != 0)) {
		myErr = SoundConverterSetInfo(myConverter, siOptionsDialog, NULL);
		FailIf(myErr != noErr, DialogErr);
	}

	// set the number of output channels
	myErr = SoundConverterSetInfo(myConverter, siCompressionChannels, &theDestInfo->numChannels);
	// ignore this error, some codecs don't use this selector (makes QDesign work)

	//////////
	//				
	// get any custom decompression settings
	//
	// not all sound compressors have custom settings, in which case myDestCompParamsHandle
	// will be returned unchanged (NULL); we return these custom settings to the caller, who
	// will put them in the proper place; if this audio is stored in a QuickTime movie, these
	// settings will be stored in a SoundDescriptionExtension of type siDecompressionParams
	//
	//////////

	SoundConverterGetInfo(myConverter, siCompressionParams, &myDestCompParamsHandle);	
	
	// send the source and destination compression settings to the sound converter; this is
	// necessary to make some sound converters (for instance, QDesign) work properly
	if (myDestCompParamsHandle != NULL) {
		HLockHi(myDestCompParamsHandle);
		myErr = SoundConverterSetInfo(myConverter, siCompressionParams, *myDestCompParamsHandle);
		HUnlock(myDestCompParamsHandle);
		FailIf(myErr != noErr, ConverterErr);
	}

	if (theSourceCompParamsHandle != NULL) {
		HLockHi(theSourceCompParamsHandle);
		myErr = SoundConverterSetInfo(myConverter, siDecompressionParams, *theSourceCompParamsHandle);
		HUnlock(theSourceCompParamsHandle);
		FailIf(myErr != noErr, ConverterErr);
	}

	*theDestCompParamsHandle = myDestCompParamsHandle;

	//////////
	//				
	// get the sizes of the source and destination sample frames
	//
	// we return the destination sample frame size to the caller, who will put it in the
	// proper place; if this audio data is stored in a QuickTime movie, this size will be
	// stored the Version 1 sound description handle
	//				
	//////////

	// get the source number of bytes in each sample frame
	myErr = SoundConverterGetInfo(myConverter, siCompressionFactor, &myCompressionInfo);
	if ((myErr != noErr) || (myCompressionInfo.format != theSourceInfo->format)) {
		myErr = GetCompressionInfo(fixedCompression, theSourceInfo->format, theSourceInfo->numChannels, theSourceInfo->sampleSize, &myCompressionInfo);
		FailIf(myErr != noErr, ConverterErr);
	}
	
	// bytesPerFrame is not filled in by GetCompressionInfo, so we set it here
	mySourceBytesPerFrame = myCompressionInfo.bytesPerPacket * theSourceInfo->numChannels;
	
	// get the destination number of bytes in each sample frame
	if (theDestCompInfo != NULL) {
		// get info about destination compression.
		myErr = SoundConverterGetInfo(myConverter, siCompressionFactor, theDestCompInfo);
		if ((myErr != noErr) || (theDestCompInfo->format != theDestInfo->format)) {
			myErr = GetCompressionInfo(fixedCompression, theDestInfo->format, theDestInfo->numChannels, theDestInfo->sampleSize, theDestCompInfo);
			FailIf(myErr != noErr, ConverterErr);
		}
		
		// bytesPerFrame is not filled in by GetCompressionInfo, so we set it here
		theDestCompInfo->bytesPerFrame = theDestCompInfo->bytesPerPacket * theDestInfo->numChannels;
	}
	
	//////////
	//				
	// do the actual data conversion
	//
	//////////

	myErr = AudConv_WriteAudioToHandle(myConverter, theSourceInfo, mySourceBytesPerFrame, theDestInfo, theDestHandle);
	FailIf(myErr != noErr, ConverterErr);

	SoundConverterClose(myConverter);
	return(noErr);

ConverterErr:
	if (myDestCompParamsHandle != NULL)
		DisposeHandle(myDestCompParamsHandle);
		
DialogErr:
	if (myConverter != NULL)
		SoundConverterClose(myConverter);
	
Exit:
	return(myErr);
}


//////////
//
// AudConv_WriteAudioToHandle
// Convert the source data into an ever-expanding handle of the given format.
// Of course this means you've only got one shot at converting the data, and it
// all has to fit into memory. Converting larger amounts is left to the reader.
//
//////////

OSErr AudConv_WriteAudioToHandle (SoundConverter theConverter, SoundComponentDataPtr theSourceInfo, short theSourceBytesPerFrame, SoundComponentDataPtr theDestInfo, Handle theDestHandle)
{
	unsigned long	myNumFramesLeft;
	unsigned long	mySourceFrames;
	unsigned long	myDestFrames;
	unsigned long	mySourceBytes;
	unsigned long	myDestBytes;
	unsigned long	myDataOffset = 0;
	Ptr				myDestPtr = NULL;
	OSErr			myErr = noErr;

	// get sound converter buffer size info
	myErr = SoundConverterGetBufferSizes(theConverter, kMaxSndConvtBufferSize, &mySourceFrames, &mySourceBytes, &myDestBytes);
	FailIf(myErr != noErr, Exit);

	// create destination data buffer
	myDestPtr = NewPtrClear(myDestBytes);
	FailWithAction(myErr != noErr, myErr = MemError(), Exit);

	//////////
	//
	// convert the sound to the desired output format
	//
	//////////

	myErr = SoundConverterBeginConversion(theConverter);
	FailIf(myErr != noErr, ConverterErr);

	// initialize destination total frame count to zero
	theDestInfo->sampleCount = 0;
	myNumFramesLeft = (unsigned long)theSourceInfo->sampleCount;
	
	while (myNumFramesLeft > 0) {
		// if there are fewer frames remaining than our source buffer size,
		// we're near the end of our data, so get just what's remaining
		if (mySourceFrames > myNumFramesLeft)
			mySourceFrames = myNumFramesLeft;

		myErr = SoundConverterConvertBuffer(theConverter, theSourceInfo->buffer + myDataOffset, mySourceFrames, myDestPtr, &myDestFrames, &myDestBytes);
		FailIf(myErr != noErr, ConverterErr);

		// write this data to the output handle
		if (myDestBytes > 0) {
			myErr = PtrAndHand(myDestPtr, theDestHandle, myDestBytes);
			FailIf(myErr != noErr, ConverterErr);
		}

		// move offset to appropriate place in source data
		myDataOffset += mySourceFrames * theSourceBytesPerFrame;
		
		// keep track of total frames returned by converter
		theDestInfo->sampleCount += myDestFrames;
		
		myNumFramesLeft -= mySourceFrames;
	}

	// end the conversion, and see if we get back a few more bytes of data	
	myErr = SoundConverterEndConversion(theConverter, myDestPtr, &myDestFrames, &myDestBytes);
	FailIf(myErr != noErr, ConverterErr);

	if (myDestBytes > 0) {
		// write this data to the output handle
		myErr = PtrAndHand(myDestPtr, theDestHandle, myDestBytes);
		FailIf(myErr != noErr, ConverterErr);
	}
	
	theDestInfo->sampleCount += myDestFrames;			// update final amount of samples

ConverterErr:
	if (myDestPtr != NULL)
		DisposePtr(myDestPtr);
		
Exit:
	return(myErr);
}


//////////
//
// AudConv_GetOutputFormat
// Display the QuickTime standard sound settings dialog to obtain the output format.
//
//////////

OSErr AudConv_GetOutputFormat (SoundComponentDataPtr theDestInfo)
{
	ComponentInstance	myComponent = NULL;
	ComponentResult		myErr = noErr;

	myComponent = OpenDefaultComponent(StandardCompressionType, StandardCompressionSubTypeSound);
	FailWithAction(myComponent == NULL, myErr = cantFindHandler, NoComponent);

	myErr = SCRequestImageSettings(myComponent);			// show the dialog to user
	if (myErr == noErr) {									// then get the settings
		myErr = SCGetInfo(myComponent, scSoundCompressionType, &theDestInfo->format);
		FailIf(myErr != noErr, SCGetInfoFailed);

		myErr = SCGetInfo(myComponent, scSoundChannelCountType, &theDestInfo->numChannels);
		FailIf(myErr != noErr, SCGetInfoFailed);

		myErr = SCGetInfo(myComponent, scSoundSampleSizeType, &theDestInfo->sampleSize);
		FailIf(myErr != noErr, SCGetInfoFailed);

		myErr = SCGetInfo(myComponent, scSoundSampleRateType, &theDestInfo->sampleRate);
		FailIf(myErr != noErr, SCGetInfoFailed);

		// any non-compressed format appears as 'raw ', which is not what the Sound Manager expects
		if (theDestInfo->format == k8BitOffsetBinaryFormat) {
			if (theDestInfo->sampleSize > 8)
				theDestInfo->format = k16BitBigEndianFormat;
			else
				theDestInfo->format = kSoundNotCompressed;
		}

		// clear the rest of the unused parameters
		theDestInfo->flags = 0;
		theDestInfo->sampleCount = 0;
		theDestInfo->buffer = NULL;
		theDestInfo->reserved = 0L;
	}
	
	return((OSErr)myErr);

SCGetInfoFailed:
	CloseComponent(myComponent);
	
NoComponent:
	return((OSErr)myErr);
}


//////////
//
// AudConv_SetFPosToChunk
// Set the file's position to the start of the given chunk.
//
//////////

OSErr AudConv_SetFPosToChunk (short theRefNum, ID theChunkID)
{
	long				myCount;
	ChunkHeader			myCHeader;
	OSErr				myErr = noErr;

	// position the file mark past the ContainerChunk, which is always the first thing in the file
	myErr = SetFPos(theRefNum, fsFromStart, sizeof(ContainerChunk));
	FailIf(myErr != noErr, Exit);

	while (true) {
		myCount = sizeof(myCHeader);
		myErr = FSRead(theRefNum, &myCount, &(myCHeader.ckID));
		if (myErr == eofErr)							// reached the end of file, exit gracefully
			break;
		FailIf(myErr != noErr, Exit);

		if (theChunkID == EndianU32_BtoN(myCHeader.ckID)) {
			myCount = -myCount;							// rewind ptr to top of chunk if found
			myErr = SetFPos(theRefNum, fsFromMark, myCount);
			FailIf(myErr != noErr, Exit);
			break;
		} else {										// else skip over the entire chunk
			myErr = SetFPos(theRefNum, fsFromMark, EndianS32_BtoN(myCHeader.ckSize));
			if (myErr == eofErr)						// reached the end of file, exit gracefully
				break;
			FailIf(myErr != noErr, Exit);
		}
	}

Exit:
	return(myErr);
}


//////////
//
// AudConv_PrepFileAsAIFF
// Write out the necessary chunks, without audio samples, to prepare for writing;
// leave file position at the proper place for audio samples.
//
//////////

OSErr AudConv_PrepFileAsAIFF (short theOutputFile, SoundComponentDataPtr theDestInfo, Handle theDestParams)
{
	Str255				myCompressionName;
	ContainerChunk		myContainer;
	FormatVersionChunk	myVersion;
	ExtCommonChunk		myCommon;
	SoundDataChunk		mySoundData;
	ID					myFormType;
	long				myCount;
	OSErr				myErr = noErr;

	switch (theDestInfo->format) {
		case kSoundNotCompressed:
		case k8BitOffsetBinaryFormat:
		case k16BitBigEndianFormat:
		case k24BitFormat:
		case k32BitFormat:
			myFormType = AIFFID;
			break;

		default :
			myFormType = AIFCID;
			break;
	}

	myErr = SetFPos(theOutputFile, fsFromStart, 0);
	FailIf(myErr != noErr, Exit);

	// write out the first chunk
	myContainer.ckID = EndianU32_NtoB(FORMID);
	myContainer.ckSize = EndianU32_NtoB(0);									// nothing in the file just yet
	myContainer.formType = EndianU32_NtoB(myFormType);
	myCount = sizeof(myContainer);
	myErr = FSWrite(theOutputFile, &myCount, &myContainer);
	FailIf(myErr != noErr, Exit);

	// write out the version chunk for AIFC files
	if (myFormType == AIFCID) {
		myVersion.ckID = EndianU32_NtoB(FormatVersionID);
		myVersion.ckSize = EndianS32_NtoB(sizeof(FormatVersionChunk) - sizeof(ChunkHeader));
		myVersion.timestamp = EndianU32_NtoB(AIFCVersion1);					// timestamp for this version
		myCount = sizeof(myVersion);
		myErr = FSWrite(theOutputFile, &myCount, &myVersion);
		FailIf(myErr != noErr, Exit);
	}

	// write out the CommonChunk or ExtCommonChunk if the data is compressed
	myCommon.ckID = EndianU32_NtoB(CommonID);
	myCommon.ckSize = EndianS32_NtoB(sizeof(CommonChunk) - sizeof(ChunkHeader));	// size of common chunk variables without compression info
	myCommon.numChannels = EndianS16_NtoB(theDestInfo->numChannels);
	myCommon.numSampleFrames = EndianU32_NtoB(0);							// nothing in the file just yet
	myCommon.sampleSize = EndianS16_NtoB(theDestInfo->sampleSize);
#if TARGET_CPU_68K
	myCommon.sampleRate = theDestInfo->sampleRate / 65536.0;
#else
	{
		long double		ldRate;

		ldRate = theDestInfo->sampleRate / 65536.0;
		ldtox80(&ldRate, &myCommon.sampleRate);
	}
#endif
	myCommon.compressionType = EndianU32_NtoB(theDestInfo->format);
	myCommon.compressionName[0] = 0;

	if (myFormType == AIFCID) {
		myErr = GetCompressionName(theDestInfo->format, myCompressionName);
		if (myErr == noErr) {
			// write out the ExtCommonChunk for compressed data, with the name
			myCount = offsetof(ExtCommonChunk, compressionName);
			myCommon.ckSize = myCount - sizeof(ChunkHeader) + myCompressionName[0] + 1;
			myCommon.ckSize = ++myCommon.ckSize & ~1;			// must be word aligned
			myCommon.ckSize = EndianS32_NtoB(myCommon.ckSize);
			myErr = FSWrite(theOutputFile, &myCount, &myCommon);
			FailIf(myErr != noErr, Exit);

			myCount = myCompressionName[0] + 1;
			myCount = ++myCount & ~1;							// must be word aligned
			myErr = FSWrite(theOutputFile, &myCount, &myCompressionName);
			FailIf(myErr != noErr, Exit);
		} else {
			// write out the ExtCommonChunk for compressed data, without the name
			myCommon.ckSize = EndianS32_NtoB(sizeof(ExtCommonChunk) - sizeof(ChunkHeader));	// size of common chunk variables without compression info
			myCount = offsetof(ExtCommonChunk, compressionName);
			myErr = FSWrite(theOutputFile, &myCount, &myCommon);
			FailIf(myErr != noErr, Exit);
		}
	} else {	// write out the CommonChunk for non-compressed data
		myCount = sizeof(CommonChunk);
		myErr = FSWrite(theOutputFile, &myCount, &myCommon);
		FailIf(myErr != noErr, Exit);
	}

	if (theDestParams != NULL) {
		ChunkHeader		myCHeader;

		myCHeader.ckID = EndianU32_NtoB(siDecompressionParams);
		myCHeader.ckSize = EndianS32_NtoB(GetHandleSize(theDestParams));
		myCount = sizeof(myCHeader);
		myErr = FSWrite(theOutputFile, &myCount, &myCHeader);
		FailIf(myErr != noErr, Exit);

		myCount = EndianS32_BtoN(myCHeader.ckSize);
		HLock(theDestParams);
		myErr = FSWrite(theOutputFile, &myCount, *theDestParams);
		FailIf(myErr != noErr, Exit);
	}

	mySoundData.ckID = EndianU32_NtoB(SoundDataID);
	mySoundData.ckSize = EndianS32_NtoB(0);							// nothing in the file just yet
	mySoundData.offset = EndianU32_NtoB(0);
	mySoundData.blockSize = EndianU32_NtoB(0);
	myCount = sizeof(mySoundData);
	myErr = FSWrite(theOutputFile, &myCount, &mySoundData);
	FailIf(myErr != noErr, Exit);

	// now go back and update Container to current file size
	myErr = SetFPos(theOutputFile, fsFromStart, 0);
	FailIf(myErr != noErr, Exit);

	myCount = sizeof(myContainer);
	myErr = FSRead(theOutputFile, &myCount, &myContainer);
	FailIf(myErr != noErr, Exit);

	myErr = GetEOF(theOutputFile, &myContainer.ckSize);
	FailIf(myErr != noErr, Exit);

	myErr = SetFPos(theOutputFile, fsFromStart, 0);
	FailIf(myErr != noErr, Exit);

	myContainer.ckSize -= sizeof(ChunkHeader);
	myContainer.ckSize = EndianS32_NtoB(myContainer.ckSize);
	myCount = sizeof(myContainer);
	myErr = FSWrite(theOutputFile, &myCount, &myContainer);
	FailIf(myErr != noErr, Exit);

	// set current file position to the end of end of the file
	// the last chunk is the SoundDataChunk, and that's where to begin writing data
	myErr = SetFPos(theOutputFile, fsFromStart, EndianS32_BtoN(myContainer.ckSize) + sizeof(ChunkHeader));
	FailIf(myErr != noErr, Exit);

Exit:
	return(myErr);
}


//////////
//
// AudConv_FinishFileAsAIFF
// This assumes that all the necessary chunks have been written to the file and
// then audio samples have been added. Finally, update the chunks to reflect the
// samples that were added.
//
//////////

OSErr AudConv_FinishFileAsAIFF (short theOutputFile, unsigned long destFramesMoved, unsigned long destBytesMoved)
{
	CommonChunk			myCommon;
	SoundDataChunk		mySoundData;
	long				myCount;
	OSErr				myErr = noErr;

	// find the CommonChunk and update the numSampleFrames
	myErr = AudConv_SetFPosToChunk(theOutputFile, CommonID);
	FailIf(myErr != noErr, Exit);

	myCount = sizeof(myCommon);
	myErr = FSRead(theOutputFile, &myCount, &myCommon);
	FailIf(myErr != noErr, Exit);

	myErr = SetFPos(theOutputFile, fsFromMark, -myCount);		// reset to start of chunk
	FailIf(myErr != noErr, Exit);

	myCommon.numSampleFrames = EndianU32_NtoB(destFramesMoved);
	myCount = sizeof(myCommon);
	myErr = FSWrite(theOutputFile, &myCount, &myCommon);
	FailIf(myErr != noErr, Exit);

	// find the SoundDataChunk and update the ckSize
	myErr = AudConv_SetFPosToChunk(theOutputFile, SoundDataID);
	FailIf(myErr != noErr, Exit);

	myCount = sizeof(mySoundData);
	myErr = FSRead(theOutputFile, &myCount, &mySoundData);
	FailIf(myErr != noErr, Exit);

	myErr = SetFPos(theOutputFile, fsFromMark, -myCount);		// reset to start of chunk
	FailIf(myErr != noErr, Exit);

	mySoundData.ckSize = EndianS32_NtoB(sizeof(SoundDataChunk) - sizeof(ChunkHeader) + destBytesMoved);
	myCount = sizeof(mySoundData);
	myErr = FSWrite(theOutputFile, &myCount, &mySoundData);
	FailIf(myErr != noErr, Exit);

Exit:
	return(myErr);
}


//////////
//
// AudConv_PutAudioIntoTrack
// Add the audio as a track with the necessary decompression extension.
//
//////////

OSErr AudConv_PutAudioIntoTrack (	Track theTrack, 
									Handle theDestAudioData, 
									SoundComponentDataPtr theDestInfo,
									Handle theDestCompParamsHandle, 
									CompressionInfoPtr theDestCompInfo)
{
	SoundDescriptionV1Handle	mySampleDesc;
	Media						myMedia;
	OSErr						myErr = noErr;

	//////////
	//				
	// create a media for the track passed in
	//				
	//////////

	// set new track to be a sound track
	myMedia = NewTrackMedia(theTrack, SoundMediaType, theDestInfo->sampleRate >> 16, NULL, 0);
	myErr = GetMoviesError();
	FailIf(myErr != noErr, Exit);

	// start a media editing session
	myErr = BeginMediaEdits(myMedia);
	FailIf(myErr != noErr, Exit);

	//////////
	//				
	// create a sound sample description
	//				
	//////////

	// use the SoundDescription format 1 because it adds fields for data size information
	// and is required by AddSoundDescriptionExtension if an extension is required for the compression format
	mySampleDesc = (SoundDescriptionV1Handle)NewHandleClear(sizeof(SoundDescriptionV1));
	FailWithAction(myErr != noErr, myErr = MemError(), Exit);

	(**mySampleDesc).desc.descSize			= sizeof(SoundDescriptionV1);
	(**mySampleDesc).desc.dataFormat		= theDestInfo->format;
	(**mySampleDesc).desc.resvd1			= 0;
	(**mySampleDesc).desc.resvd2			= 0;
	(**mySampleDesc).desc.dataRefIndex		= 1;
	(**mySampleDesc).desc.version			= 1;
	(**mySampleDesc).desc.revlevel			= 0;
	(**mySampleDesc).desc.vendor			= 0;
	(**mySampleDesc).desc.numChannels		= theDestInfo->numChannels;
	(**mySampleDesc).desc.sampleSize		= theDestInfo->sampleSize;
	(**mySampleDesc).desc.compressionID		= 0;
	(**mySampleDesc).desc.packetSize		= 0;
	(**mySampleDesc).desc.sampleRate		= theDestInfo->sampleRate;
	(**mySampleDesc).samplesPerPacket		= theDestCompInfo->samplesPerPacket;
	(**mySampleDesc).bytesPerPacket			= theDestCompInfo->bytesPerPacket;
	(**mySampleDesc).bytesPerFrame			= theDestCompInfo->bytesPerFrame;
	(**mySampleDesc).bytesPerSample			= theDestCompInfo->bytesPerSample;

	// not all compression formats have compression params, so we need to add a
	// sound description extension only for those that do
	if (theDestCompParamsHandle != NULL) {
		myErr = AddSoundDescriptionExtension((SoundDescriptionHandle)mySampleDesc, theDestCompParamsHandle, siDecompressionParams);
		FailIf(myErr != noErr, MediaErr);
	}

	//////////
	//				
	// add samples to the media
	//				
	//////////

	myErr = AddMediaSample(	myMedia,
							theDestAudioData,
							0,
							theDestInfo->sampleCount * theDestCompInfo->bytesPerFrame,
							1,
							(SampleDescriptionHandle)mySampleDesc,
							theDestInfo->sampleCount * theDestCompInfo->samplesPerPacket,
							0,
							NULL);
	FailIf(myErr != noErr, MediaErr);

	myErr = EndMediaEdits(myMedia);
	FailIf(myErr != noErr, MediaErr);

	//////////
	//				
	// insert the media into the track
	//				
	//////////

	myErr = InsertMediaIntoTrack(theTrack, 0, 0, GetMediaDuration(myMedia), fixed1);
	FailIf(myErr != noErr, MediaErr);

MediaErr:
	if (mySampleDesc != NULL)	
		DisposeHandle((Handle)mySampleDesc);
		
Exit:
	return(myErr);
}


//////////
//
// AudConv_SFGetDialogHook
// Hook function for the get file dialog box.
//
//////////

PASCAL_RTN short AudConv_SFGetDialogHook (short theItem, DialogPtr theDialog, void *theOutputAIFF)
{
	ControlHandle			myAIFFControl;
	ControlHandle			myMovieControl;
	Rect					myRect;
	short					myKind;
	
	// make sure we've got a real dialog
	if (theDialog == NULL)
		return(theItem);

	GetDialogItem(theDialog, kOutputAIFFButton, &myKind, (Handle *)&myAIFFControl, &myRect);
	GetDialogItem(theDialog, kOutputMovieButton, &myKind, (Handle *)&myMovieControl, &myRect);

	switch (theItem) {
		case sfHookFirstCall:
			if (*(Boolean *)theOutputAIFF)
				SetControlValue(myAIFFControl, kControlRadioButtonCheckedValue);
			else
				SetControlValue(myMovieControl, kControlRadioButtonUncheckedValue);
			break;

		case kOutputAIFFButton:
			SetControlValue(myAIFFControl, kControlRadioButtonCheckedValue);
			SetControlValue(myMovieControl, kControlRadioButtonUncheckedValue);
			*(Boolean *)theOutputAIFF = true;
			break;

		case kOutputMovieButton:
			SetControlValue(myAIFFControl, kControlRadioButtonUncheckedValue);
			SetControlValue(myMovieControl, kControlRadioButtonCheckedValue);
			*(Boolean *)theOutputAIFF = false;
			break;
	}
	
	return(theItem);
}


//////////
//
// AudConv_NavObjectFilterProc
// Object filter function for the get file dialog box.
//
//////////

PASCAL_RTN Boolean AudConv_NavObjectFilterProc (AEDesc *theItem, void *theInfo, void *theCallBackUD, NavFilterModes theFilterMode)
{
#pragma unused(theCallBackUD, theFilterMode)
	NavFileOrFolderInfo		*myInfo = (NavFileOrFolderInfo *)theInfo;
	
	if (theItem->descriptorType == typeFSS) {
		if (!myInfo->isFolder) {
			OSType			myType = myInfo->fileAndFolder.fileInfo.finderInfo.fdType;
			
			// see whether the file type is in the list of file types that our application can open 
			if ((myType == kQTFileTypeAIFF) || (myType == kQTFileTypeAIFC))
				return(true);

			// if we got to here, it's a file we cannot open
			return(false);		
		}
	}
	
	// if we got to here, it's a folder or non-HFS object
	return(true);
}


//////////
//
// AudConv_ConvertCToPascalString
// Convert a C string into a Pascal string.
//
// The caller is responsible for disposing of the pointer returned by this function (by calling free).
//
//////////

StringPtr AudConv_ConvertCToPascalString (char *theString)
{
	StringPtr	myString = malloc(strlen(theString) + 1);
	short		myIndex = 0;

	while (theString[myIndex] != '\0') {
		myString[myIndex + 1] = theString[myIndex];
		myIndex++;
	}
	
	myString[0] = (unsigned char)myIndex;
	
	return(myString);
}


//////////
//
// AudConv_HandleNavEvent
// A callback procedure that handles events while a Navigation Service dialog box is displayed.
//
//////////

PASCAL_RTN void AudConv_HandleNavEvent (NavEventCallbackMessage theCallBackSelector, NavCBRecPtr theCallBackParms, void *theCallBackUD)
{
	Boolean				myOutputAIFF = *(Boolean *)theCallBackUD;
	NavMenuItemSpec		myMenuItemSpec;
	Str255				myFileName;
	static Str15		myMooVExt;
	static Str15		myAIFFExt;
	
	if (theCallBackSelector == kNavCBStart) {
		// get the filename extensions
		GetIndString(myMooVExt, rStringsResID, rMooVExtensionIndex);
		GetIndString(myAIFFExt, rStringsResID, rAIFFExtensionIndex);
	}

	if (theCallBackSelector == kNavCBPopupMenuSelect) {
		myMenuItemSpec = *(NavMenuItemSpec *)theCallBackParms->eventData.eventDataParms.param;
		myOutputAIFF = (myMenuItemSpec.menuType == kQTFileTypeAIFF);
	}
	
	if (*(Boolean *)theCallBackUD != myOutputAIFF) {
		// the output file type has changed, so adjust the filename extension accordingly
		short			myIndex;
		short			myCount;
		
		// get the current filename
		NavCustomControl(theCallBackParms->context, kNavCtlGetEditFileName, &myFileName);

		// find the position of the last filename separator in the current filename
		myIndex = myFileName[0];
		while ((myFileName[myIndex] != kFileExtSeparator) && (myIndex > 0))
			myIndex--;

		if (myIndex != 0) {
			if (myOutputAIFF) {
				for (myCount = 1; myCount <= myAIFFExt[0]; myCount++)
					myFileName[myIndex + myCount - 1] = myAIFFExt[myCount];
			} else {
				for (myCount = 1; myCount <= myMooVExt[0]; myCount++)
					myFileName[myIndex + myCount - 1] = myMooVExt[myCount];
			}
			
			myFileName[0] = myIndex + myCount - 2;
			NavCustomControl(theCallBackParms->context, kNavCtlSetEditFileName, &myFileName);
		}
		
		*(Boolean *)theCallBackUD = myOutputAIFF;
	}
}


