/*=============================================================================
	CAAUParameter.cpp
	
	$Log: CAAUParameter.cpp,v $
	Revision 1.9.2.1  2005/09/20 23:24:01  bills
	change resolution for StringValue return.
	
	Revision 1.9  2005/01/28 03:25:59  bills
	GetStringFromValueCopy deals with indexed named params
	
	Revision 1.8  2005/01/25 19:47:37  luke
	add newline at EOF
	
	Revision 1.7  2004/09/13 23:16:53  luke
	add 'ratio'
	
	Revision 1.6  2004/07/13 00:43:05  bills
	some cleanup
	
	Revision 1.5  2004/07/10 01:40:38  bills
	some cleanup here
	
	Revision 1.4  2004/05/11 01:24:04  bills
	fix memory leak in operator=
	
	Revision 1.3  2004/02/18 01:51:55  luke
	fix case misordering
	
	Revision 1.2  2004/02/18 01:38:38  luke
	sync with changes to NamedParameters in AudioUnit framework
	
	Revision 1.1  2003/12/01 18:38:47  luke
	moved here from AUPublic/AUCarbonViewBase
	
	Revision 1.11  2003/08/18 17:51:18  bills
	fix struct for parameter names
	
	Revision 1.10  2003/08/14 00:05:42  mhopkins
	Radar 3376853: two new routines added to get name of a specific value
	
	Revision 1.9  2003/08/01 18:24:59  bills
	fix precedence problem
	
	Revision 1.8  2003/08/01 00:48:08  mhopkins
	Radar #3359266: Cleanup release semantics
	
	Revision 1.7  2003/07/10 18:17:02  luke
	bounds-check parameters before setting [3192162]
	
	Revision 1.6  2002/10/18 21:24:31  bills
	add name strings for new param unit values
	
	Revision 1.5  2002/06/16 02:48:03  bills
	if you're checking a flag use & not |!!!
	
	Revision 1.4  2002/06/16 01:18:34  bills
	add handling of indexed params (inc. named ones - with popup menu)
	
	Revision 1.3  2002/06/14 06:04:41  bills
	deal with parameters with CFString names
	
	Revision 1.2  2002/06/13 09:45:06  bills
	deal with parameterUnits
	
	Revision 1.1  2002/03/07 07:31:30  dwyatt
	moved from AUViewBase and made Carbon-specific -- a Cocoa plugin is
	going to get written in ObjC so why bother with abstracting for it
	in C++
	
	created 11 Feb 2002, 12:39, Doug Wyatt
	Copyright (c) 2002 Apple Computer, Inc.  All Rights Reserved
	
	$NoKeywords: $
=============================================================================*/

#include "CAAUParameter.h"

CAAUParameter::CAAUParameter() 
{
	memset(this, 0, sizeof(CAAUParameter));
}

CAAUParameter::CAAUParameter(AudioUnit au, AudioUnitParameterID param, AudioUnitScope scope, AudioUnitElement element)
{
	memset(this, 0, sizeof(CAAUParameter));
	Init (au, param, scope, element);
}

CAAUParameter::CAAUParameter (AudioUnitParameter &inParam)
{
	memset(this, 0, sizeof(CAAUParameter));
	Init (inParam.mAudioUnit, inParam.mParameterID, inParam.mScope, inParam.mElement);
}

CAAUParameter::CAAUParameter(const CAAUParameter &a) 
{
	memset(this, 0, sizeof(CAAUParameter));
	*this = a;
}

CAAUParameter &	CAAUParameter::operator = (const CAAUParameter &a)
{
	if (mParamName) CFRelease(mParamName);
	if (mParamTag) CFRelease(mParamTag);
	if (mNamedParams) CFRelease(mNamedParams);
	
	memcpy(this, &a, sizeof(CAAUParameter));

	if (mParamName) CFRetain(mParamName);
	if (mParamTag) CFRetain(mParamTag);
	if (mNamedParams) CFRetain(mNamedParams);
	
	return *this;
}

