/*
	File:		uLawCompressor.c

	Contains:	uLaw Compression Sound Component

	Written by:	Jim Reekes

	Copyright:	© 1991-1998 by Apple Computer, Inc., all rights reserved.

*/
#if defined(_MSC_VER) && !defined(__MWERKS__) 
#pragma warning(disable:4229)		// ignore anachronism used: modifiers on data are ignored
#endif

#include <MacTypes.h>
#include <MacErrors.h>
#include <Endian.h>
#include <Components.h>
#include <MacMemory.h>
#include <Resources.h>
#include <Controls.h>
#include <ControlDefinitions.h>
#include <Dialogs.h>
#include <Sound.h>
#include <MoviesFormat.h>

#include "uLawCodec.h"


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//			Constants
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

#define kInputFormat		k16BitNativeEndianFormat		/* required input format */
#define kOutputFormat		kCodecFormat					/* only formats we output */

#define ZEROTRAP	1										/* turn on the trap as per the MIL-STD */

enum {
	kOutputSampleSize		= 16,								/* size of output samples */
	kOutputSamples			= ((kMaxOutputSamples / kULawBlockSamples) * kULawBlockSamples),	/* max actual samples in output buffer */
	kOutputBufferBytes		= ((kMaxOutputSamples / kULawBlockSamples) * kULawBlockBytes * 2),	/* room for ULaw stereo data */
	kLeftoverBufferBytes	= (kULawBlockSamples * 2 * 2),		/* room for leftover stereo 16-bit samples */

	kInputSampleSize		= 16,

	BIAS					= 0x84,							/* define the add-in bias for 16 bit samples */
	CLIP					= 32635,

	kOptionCheckBox			= 4
};

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//			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

// Keep your data long word aligned for best performance

typedef struct {
	ComponentInstance		self;								/* our instance */
	ComponentInstance		sourceComponent;					/* who to call for more source data */
	SoundComponentDataPtr	sourceDataPtr;						/* pointer to source data descriptor */
	SoundComponentData		outputData;							/* local data descriptor record */
	SoundSource				sourceID;
	long					outputBufferSamples;				/* no. samples in output buffer */
	CompressionInfo			sourceCompInfo;						/* info about source data format */
	CompressionInfo			destCompInfo;						/* info about destination data format */
	Handle					ulawTableHandle;					/* resource handle to ulaw table */
	Byte					*ulawTable;							/* pointer to ulaw table */
	unsigned long			leftOverSamples;					/* no. samples in leftover buffer */
	Byte					leftOverBuffer[kLeftoverBufferBytes];				/* space for leftover samples */
	Byte					buffer[kOutputBufferBytes + kLeftoverBufferBytes];	/* buffer space */
} ULawCompGlobals, *ULawCompGlobalsPtr;

typedef struct {
	AudioFormatAtom			formatData;
	AudioEndianAtom			endianData;
	AudioTerminatorAtom		terminatorData;
} AudioCompressionAtom, *AudioCompressionAtomPtr, **AudioCompressionAtomHandle;

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//			Prototypes
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

static OSErr				DisplayOptionsDialog(ULawCompGlobalsPtr globals);
static void					SetButtonState(DialogPtr dialog, short item, Boolean state);
static short				GetButtonState(DialogPtr dialog, short item);
static OSErr				GetCompressionParams(ULawCompGlobalsPtr globals, AudioCompressionAtomHandle *params);
static OSErr				SetCompressionParams(ULawCompGlobalsPtr globals, UserDataAtom *atom);
static ComponentResult		PrimeSource(ULawCompGlobalsPtr globals);
static unsigned long		SamplesToBytes(unsigned long sampleCount,  CompressionInfoPtr compInfo);
static unsigned long		BytesToSamples(unsigned long byteCount,  CompressionInfoPtr compInfo);
static unsigned long		SamplesToFrames(unsigned long sampleCount,  CompressionInfoPtr compInfo);
static unsigned long		FramesToSamples(unsigned long framesCount,  CompressionInfoPtr compInfo);
static void					CompressThisMess(Byte *inputBuffer, Byte *outputBuffer, Byte *ulawTable, long samplesToConvert, short numChannels);
static void					GetCompressorInfo(CompressionInfoPtr cp);
static void					CompressULaw(SInt16 *inPtr, Byte *outPtr, Byte *ulawTable, long sampleCount, short numChannels, short whichChannel);

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//			Component Dispatcher
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

#if TARGET_CPU_68K
	#define COMPONENT_C_DISPATCHER
	#define COMPONENT_DISPATCH_MAIN
#endif

#define CALLCOMPONENT_BASENAME() 		__uLawComp
#define CALLCOMPONENT_GLOBALS() 		ULawCompGlobalsPtr storage

#define SOUNDCOMPONENT_BASENAME() 		CALLCOMPONENT_BASENAME()
#define SOUNDCOMPONENT_GLOBALS() 		CALLCOMPONENT_GLOBALS()

#define COMPONENT_UPP_SELECT_ROOT()		SoundComponent
#define COMPONENT_DISPATCH_FILE			"uLawCodecDispatch.h"

#define	GET_DELEGATE_COMPONENT()		(storage->sourceComponent)

#include "Components.k.h"
#include "Sound.k.h"
#include "ComponentDispatchHelper.c"


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//			Component Routines
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
static pascal ComponentResult __uLawCompOpen(ULawCompGlobalsPtr globals, ComponentInstance self)
{
	OSErr				result;

	globals = (ULawCompGlobalsPtr)NewPtrSysClear(sizeof(ULawCompGlobals));
	FailWithAction(globals == nil, result = MemError(), Failure);
	
	result = GetComponentResource((Component)self, k8BitTableResType, kSoundCompressorResID, &globals->ulawTableHandle);
	FailIf(result != noErr, NoResource);
	
	HLock(globals->ulawTableHandle);
	globals->ulawTable = (Byte *)StripAddress(*globals->ulawTableHandle);

	globals->self = self;
	globals->outputData.format = kOutputFormat;			// output format
	globals->outputData.sampleSize = kOutputSampleSize;	// output sample size

	globals->outputBufferSamples = kOutputSamples;		// will be set to requested->sampleCount later

	// set our storage pointer to our globals
	SetComponentInstanceStorage(self, (Handle) globals);
	return (noErr);

NoResource:
	DisposePtr((Ptr)globals);
Failure:
	return (result);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
static pascal ComponentResult __uLawCompClose(ULawCompGlobalsPtr globals, ComponentInstance self)
{
	ComponentResult		result;

	self; // suppress "unused variable" warning for all compilers

	if (globals != nil)									// we have some globals
	{
		if (globals->sourceComponent)
		{
			result = CloseComponent(globals->sourceComponent);	// torch source component
			FailMessage(result != noErr);
		}
		globals->outputData.sampleCount = 0;			// nothing in our buffer now

		if (globals->ulawTableHandle != nil)
			DisposeHandle(globals->ulawTableHandle);
		DisposePtr((Ptr)globals);						// torch them
	}

	return (noErr);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
pascal ComponentResult __uLawCompVersion(ULawCompGlobalsPtr globals)
{
	globals; // suppress "unused variable" warning for all compilers

	return (kSoundCompressorVersion);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Sound Component Methods
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
static pascal ComponentResult __uLawCompSetSource(ULawCompGlobalsPtr globals, SoundSource sourceID, ComponentInstance source)
{
	SoundComponentData		sourceData;
	short					i;
	Byte					*bPtr;

	globals->sourceID = sourceID;
	globals->sourceComponent = source;					// our food source
	globals->sourceDataPtr = nil;						// nothing read from source yet

	bPtr = (Byte *)&sourceData;							// zero out struct
	for (i = 0; i < sizeof(SoundComponentData); i++)
		bPtr = 0;
		
	sourceData.format = kInputFormat;					// our source must give us this format
	sourceData.sampleSize = kInputSampleSize;
	sourceData.sampleCount = globals->outputBufferSamples;

	// make sure we can get the source we need
    return (noErr);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
static pascal ComponentResult __uLawCompSetOutput(ULawCompGlobalsPtr globals, SoundComponentDataPtr requested, SoundComponentDataPtr *actual)
{
	globals->outputBufferSamples = requested->sampleCount;				// no. samples to output
	if (globals->outputBufferSamples > kOutputSamples)					// too much for our buffer
		globals->outputBufferSamples = kOutputSamples;

	// must be one of the output formats we support
	if (requested->format != kOutputFormat)
		goto Failure;

	globals->outputData = *requested;
	return (noErr);

Failure:
	// here's what we can do
	*actual = &globals->outputData;
	return (paramErr);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
static pascal ComponentResult __uLawCompGetInfo(ULawCompGlobalsPtr globals, SoundSource sourceID, OSType selector, void *infoPtr)
{
	ComponentResult		result;

	result = noErr;
	switch (selector)
	{
		case siCompressionFactor:
			GetCompressorInfo(infoPtr);
			break;

		case siCompressionParams:
			result = GetCompressionParams(globals, infoPtr);
			break;

		case siOptionsDialog:
			*(short *)infoPtr = true;
			break;

		default:
			if (globals->sourceComponent == nil)
				result = siUnknownInfoType;
			else
				result = SoundComponentGetInfo(globals->sourceComponent, sourceID, selector, infoPtr);
			break;
	}
	return (result);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
static pascal ComponentResult __uLawCompSetInfo(ULawCompGlobalsPtr globals, SoundSource sourceID, OSType selector, void *infoPtr)
{
	ComponentResult		result;

	result = noErr;
	switch (selector)
	{
		case siOptionsDialog:
			DisplayOptionsDialog(globals);
			break;

		case siCompressionParams:
			result = SetCompressionParams(globals, infoPtr);
			break;

		default:
			if (globals->sourceComponent == nil)
				result = siUnknownInfoType;
			else
				result = SoundComponentSetInfo(globals->sourceComponent, sourceID, selector, infoPtr);
			break;
	}

	return (result);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
static pascal ComponentResult __uLawCompPlaySourceBuffer(ULawCompGlobalsPtr globals, SoundSource sourceID, SoundParamBlockPtr pb, long actions)
{
	globals->sourceDataPtr = nil;						// no source yet
	globals->outputData.sampleCount = 0;				// our buffer is empty

	return (SoundComponentPlaySourceBuffer(globals->sourceComponent, sourceID, pb, actions));
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
static pascal ComponentResult __uLawCompStopSource(ULawCompGlobalsPtr globals, short count, SoundSource *sources)
{
	globals->sourceDataPtr = nil;						// assume our source is gone
	globals->leftOverSamples = 0;						// clear anything in leftover buffer
	return (SoundComponentStopSource(globals->sourceComponent, count, sources));
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
static pascal ComponentResult __uLawCompGetSourceData(ULawCompGlobalsPtr globals, SoundComponentDataPtr *resultData)
{
	SoundComponentDataPtr	sourceDataPtr;
	long					samplesToCopy;
	unsigned long			bytesToCopy, framesToCopy, byteOffset;
	ComponentResult			result = noErr;

	if (globals->sourceDataPtr == nil)							// no source pointer so...
	{
		result = PrimeSource(globals);							// get data from our source
		FailIf(result != noErr, Failure);
	}

	sourceDataPtr = globals->sourceDataPtr;						// get pointer to source sound component
	if ((sourceDataPtr->format == globals->outputData.format) ||		// input and output are same
		(sourceDataPtr->buffer == nil))									// or no source buffer
	{
		globals->sourceDataPtr = nil;							// get new source next time
		*resultData = sourceDataPtr;							// pass source on down
		return (noErr);											// get out
	}

	globals->outputData.buffer = globals->buffer;				// initialize output buffer
	globals->outputData.sampleCount = 0;

	while ((sourceDataPtr->sampleCount < kULawBlockSamples)	||	// we don't have enough source for at least one block
		   (globals->leftOverSamples))							// or we have samples in the leftover buffer
	{
		if (sourceDataPtr->sampleCount == 0)					// used up all the source
		{
			result = SoundComponentGetSourceData(globals->sourceComponent, &globals->sourceDataPtr);	// get more source
			FailIf(result != noErr, Failure);
			sourceDataPtr = globals->sourceDataPtr;				// get pointer to source sound component
			if (sourceDataPtr->sampleCount == 0)				// still no source samples - all done
				break;
		}

		samplesToCopy = kULawBlockSamples - globals->leftOverSamples; // compute samples needed to fill leftover buffer
		if (sourceDataPtr->sampleCount < samplesToCopy)				// not enough source to fill it, so do what we can
			samplesToCopy = sourceDataPtr->sampleCount;

		// copy from source into leftover buffer
		bytesToCopy = SamplesToBytes(samplesToCopy, &globals->sourceCompInfo);
		byteOffset = SamplesToBytes(globals->leftOverSamples, &globals->sourceCompInfo);
		BlockMoveData(sourceDataPtr->buffer, globals->leftOverBuffer + byteOffset, bytesToCopy);

		sourceDataPtr->buffer += bytesToCopy;					// samples removed from source
		sourceDataPtr->sampleCount -= samplesToCopy;
		globals->leftOverSamples += samplesToCopy;				// keep track off samples in leftover buffer

		if (globals->leftOverSamples == kULawBlockSamples)		// leftover buffer is full
		{
			CompressThisMess(globals->leftOverBuffer, globals->outputData.buffer, globals->ulawTable, kULawBlockSamples, sourceDataPtr->numChannels);

			globals->outputData.buffer += SamplesToBytes(kULawBlockSamples, &globals->destCompInfo);
			globals->outputData.sampleCount += kULawBlockSamples;
			globals->leftOverSamples = 0;

			if (globals->outputData.sampleCount == kOutputSamples)	// output buffer is full
				break;											// stop converting
		}
	}

	samplesToCopy = kOutputSamples - globals->outputData.sampleCount;	// find amount available in output buffer
	if (samplesToCopy > sourceDataPtr->sampleCount)						// don't copy more than we have
		samplesToCopy = sourceDataPtr->sampleCount;

	framesToCopy = SamplesToFrames(samplesToCopy, &globals->destCompInfo);

	if (framesToCopy)											// source has some data for us
	{
		samplesToCopy = FramesToSamples(framesToCopy, &globals->destCompInfo);

		CompressThisMess(sourceDataPtr->buffer, globals->outputData.buffer, globals->ulawTable, samplesToCopy, sourceDataPtr->numChannels);

		sourceDataPtr->buffer += SamplesToBytes(samplesToCopy, &globals->sourceCompInfo);	// update input buffer
		sourceDataPtr->sampleCount -= samplesToCopy;

		globals->outputData.buffer += SamplesToBytes(samplesToCopy, &globals->destCompInfo);
		globals->outputData.sampleCount += samplesToCopy;
	}

	globals->outputData.buffer = globals->buffer;				// reset to beginning of buffer
	*resultData = &globals->outputData;							// tell them what we made
	return (noErr);

Failure:
	return (result);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//		sub routines    _
//				   	  _|_
//		o		_____|   |_____
//		  o o  +______________/
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
static OSErr DisplayOptionsDialog(ULawCompGlobalsPtr globals)
{
	DialogPtr		dialog;
	short			resFile;
	short			currentResFile;
	short			itemHit;
	OSErr			result;

	result = noErr;
	currentResFile = CurResFile();
	resFile = OpenComponentResFile((Component)globals->self);
	FailWithAction(resFile == kResFileNotOpened, result = ResError(), Exit);

	dialog = GetNewDialog(kSoundCompressorResID, nil, (WindowPtr)-1);
	FailWithAction(dialog == nil, result = resNotFound, NoDialog);

	MacSetPort(dialog);
	SetButtonState(dialog, kOptionCheckBox, true);
	SetDialogDefaultItem(dialog, kStdOkItemIndex);
	SetDialogCancelItem(dialog, kStdCancelItemIndex);
	MacShowWindow(dialog);

	do
	{
		ModalDialog(nil, &itemHit);
		switch (itemHit)
		{
			case kOptionCheckBox:
				if (GetButtonState(dialog, kOptionCheckBox))
					SetButtonState(dialog, kOptionCheckBox, false);
				else
					SetButtonState(dialog, kOptionCheckBox, true);
				break;
				
			case kStdOkItemIndex:
				GetButtonState(dialog, kOptionCheckBox);
				// accept changes according to values set in dialog
				break;
		}
	} while ( (itemHit != kStdOkItemIndex) && (itemHit != kStdCancelItemIndex) );

	DisposeDialog(dialog);

NoDialog:
	CloseResFile(resFile);
Exit:
	UseResFile(currentResFile);
	return (result);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
static void SetButtonState(DialogPtr dialog, short item, Boolean state)
{
	short		iType;
	Handle		iHandle;
	Rect		iRect;

	GetDialogItem(dialog, item, &iType, &iHandle, &iRect);
	if (iHandle != nil)
		SetControlValue((ControlHandle) iHandle, (state) ? kControlRadioButtonCheckedValue : kControlRadioButtonUncheckedValue);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
static short GetButtonState(DialogPtr dialog, short item)
{
	short		iType;
	Handle		iHandle;
	Rect		iRect;

	GetDialogItem(dialog, item, &iType, &iHandle, &iRect);
	return (GetControlValue((ControlHandle) iHandle));
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
static OSErr GetCompressionParams(ULawCompGlobalsPtr globals, AudioCompressionAtomHandle *params)
{
	AudioCompressionAtomHandle		atom;
	AudioCompressionAtomPtr			atomPtr;
	OSErr							result;

	globals; // suppress "unused variable" warning for all compilers

	result = noErr;
	atom = (AudioCompressionAtomHandle)NewHandle(sizeof(AudioCompressionAtom));
	FailWithAction(atom == nil, result = MemError(), Exit);

	atomPtr = *atom;
	atomPtr->formatData.size = EndianU32_NtoB(sizeof(AudioFormatAtom));
	atomPtr->formatData.atomType = EndianU32_NtoB(kAudioFormatAtomType);
	atomPtr->formatData.format = EndianU32_NtoB(kOutputFormat);

	atomPtr->endianData.size = EndianU32_NtoB(sizeof(AudioEndianAtom));
	atomPtr->endianData.atomType = EndianU32_NtoB(kAudioEndianAtomType);
	atomPtr->endianData.littleEndian = EndianU16_NtoB(false);		// µlaw is not little endian

	atomPtr->terminatorData.size = EndianU32_NtoB(sizeof(AudioTerminatorAtom));
	atomPtr->terminatorData.atomType = EndianU32_NtoB(kAudioTerminatorAtomType);

	*params = atom;

Exit:
	return (result);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
static OSErr SetCompressionParams(ULawCompGlobalsPtr globals, UserDataAtom *atom)
{
	OSErr			result;
	short			littleEndian;
	Boolean			moreAtoms;

	globals; // suppress "unused variable" warning for all compilers

	result = noErr;
	moreAtoms = true;
	littleEndian = false;
	do
	{
		long atomSize = EndianS32_BtoN(atom->size);
		FailWithAction(atomSize < 8, result = invalidAtomErr, Exit);
		switch (EndianU32_BtoN(atom->atomType))
		{
			case kAudioFormatAtomType:
				FailWithAction(((AudioFormatAtom *)atom)->format != EndianU32_NtoB(kOutputFormat), result = badFormat, Exit);
				break;

			case kAudioEndianAtomType:
				littleEndian = EndianU16_BtoN(((AudioEndianAtom *)atom)->littleEndian);
				break;

			case kAudioTerminatorAtomType:
				moreAtoms = false;
				break;

			default:	// unknown atom type
				break;
		}
		atom = (UserDataAtom *)((long)atom + atomSize);
	} while (moreAtoms);
	if (littleEndian)
		result = paramErr;						// we do not do little endian

Exit:
	return (result);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
static ComponentResult PrimeSource(ULawCompGlobalsPtr globals)
{
	ComponentResult			result = noErr;
	SoundComponentDataPtr	sourceDataPtr;

	result = SoundComponentGetSourceData(globals->sourceComponent, &globals->sourceDataPtr);
	FailIf(result != noErr, Failure);
	FailWithAction(globals->sourceDataPtr == nil, result = paramErr, Failure);

	sourceDataPtr = globals->sourceDataPtr;
	globals->outputData.flags = sourceDataPtr->flags;				// copy flags unchanged
	globals->outputData.sampleRate = sourceDataPtr->sampleRate;		// copy sample rate unchanged
	globals->outputData.numChannels = sourceDataPtr->numChannels;	// copy numchannels unchanged

	globals->sourceCompInfo.recordSize = sizeof(CompressionInfo);	// get source compression info
	result = GetCompressionInfo(fixedCompression, sourceDataPtr->format,
								sourceDataPtr->numChannels, sourceDataPtr->sampleSize,
								&globals->sourceCompInfo);
	FailIf(result != noErr, Failure);

	globals->destCompInfo.recordSize = sizeof(CompressionInfo);
	GetCompressorInfo(&globals->destCompInfo);					// get dest compression info
	globals->destCompInfo.bytesPerFrame = globals->destCompInfo.bytesPerPacket * sourceDataPtr->numChannels;

Failure:
	return (result);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
static unsigned long SamplesToBytes(unsigned long sampleCount, CompressionInfoPtr compInfo)
{
	return ((sampleCount / compInfo->samplesPerPacket) * compInfo->bytesPerFrame);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
static unsigned long BytesToSamples(unsigned long byteCount, CompressionInfoPtr compInfo)
{
	return ((byteCount / compInfo->bytesPerFrame) * compInfo->samplesPerPacket);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
static unsigned long SamplesToFrames(unsigned long sampleCount, CompressionInfoPtr compInfo)
{
	return (sampleCount / compInfo->samplesPerPacket);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
static unsigned long FramesToSamples(unsigned long frameCount, CompressionInfoPtr compInfo)
{
	return (frameCount * compInfo->samplesPerPacket);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
static void CompressThisMess(Byte *inputBuffer, Byte *outputBuffer, Byte *ulawTable, long samplesToConvert, short numChannels)
{
	if (numChannels == 1)
	{
		CompressULaw((SInt16 *) inputBuffer, outputBuffer, ulawTable, samplesToConvert, 1, 1);
	}
	else
	{
		CompressULaw((SInt16 *) inputBuffer, outputBuffer, ulawTable, samplesToConvert, 2, 1);
		CompressULaw((SInt16 *) inputBuffer, outputBuffer, ulawTable, samplesToConvert, 2, 2);
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
static void GetCompressorInfo(CompressionInfoPtr cp)
{
	if (cp->recordSize > sizeof(CompressionInfo))				// limit amount we return
		cp->recordSize = sizeof(CompressionInfo);

	cp->compressionID = fixedCompression;
	cp->format = kOutputFormat;
	cp->samplesPerPacket = kULawBlockSamples;
	cp->bytesPerPacket = kULawBlockBytes;
	cp->bytesPerSample = kULawBytesPerSample;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
static void CompressULaw(SInt16 *inPtr, Byte *outPtr, Byte *ulawTable, long sampleCount,
				  short numChannels, short whichChannel)
{
	long			sampnum;
    int				sample, sign, exponent, mantissa;
    unsigned char	ulawbyte;

	whichChannel -= 1;
	inPtr += whichChannel;						/* skip to first sample */
	outPtr += whichChannel;

  	for (sampnum = 0; sampnum < sampleCount; sampnum++)
  	{
		sample = *inPtr;
		inPtr += numChannels;					/* skip to next sample */

	    /* Get the sample into sign-magnitude. */
	    sign = (sample >> 8) & 0x80;			/* set aside the sign */
	    if (sign != 0)
	    	sample = -sample;					/* get magnitude */
	    if (sample > CLIP)
	    	sample = CLIP;						/* clip the magnitude */

	    /* Convert from 16 bit linear to ulaw. */
	    sample = sample + BIAS;
	    exponent = ulawTable[(sample >> 7) & 0xFF];
	    mantissa = (sample >> (exponent + 3)) & 0x0F;
		ulawbyte = ~(sign | (exponent << 4) | mantissa);
#ifdef ZEROTRAP
	    if (ulawbyte == 0)
	    	ulawbyte = 0x02;					/* optional CCITT trap */
#endif
		*outPtr = ulawbyte;						/* output compressed sample */
		outPtr += numChannels;
	}
}