CAAUParameter::~CAAUParameter()
{
	if (mParamName) CFRelease(mParamName);
	if (mParamTag) CFRelease(mParamTag);
	if (mNamedParams) CFRelease (mNamedParams);
}

void		CAAUParameter::Init (AudioUnit au, AudioUnitParameterID param, AudioUnitScope scope, AudioUnitElement element)
{
	mAudioUnit = au;
	mParameterID = param;
	mScope = scope;
	mElement = element;
	
	UInt32 propertySize = sizeof(mParamInfo);
	OSStatus err = AudioUnitGetProperty(au, kAudioUnitProperty_ParameterInfo,
			scope, param, &mParamInfo, &propertySize);
	if (err)
		memset(&mParamInfo, 0, sizeof(mParamInfo));
	if (mParamInfo.flags & kAudioUnitParameterFlag_HasCFNameString) {
		mParamName = mParamInfo.cfNameString;
		if (!(mParamInfo.flags & kAudioUnitParameterFlag_CFNameRelease)) 
			CFRetain (mParamName);
	} else
		mParamName = CFStringCreateWithCString(NULL, mParamInfo.name, kCFStringEncodingUTF8);
	
	char* str = 0;
	switch (mParamInfo.unit)
	{
		case kAudioUnitParameterUnit_Boolean:
			str = "T/F";
			break;
		case kAudioUnitParameterUnit_Percent:
		case kAudioUnitParameterUnit_EqualPowerCrossfade:
			str = "%";
			break;
		case kAudioUnitParameterUnit_Seconds:
			str = "Secs";
			break;
		case kAudioUnitParameterUnit_SampleFrames:
			str = "Samps";
			break;
		case kAudioUnitParameterUnit_Phase:
		case kAudioUnitParameterUnit_Degrees:
			str = "Degr.";
			break;
		case kAudioUnitParameterUnit_Hertz:
			str = "Hz";
			break;
		case kAudioUnitParameterUnit_Cents:
		case kAudioUnitParameterUnit_AbsoluteCents:
			str = "Cents";
			break;
		case kAudioUnitParameterUnit_RelativeSemiTones:
			str = "S-T";
			break;
		case kAudioUnitParameterUnit_MIDINoteNumber:
		case kAudioUnitParameterUnit_MIDIController:
			str = "MIDI";
				//these are inclusive, so add one value here
			mNumIndexedParams = short(mParamInfo.maxValue+1 - mParamInfo.minValue);
			break;
		case kAudioUnitParameterUnit_Decibels:
			str = "dB";
			break;
		case kAudioUnitParameterUnit_MixerFaderCurve1:
		case kAudioUnitParameterUnit_LinearGain:
			str = "Gain";
			break;
		case kAudioUnitParameterUnit_Pan:
			str = "L/R";
			break;
		case kAudioUnitParameterUnit_Meters:
			str = "Mtrs";
			break;
		case kAudioUnitParameterUnit_Octaves:
			str = "8ve";
			break;
		case kAudioUnitParameterUnit_BPM:
			str = "BPM";
			break;
		case kAudioUnitParameterUnit_Beats:
			str = "Beats";
			break;
		case kAudioUnitParameterUnit_Milliseconds:
			str = "msecs";
			break;
		case kAudioUnitParameterUnit_Ratio:
			str = "ratio";
			break;
		case kAudioUnitParameterUnit_Indexed:
			{
				propertySize = sizeof(mNamedParams);
				err = AudioUnitGetProperty (au, 
									kAudioUnitProperty_ParameterValueStrings,
									scope, 
									param, 
									&mNamedParams, 
									&propertySize);
				if (!err && mNamedParams) {
					mNumIndexedParams = CFArrayGetCount(mNamedParams);
				} else {
						//these are inclusive, so add one value here
					mNumIndexedParams = short(mParamInfo.maxValue+1 - mParamInfo.minValue);
				}
				str = NULL;
			}
			break;
		case kAudioUnitParameterUnit_CustomUnit:
		{
			CFStringRef unitName = mParamInfo.unitName;
			static char paramStr[256];
			CFStringGetCString (unitName, paramStr, 256, kCFStringEncodingUTF8);
			if (mParamInfo.flags & kAudioUnitParameterFlag_CFNameRelease)
				CFRelease (unitName);
			str = paramStr;
			break;
		}
		case kAudioUnitParameterUnit_Generic:
		case kAudioUnitParameterUnit_Rate:
		default:
			str = NULL;
			break;
	}
	
	if (str)
		mParamTag = CFStringCreateWithCString(NULL, str, kCFStringEncodingUTF8);
	else
		mParamTag = NULL;
}


Float32		CAAUParameter::GetValue() const
{
	Float32 value = 0.;
	//OSStatus err = 
	AudioUnitGetParameter(mAudioUnit, mParameterID, mScope, mElement, &value);
	return value;
}

CFStringRef CAAUParameter::GetStringFromValueCopy(const Float32 *value) const
{
	if (HasNamedParams())
	{
		Float32 val = (value == NULL ? GetValue() : *value);
		int index = int(mParamInfo.minValue) + int(val);
		CFStringRef str = GetParamName (index);
		if (str) {
			CFRetain (str);
			return str;
		}
	}
	else if (ValuesHaveStrings()) 
	{
		AudioUnitParameterStringFromValue stringValue;
		stringValue.inParamID = mParameterID;
		stringValue.inValue = value;
		stringValue.outString = NULL;
		UInt32 propertySize = sizeof(stringValue);
		
		OSStatus err = AudioUnitGetProperty (mAudioUnit, 
											kAudioUnitProperty_ParameterStringFromValue,
											mScope, 
											mParameterID, 
											&stringValue, 
											&propertySize);
		
		if (err == noErr && stringValue.outString != NULL)
			return stringValue.outString;
	}
	
	Float32 val = (value == NULL ? GetValue() : *value);
	char valstr[32];
	AUParameterFormatValue (val, this, valstr, 4);
	return CFStringCreateWithCString(NULL, valstr, kCFStringEncodingUTF8);
}

Float32 CAAUParameter::GetValueFromString(CFStringRef str) const
{
	if (ValuesHaveStrings()) 
	{
		AudioUnitParameterValueFromString valueString;
		valueString.inParamID = mParameterID;
		valueString.inString = str;
		UInt32 propertySize = sizeof(valueString);
		
		OSStatus err = AudioUnitGetProperty (mAudioUnit, 
										kAudioUnitProperty_ParameterValueFromString,
										mScope, 
										mParameterID, 
										&valueString, 
										&propertySize);
										
		if (err == noErr) {
			return valueString.outValue;
		}
	}
	
	Float32 paramValue = mParamInfo.defaultValue;
	char valstr[32];
	CFStringGetCString(str, valstr, sizeof(valstr), kCFStringEncodingUTF8);
	sscanf(valstr, "%f", &paramValue);
	return paramValue;
}

void		CAAUParameter::SetValue(	AUParameterListenerRef		inListener, 
									void *							inObject,
									Float32							inValue) const
{
    // clip inValue as: maxValue >= inValue >= minValue before setting
    Float32 valueToSet = inValue;
    if (valueToSet > mParamInfo.maxValue)
        valueToSet = mParamInfo.maxValue;
    if (valueToSet < mParamInfo.minValue)
        valueToSet = mParamInfo.minValue;
    
	AUParameterSet(inListener, inObject, this, valueToSet, 0);
}

#if DEBUG
void	CAAUParameter::Print() const
{
	UInt32 clump = 0;
	GetClumpID (clump);
	
	UInt32 len = CFStringGetLength(mParamName);
	char* chars = (char*)malloc (len * 2); // give us plenty of room for unichar chars
	if (!CFStringGetCString (mParamName, chars, len * 2, kCFStringEncodingUTF8))
		chars[0] = 0;
	
	printf ("ID: %ld, Clump: %ld, Name: %s\n", mParameterID, clump, chars);
	free (chars);
}
#endif
