<commit_before>/* 
 * A Data Object
 * Copyright © 2010, <NAME>
 * 
 * License: This code is licensed under the terms of the "New BSD License"
 * http://creativecommons.org/licenses/BSD/
 */

#include "TTData.h"

#define thisTTClass			TTData
#define thisTTClassName		"Data"
#define thisTTClassTags		"data"

TT_MODULAR_CONSTRUCTOR,
mValue(TTValue(0.0)),
mValueDefault(kTTValNONE),
mValueStepsize(TTValue(0.1)),
mType(kTTSym_generic),
mTag(kTTValNONE),
mPriority(0),
mDescription(kTTSymEmpty),
mRepetitionsAllow(YES),
mEnable(YES),
mInitialized(NO),
mRangeBounds(0.0, 1.0),
mRangeClipmode(kTTSym_none),
mDynamicInstances(NO),
mInstanceBounds(0, -1),
#ifdef TTDATA_RAMPLIB
mRampDrive(kTTSym_none),
mRampFunction(kTTSymEmpty),
#endif
mDataspace(kTTSym_none),
mDataspaceUnit(kTTSym_none),
mDataspaceConverter(NULL),
mService(kTTSymEmpty),
mReturnValueCallback(NULL)
{
	TT_ASSERT("Correct number of args to create TTData", arguments.getSize() == 1);
	
	arguments.get(0, (TTPtr*)&mReturnValueCallback);
	TT_ASSERT("Return Value Callback passed to TTData is not NULL", mReturnValueCallback);
	
	if (arguments.getSize() == 2)
		arguments.get(1, &mService);
	
	addAttributeWithGetterAndSetter(Value, kTypeNone);
	addAttributeWithGetterAndSetter(ValueDefault, kTypeNone);
	addAttributeWithGetterAndSetter(ValueStepsize, kTypeNone);
	
	addAttributeWithSetter(Type, kTypeSymbol);
	addAttributeWithSetter(Tag, kTypeLocalValue);
	addAttribute(Priority, kTypeInt32);
	addAttribute(Description, kTypeSymbol);
	addAttributeWithSetter(RepetitionsAllow, kTypeBoolean);
	
	if (mService == kTTSym_return) {
		addAttributeWithSetter(Enable, kTypeBoolean);
	}
	
	addAttribute(Initialized, kTypeBoolean);
	addAttributeProperty(initialized, readOnly, YES);
	addAttributeProperty(initialized, hidden, YES);
	
	addAttributeWithSetter(RangeBounds, kTypeLocalValue);
	addAttributeWithSetter(RangeClipmode, kTypeSymbol);
	
	addAttribute(DynamicInstances, kTypeBoolean);
	addAttributeProperty(dynamicInstances, hidden, YES);
	addAttributeWithSetter(InstanceBounds, kTypeLocalValue);
	addAttributeProperty(instanceBounds, hidden, YES);
	
#ifdef TTDATA_RAMPLIB
	addAttributeWithSetter(RampDrive, kTypeSymbol);
	addAttributeWithSetter(RampFunction, kTypeSymbol);
#endif
	
	addAttributeWithSetter(Dataspace, kTypeSymbol);
	addAttributeWithSetter(DataspaceUnit, kTypeSymbol);
	
	addAttribute(Service, kTypeSymbol);
	addAttributeProperty(service, readOnly, YES);
	
	addMessage(Reset);
	addMessageWithArgument(Inc);
	addMessageWithArgument(Dec);
	addMessageWithArgument(Command);
	addMessageProperty(Command, hidden, YES);
	
	// needed to be handled by a TTTextHandler
	addMessageWithArgument(WriteAsText);
	addMessageProperty(WriteAsText, hidden, YES);
	
	mIsSending = NO;
	
#ifdef TTDATA_RAMPLIB
	mRamper = NULL;
	mRampDataNames = new TTHash();
#endif
}

TTData::~TTData()
{
#ifdef TTDATA_RAMPLIB	
	if (mRamper)
		TTObjectRelease(TTObjectHandle(&mRamper));
#endif
	
	if (mDataspaceConverter)
		TTObjectRelease(TTObjectHandle(&mDataspaceConverter));
	
	if (mReturnValueCallback) {
		delete (TTValuePtr)mReturnValueCallback->getBaton();
		TTObjectRelease(TTObjectHandle(&mReturnValueCallback));
	}
}

TTErr TTData::Reset()
{
	// if valueDefault type is right
	if (checkType(mValueDefault))
		if (!(mValueDefault == kTTValNONE))
			setValue(mValueDefault);
	
	// Set data to be uninitialised
	// to circumvent filtering of repetitions when outputing value from default preset
	mInitialized = NO;
	notifyObservers(kTTSym_initialized, NO);
	
	return kTTErrNone;
}

TTErr TTData::Inc(const TTValue& value)
{
	TTUInt32	i;
	TTFloat64	inc, ramptime, v, vStepsize;
	TTSymbolPtr	ramp;
	TTValue		command;
	
	mValueStepsize.get(0, vStepsize);
	
	switch (value.getSize()) {
			
			// 1 incrementation step	
		case 1 :
		{
			if (value.getType(0) == kTypeFloat32 || value.getType(0)  == kTypeInt32) {
				value.get(0, inc);
				
				for (i=0; i<mValue.getSize(); i++) {
					mValue.get(i, v);
					command.append(v + inc * vStepsize);
				}
			}
			break;
		}
			
			// 1 incrementation step + ramp ramptime
		case 3 :
		{
			if (value.getType(0) == kTypeFloat32 || value.getType(0)  == kTypeInt32) {
				value.get(0, inc);
				
				for (i=0; i<mValue.getSize(); i++) {
					mValue.get(i, v);
					command.append(v + inc * vStepsize);
				}
				
				if (value.getType(1) == kTypeSymbol) {
					value.get(1, &ramp);
					if (ramp == kTTSym_ramp) {
						command.append(ramp);
						if (value.getType(2) == kTypeFloat32 || value.getType(2)  == kTypeInt32) {
							value.get(2, ramptime);
							command.append(ramptime);
						}
					}
				}
				break;	
			}
		}
			
			// no value or wrong value
		default :
		{
			for (i=0; i<mValue.getSize(); i++) {
				mValue.get(i, v);
				command.append(v + vStepsize);
			}
			
			break;	
		}
	}
	
	this->Command(command);
	
	return kTTErrNone;
}

TTErr TTData::Dec(const TTValue& value)
{
	TTUInt32	i;
	TTFloat64	dec, ramptime, v, vStepsize;
	TTSymbolPtr	ramp;
	TTValue		command;
	
	mValueStepsize.get(0, vStepsize);
	
	switch (value.getSize()) {
			
			// 1 decrementation step	
		case 1 :
		{
			if (value.getType(0) == kTypeFloat32 || value.getType(0)  == kTypeInt32) {
				value.get(0, dec);
				
				for (i=0; i<mValue.getSize(); i++) {
					mValue.get(i, v);
					command.append(v - dec * vStepsize);
				}
			}
			break;
		}
			
			// 1 decrementation step + ramp ramptime
		case 3 :
		{
			if (value.getType(0) == kTypeFloat32 || value.getType(0)  == kTypeInt32) {
				value.get(0, dec);
				
				for (i=0; i<mValue.getSize(); i++) {
					mValue.get(i, v);
					command.append(v - dec * vStepsize);
				}
				
				if (value.getType(1) == kTypeSymbol) {
					value.get(1, &ramp);
					if (ramp == kTTSym_ramp) {
						command.append(ramp);
						if (value.getType(2) == kTypeFloat32 || value.getType(2)  == kTypeInt32) {
							value.get(2, ramptime);
							command.append(ramptime);
						}
					}
				}
				break;	
			}
		}
			
			// no value or wrong value
		default :
		{
			for (i=0; i<mValue.getSize(); i++) {
				mValue.get(i, v);
				command.append(v - vStepsize);
			}
			
			break;	
		}
	}
	
	this->Command(command);
	
	return kTTErrNone;
}

TTErr TTData::Command(const TTValue& command)
{
	TTMessagePtr aMessage;
	TTErr		err = kTTErrNone;
#ifdef TTDATA_RAMPLIB
	double		time;
#endif
	int			commandSize;
	TTSymbolPtr	unit, ramp;
	TTValue		aValue, c;
	bool		hasRamp = false;
	bool		hasUnit = false;
	
	// 1. Notify Command observer for value changes only
	///////////////////////////////////////////////////
	c = command;											// protect the command value
	err = this->findMessage(kTTSym_Command, &aMessage);
	if (!err)
		aMessage->sendNotification(kTTSym_notify, c);	// we use kTTSym_notify because we know that observers are TTCallback
	
	// 2. Parse the command to handle unit and ramp
	///////////////////////////////////////////////////
	commandSize = command.getSize();
	switch(commandSize) {
			
		// no value	
		case 0 :
		{
			// nothing to do
			break;	
		}
			
		// 1 value	
		case 1 :
		{
			// nothing to do
			break;	
		}
			
		// 2 values || 1 value + unit
		case 2 :
		{
			// Is the second element is a unit symbol ?
			if (mDataspaceUnit != kTTSym_none)
				if (command.getType(0) != kTypeSymbol && command.getType(1) == kTypeSymbol) {
					hasUnit = true;
					command.get(1, &unit);
				}
			
			break;	
		}
			
		// 3 values || 2 values + unit || 1 value + ramp ramptime
		case 3 :
		{
			// Is the second element is a ramp symbol ?
			if (command.getType(1) == kTypeSymbol) {
				command.get(1, &ramp);
				if (ramp == kTTSym_ramp)
					hasRamp = true;
			}
			// or is the last element is a unit symbol ?
			else if (mDataspace != kTTSym_none && command.getType(0) != kTypeSymbol && command.getType(2) == kTypeSymbol) {
				hasUnit = true;
				command.get(2, &unit);
			}
			
			break;	
		}
			
		// X values || X-1 values + unit || X-2 values + ramp ramptime || X-3 values + unit + ramp ramptime
		default :
		{
			// Is the X-2 element is a ramp symbol ?
			if (command.getType(commandSize - 2) == kTypeSymbol) {
				command.get(commandSize - 2, &ramp);
				if (ramp == kTTSym_ramp)
					hasRamp = true;
			}
			
			// Is the X-3 or last element a unit symbol ?
			if (mDataspace != kTTSym_none && command.getType(0) != kTypeSymbol)
				if (hasRamp)
					if (command.getType(commandSize - 3) == kTypeSymbol) {
						hasUnit = true;
						command.get(commandSize - 3, &unit);
					}
					else
						if (command.getType(commandSize - 1) == kTypeSymbol) {
							hasUnit = true;
							command.get(commandSize - 1, &unit);
						}
			
			break;	
		}
			
	}

	
	// 3. Strip ramp or unit informations if needed
	if (hasRamp && hasUnit) {
		aValue = command;
		aValue.setSize(commandSize - 3);
	}
	else if (hasRamp) {
		aValue = command;
		aValue.setSize(commandSize - 2);
	}
	else if (hasUnit) {
		aValue = command;
		aValue.setSize(commandSize - 1);
	}
	else
		aValue = command;
	
	
	// 4. Set Dataspace input unit and convert the value
	// Note : The current implementation does not override the active unit temporarily or anything fancy.
	// It just sets the input unit and then runs with it...
	// For this initial implementation we are converting the values prior to ramping, as it is easier.
	// Ultimately though, we actually want to convert the units after the ramping, 
	// for example to perform a sweep that is linear vs logarithmic
	////////////////////////////////////////////////////////////////
	if (hasUnit)
		if (mDataspaceConverter) {
			mDataspaceConverter->setAttributeValue(TT("inputUnit"), unit);
			convertUnit(aValue);
		}
	
	
	// 5. Ramp the convertedValue
	/////////////////////////////////
#ifdef TTDATA_RAMPLIB
	if (hasRamp) {
		
		command.get(commandSize - 1, time);

		if (time <= 0) {
			setValue(aValue);
			return kTTErrNone;
		}	
		
		if (!mRepetitionsAllow && mInitialized) {
			
			// float to integer case
			if (mType == kTTSym_integer)
				aValue.truncate();
			
			// integer/float to boolean case
			if (mType == kTTSym_boolean)
				aValue.booleanize();
			
			if (mValue == aValue)
				return kTTErrNone;	// nothing to do
		}
		
		if (mRamper) {
			TTUInt16	i, s = aValue.getSize();
			TTFloat64*	startArray = new TTFloat64[s];		// start to mValue
			TTFloat64*	targetArray = new TTFloat64[s];		// go to convertedValue
			
			if(mValue.getSize() != s)
				mValue.setSize(s);
			
			for (i=0; i<s; i++) {
				startArray[i] = mValue.getFloat64(i);
				targetArray[i] = aValue.getFloat64(i);
			}
			
			mRamper->set(s, startArray);		
			mRamper->go(s, targetArray, time);	
			
			delete [] startArray;
			delete [] targetArray;
		}
	} 
	else {
#endif
		// check repetitions
		if (!mRepetitionsAllow && mInitialized) {
			
			// float to integer case
			if (mType == kTTSym_integer)
				aValue.truncate();
			
			// integer/float to boolean case
			if (mType == kTTSym_boolean)
				aValue.booleanize();
			
			if (mValue == aValue)
				return kTTErrNone;	// nothing to do
		}

#ifdef TTDATA_RAMPLIB
		// stop ramping before to set a value
		if (mRamper)
			mRamper->stop();
#endif
		
		setValue(aValue);
		
#ifdef TTDATA_RAMPLIB
	}
#endif
	
	return kTTErrNone;
}

TTErr TTData::getValue(TTValue& value)
{
	// can't get the value before the data has been initialized
	if (mInitialized) {
		
		value = mValue;
		return kTTErrNone;
	}
	else
		return kTTErrGeneric;
}

TTErr TTData::setValue(const TTValue& value)
{
	TTValue r, n;
	
	if (!mIsSending && mEnable) {
		
		// lock
		mIsSending = YES;
		
		// a kTTValNONE would only return the actual value
		if (value == kTTValNONE) {
			
			// if mType is 'none' we have had our value set at least once
			if (mService == kTTSym_parameter && mType == kTTSym_none && !mInitialized) {
				mInitialized = YES;
				notifyObservers(kTTSym_initialized, YES);
			}
		}
		// otherwise check the type of the incoming value
		else if (checkType(value)) {
			
			// set internal value 
			mValue = value;
			
			// float to integer case
			if (mType == kTTSym_integer)
				mValue.truncate();
			
			// integer/float to boolean case
			if (mType == kTTSym_boolean)
				mValue.booleanize();
			
#ifdef TTDATA_RAMPLIB
			if (clipValue() && mRamper)
				mRamper->stop();
#else
			clipValue();
#endif
		}
		else {
			// unlock
			mIsSending = NO;
			return kTTErrInvalidValue;
		}
		
		// used new values to protect the attribute
		r = mValue;
		n = mValue;
		
		// return the value to his owner
		if (!(mService == kTTSym_return))
			this->mReturnValueCallback->notify(r);
		
		// notify each observers
		//if (!(mService == kTTSym_message))		// to -- to allow message to be mapped for example
			notifyObservers(kTTSym_value, n);
		
		// we have had our value set at least once
		// only parameters notify their initialisation
		if (mService == kTTSym_parameter && !mInitialized) {
			mInitialized = YES;
			notifyObservers(kTTSym_initialized, YES);
		}
		else if (!mInitialized) mInitialized = YES;
		
		// unlock
		mIsSending = NO;
		
		return kTTErrNone;
	}
	
	return kTTErrGeneric;
}

TTErr TTData::getValueDefault(TTValue& value)
{
	value = mValueDefault;
	return kTTErrNone;
}

TTErr TTData::setValueDefault(const TTValue& value)
{
	TTValue n = value;				// use new value to protect the attribute
	mValueDefault = value;
	notifyObservers(kTTSym_valueDefault, n);
	return kTTErrNone;
}

TTErr TTData::getValueStepsize(TTValue& value)
{
	value = mValueStepsize;
	return kTTErrNone;
}

TTErr TTData::setValueStepsize(const TTValue& value)
{
	TTValue n = value;				// use new value to protect the attribute
	mValueStepsize = value;
	notifyObservers(kTTSym_valueStepsize, n);
	return kTTErrNone;
}

TTErr TTData::setType(const TTValue& value)
{
	TTAttributePtr valueAttribute, valueDefaultAttribute, valueStepSizeAttribute;
	// if the new type is different
	if (!(TTValue(mType) == value)) {
		
		TTValue n = value;				// use new value to protect the attribute
		mType = value;
		
		// Get Value, ValueDefault and ValueStepsize attributes
		this->findAttribute(kTTSym_value, &valueAttribute);
		this->findAttribute(kTTSym_value, &valueDefaultAttribute);
		this->findAttribute(kTTSym_valueStepsize, &valueStepSizeAttribute);

		mInstanceBounds.set(0, TTInt16(0));
		mInstanceBounds.set(1, TTInt16(-1));
		
		// register mValue Attribute and prepare memory
		if (mType == kTTSym_integer) {
			valueAttribute->type = kTypeInt32;
			valueDefaultAttribute->type = kTypeInt32;
			valueStepSizeAttribute->type = kTypeInt32;
			mValue = TTValue(0);
			mValueStepsize = TTValue(1);
			mRangeBounds.set(0, TTUInt16(0));
			mRangeBounds.set(1, TTUInt16(1));
		}
		else if (mType == kTTSym_decimal) {
			valueAttribute->type = kTypeFloat64;
			valueDefaultAttribute->type = kTypeFloat64;
			valueStepSizeAttribute->type = kTypeFloat64;
			mValue = TTValue(0.);
			mValueStepsize = TTValue(0.1);
			mRangeBounds.set(0, 0.);
			mRangeBounds.set(1, 1.);
		}
		else if (mType == kTTSym_string) {
			valueAttribute->type = kTypeSymbol;
			valueDefaultAttribute->type = kTypeSymbol;
			valueStepSizeAttribute->type = kTypeSymbol;
			mValue = TTValue(kTTSymEmpty);
			mValueStepsize = kTTValNONE;
			mRangeBounds = kTTValNONE;
		}
		else if (mType == kTTSym_boolean) {
			valueAttribute->type = kTypeBoolean;
			valueDefaultAttribute->type = kTypeBoolean;
			valueStepSizeAttribute->type = kTypeBoolean;
			mValue = TTValue(NO);
			mValueStepsize = TTValue(YES);
			mRangeBounds.set(0, NO);
			mRangeBounds.set(1, YES);
		}
		else if (mType == kTTSym_array) {				// Is this case means something now we have TTValue?
			valueAttribute->type = kTypeFloat64;
			valueDefaultAttribute->type = kTypeFloat64;
			valueStepSizeAttribute->type = kTypeFloat64;
			mValue = TTValue(0.);
			mValueStepsize = TTValue(0.1);
			mRangeBounds.set(0, 0.);
			mRangeBounds.set(1, 1.);
		}
		else if (mType == kTTSym_none) {
			valueAttribute->type = kTypeNone;
			valueDefaultAttribute->type = kTypeNone;
			valueStepSizeAttribute->type = kTypeNone;
			mValue = kTTValNONE;
			mValueStepsize = kTTValNONE;
			mRangeBounds = kTTValNONE;
		}
		else {
			valueAttribute->type = kTypeFloat64;
			valueDefaultAttribute->type = kTypeFloat64;
			valueStepSizeAttribute->type = kTypeFloat64;
			mType = kTTSym_generic;						// Is this case means something now we have TTValue ?
			mValue = TTValue(0.);
			mValueStepsize = TTValue(0.1);
			mRangeBounds = TTValue(0., 1.);
			return kTTErrGeneric;
		}

#ifdef TTDATA_RAMPLIB
		rampSetup();
#endif
		
		notifyObservers(kTTSym_type, n);
	}
	return kTTErrNone;
}

TTErr TTData::setTag(const TTValue& value)
{
	TTValue n = value;				// use new value to protect the attribute
	mTag = value;
	notifyObservers(kTTSym_tag, n);
	return kTTErrNone;
}

TTErr TTData::setRepetitionsAllow(const TTValue& value)
{
	TTValue n = value;				// use new value to protect the attribute
	mRepetitionsAllow = value;
	notifyObservers(kTTSym_repetitionsAllow, n);
	return kTTErrNone;
}

TTErr TTData::setEnable(const TTValue& value)
{
	TTValue n = value;				// use new value to protect the attribute
	mEnable = value;
	notifyObservers(kTTSym_enable, n);
	return kTTErrNone;
}

TTErr TTData::setRangeBounds(const TTValue& value)
{	
	TTValue n;				// use new value to protect the attribute
	mRangeBounds = value;
	
	if (mType == kTTSym_integer)
		mRangeBounds.truncate();
	
	n = mRangeBounds;
	
	notifyObservers(kTTSym_rangeBounds, n);
	return kTTErrNone;
}

TTErr TTData::setRangeClipmode(const TTValue& value)
{
	TTValue n = value;				// use new value to protect the attribute
	mRangeClipmode = value;
	notifyObservers(kTTSym_rangeClipmode, n);
	return kTTErrNone;
}

TTErr TTData::setInstanceBounds(const TTValue& value)
{
	TTValue n;				// use new value to protect the attribute
	TTInt16 vmin, vmax;
	value.get(0, vmin);
	value.get(1, vmax);
	mInstanceBounds.set(0, vmin);
	mInstanceBounds.set(1, vmax);
	
	return kTTErrNone;
}

#ifdef TTDATA_RAMPLIB
TTErr TTData::setRampDrive(const TTValue& value)
{
	TTValue n = value;				// use new value to protect the attribute
	mRampDrive = value;
	
	rampSetup();
	
	notifyObservers(kTTSym_rampDrive, n);
	return kTTErrNone;
}

TTErr TTData::setRampFunction(const TTValue& value)
{
	TTValue n = value;				// use new value to protect the attribute
	mRampFunction = value;
	
	if (mRamper && mRampFunction != kTTSymEmpty && mRampFunction != TT("linear")) {
		
			// set the function of the ramper
			mRamper->setAttributeValue(kTTSym_function, mRampFunction);
			
			 long		n;
			 TTValue		names;
			 TTSymbolPtr	aName;
			 TTString	nameString;
			 
			// cache the function's attribute names
			mRampDataNames->clear();
			mRamper->getFunctionParameterNames(names);
			n = names.getSize();
			for (int i=0; i<n; i++) {
				
				names.get(i, &aName);
				nameString = aName->getString();
				
				if (aName == kTTSym_bypass || aName == kTTSym_mute || aName == kTTSym_maxNumChannels || aName == kTTSym_sampleRate)
					continue;										// don't publish these datas
				
				if (nameString[0] > 64 && nameString[0] < 91) {		// ignore all params not starting with upper-case
					nameString[0] += 32;							// convert first letter to lower-case for Max
					
					TTValue v = aName;
					mRampDataNames->append(TT(nameString.c_str()), v);
				}
			}
	}
	
	notifyObservers(kTTSym_rampFunction, n);
	return kTTErrNone;
}
#endif

TTErr TTData::setDataspace(const TTValue& value)
{
	TTErr	err;
	TTValue v;
	TTValue n = value;				// use new value to protect the attribute
	mDataspace = value;
	
	TTObjectInstantiate(TT("dataspace"),  &mDataspaceConverter, kTTValNONE);
	mDataspaceConverter->setAttributeValue(TT("dataspace"), mDataspace);
	
	// If there is already a unit defined, then we try to use that
	// Otherwise we use the default (neutral) unit.
	err = kTTErrGeneric;
	if (mDataspaceUnit)
		err = mDataspaceConverter->setAttributeValue(TT("outputUnit"), mDataspaceUnit);

	if (err) {
		mDataspaceConverter->getAttributeValue(TT("outputUnit"), v);
		v.get(0, &mDataspaceUnit);
		mDataspaceConverter->setAttributeValue(TT("outputUnit"), mDataspaceUnit);
	}
	
	notifyObservers(kTTSym_dataspace, n);
	return kTTErrNone;
}

TTErr TTData::setDataspaceUnit(const TTValue& value)
{
	TTValue n = value;				// use new value to protect the attribute
	mDataspaceUnit = value;
	if (mDataspaceConverter)
		mDataspaceConverter->setAttributeValue(TT("outputUnit"), mDataspaceUnit);
	
	notifyObservers(kTTSym_dataspaceUnit, n);
	return kTTErrNone;
}

TTBoolean TTData::checkType(const TTValue& value)
{
	if (mType == kTTSym_generic || mType == kTTSym_array) return true;
	
	switch (value.getType()) 
	{
		case kTypeNone :		return mType == kTTSym_none;
		case kTypeFloat32 :		return mType == kTTSym_integer || mType == kTTSym_decimal || mType == kTTSym_boolean;
		case kTypeFloat64 :		return mType == kTTSym_integer || mType == kTTSym_decimal || mType == kTTSym_boolean;
		case kTypeInt8 :		return mType == kTTSym_integer || mType == kTTSym_decimal || mType == kTTSym_boolean;
		case kTypeUInt8 :		return mType == kTTSym_integer || mType == kTTSym_decimal || mType == kTTSym_boolean;
		case kTypeInt16 :		return mType == kTTSym_integer || mType == kTTSym_decimal || mType == kTTSym_boolean;
		case kTypeUInt16 :		return mType == kTTSym_integer || mType == kTTSym_decimal || mType == kTTSym_boolean;
		case kTypeInt32 :		return mType == kTTSym_integer || mType == kTTSym_decimal || mType == kTTSym_boolean;
		case kTypeUInt32 :		return mType == kTTSym_integer || mType == kTTSym_decimal || mType == kTTSym_boolean;
		case kTypeInt64 :		return mType == kTTSym_integer || mType == kTTSym_decimal || mType == kTTSym_boolean;
		case kTypeUInt64 :		return mType == kTTSym_integer || mType == kTTSym_decimal || mType == kTTSym_boolean;
		case kTypeBoolean :		return mType == kTTSym_boolean;
		case kTypeSymbol :		return mType == kTTSym_string;
		case kTypeObject :		return false;
		case kTypePointer :		return false;
		case kTypeString :		return false;
		case kTypeLocalValue :	return false;
		case kNumTTDataTypes :	return false;
		default :				return false;
	}
}

TTBoolean TTData::clipValue()
{
	//bool	didClipAll = false;
	
	// the code regarding didClipAll is supposed to return true when every member of the list has been clipped to its limit
	// that way ramping can be terminated prematurely if it was trying to ramp to something out of range
	// however, this code as it is doesn't work, and it doesn't buy us much anyway
	// so I'm just commenting it out for the time being [TAP]
	
	if (mRangeClipmode != kTTSym_none) {
		
		if (mType == kTTSym_generic || mType == kTTSym_integer || mType == kTTSym_decimal) {
			
			if (mRangeClipmode == kTTSym_low)
				mValue.cliplow(mRangeBounds.getFloat64(0));
			else if (mRangeClipmode == kTTSym_high)
				mValue.cliphigh(mRangeBounds.getFloat64(1));
			else if (mRangeClipmode == kTTSym_both)
				mValue.clip(mRangeBounds.getFloat64(0), mRangeBounds.getFloat64(1));
			//else if (mRangeClipmode == kTTSym_wrap)
				;//mValue.clipwrap(mRangeBounds.getFloat64(0), mRangeBounds.getFloat64(1));
			//else if (mRangeClipmode == kTTSym_fold)
				;//mValue.clipfold(mRangeBounds.getFloat64(0), mRangeBounds.getFloat64(1));
		}
	}
	
	return false;
}

#ifdef TTDATA_RAMPLIB
TTErr TTData::rampSetup()
{

	// 1. destroy the old rampunit
	if (mRamper != NULL) {
		TTObjectRelease(TTObjectHandle(&mRamper));
		mRamper = NULL;
	}
	
	// 2. create the new rampunit
	// For some types ramping doesn't make sense, so they will be set to none
	if (mType == kTTSym_none || mType == kTTSym_string || mType == kTTSym_generic)
		mRampDrive = kTTSym_none;
	else 
		RampLib::createUnit(mRampDrive, &mRamper, &TTDataRampUnitCallback, (void *)this);
	
	if (mRamper == NULL)
		return kTTErrGeneric; //error("jcom.data (%s module): could not allocate memory for ramp unit!", x->common.module_name);
	
	// 3. reset the ramp function
	setRampFunction(mRampFunction);
	
	return kTTErrNone;	
}
#endif

TTErr TTData::convertUnit(TTValue& value)
{
	if (mDataspaceConverter)
		return mDataspaceConverter->sendMessage(TT("convert"), value);

	return kTTErrNone;
}

TTErr TTData::notifyObservers(TTSymbolPtr attrName, const TTValue& value)
{
	TTAttributePtr	anAttribute = NULL;
	TTErr			err;
	
	err = this->findAttribute(attrName, &anAttribute);
	
	if (!err)
		anAttribute->sendNotification(kTTSym_notify, value);	// we use kTTSym_notify because we know that observers are TTCallback
	
	return kTTErrNone;
}

TTErr TTData::WriteAsText(const TTValue& value)
{
	TTTextHandlerPtr aTextHandler;
	ofstream		*file;
	TTValue			toString;
	TTString		line;
	
	value.get(0, (TTPtr*)&aTextHandler);
	file = aTextHandler->mWriter;
	
	// Type
	*file << "\t\t\t<td class =\"instructionType\">" << this->mType->getCString() <<  "</td>";

	// range/bounds
	toString = this->mRangeBounds;
	toString.toString();
	toString.get(0, line);
	
	if ( (this->mType == kTTSym_integer) || (this->mType == kTTSym_boolean) || (this->mType == kTTSym_decimal) || (this->mType == kTTSym_generic) )
		*file << "\t\t\t<td class =\"instructionRangeBounds\">" << line.data() << "</td>";
	else
		*file << "\t\t\t<td class = \"instructionRangeBounds\"> N/A </td>";

	// range/clipmode
	*file << "\t\t\t<td class =\"instructionRangeClipmode\">" << this->mRangeClipmode->getCString() << "</td>";

#ifdef TTDATA_RAMPLIB
	// ramp/drive
	*file << "\t\t\t<td class =\"instructionRampDrive\">" << this->mRampDrive->getCString() << "</td>";
	
	// ramp/function
	*file << "\t\t\t<td class =\"instructionRampFunction\">" << this->mRampFunction->getCString() << "</td>";
#endif
	
	// dataspace
	*file << "\t\t\t<td class =\"instructionDataspace\">" << this->mDataspace->getCString() << "</td>";
	
	// dataspace/unit/native
	*file << "\t\t\t<td class =\"instructionDataspaceUnit\">" << this->mDataspaceUnit->getCString() << "</td>";
	
	// repetitions/allow
	toString = this->mRepetitionsAllow;
	toString.toString();
	toString.get(0, line);
	*file << "\t\t\t<td class =\"instructionRepetitionsAllow\">" << line.data() << "</td>";
	
	// description
	*file << "\t\t\t<td class =\"instructionDescription\">" << this->mDescription->getCString() << "</td>";
	return kTTErrNone;
}

#if 0
#pragma mark -
#pragma mark Some Methods
#endif

#ifdef TTDATA_RAMPLIB
void TTDataRampUnitCallback(void *o, TTUInt32 n, TTFloat64 *rampedArray)
{
	TTDataPtr	aData = (TTDataPtr)o;
	TTValue		rampedValue;
	TTUInt16	i;
	
	rampedValue.setSize(n);
	for (i=0; i<n; i++)
		rampedValue.set(i, rampedArray[i]);
	
	if (aData->mType == kTTSym_integer)
		rampedValue.truncate();
	
	if (!aData->mRepetitionsAllow)
		if (aData->mValue == rampedValue)
			return;
		
	// set internal value
	aData->setValue(rampedValue);
}
#endif
<commit_msg>adding the enable attribute to parameters (and messages) as well as with returns - this is a workaround to allow parameter exclusion in cues...<commit_after>/* 
 * A Data Object
 * Copyright © 2010, <NAME>
 * 
 * License: This code is licensed under the terms of the "New BSD License"
 * http://creativecommons.org/licenses/BSD/
 */

#include "TTData.h"

#define thisTTClass			TTData
#define thisTTClassName		"Data"
#define thisTTClassTags		"data"

TT_MODULAR_CONSTRUCTOR,
mValue(TTValue(0.0)),
mValueDefault(kTTValNONE),
mValueStepsize(TTValue(0.1)),
mType(kTTSym_generic),
mTag(kTTValNONE),
mPriority(0),
mDescription(kTTSymEmpty),
mRepetitionsAllow(YES),
mEnable(YES),
mInitialized(NO),
mRangeBounds(0.0, 1.0),
mRangeClipmode(kTTSym_none),
mDynamicInstances(NO),
mInstanceBounds(0, -1),
#ifdef TTDATA_RAMPLIB
mRampDrive(kTTSym_none),
mRampFunction(kTTSymEmpty),
#endif
mDataspace(kTTSym_none),
mDataspaceUnit(kTTSym_none),
mDataspaceConverter(NULL),
mService(kTTSymEmpty),
mReturnValueCallback(NULL)
{
	TT_ASSERT("Correct number of args to create TTData", arguments.getSize() == 1);
	
	arguments.get(0, (TTPtr*)&mReturnValueCallback);
	TT_ASSERT("Return Value Callback passed to TTData is not NULL", mReturnValueCallback);
	
	if (arguments.getSize() == 2)
		arguments.get(1, &mService);
	
	addAttributeWithGetterAndSetter(Value, kTypeNone);
	addAttributeWithGetterAndSetter(ValueDefault, kTypeNone);
	addAttributeWithGetterAndSetter(ValueStepsize, kTypeNone);
	
	addAttributeWithSetter(Type, kTypeSymbol);
	addAttributeWithSetter(Tag, kTypeLocalValue);
	addAttribute(Priority, kTypeInt32);
	addAttribute(Description, kTypeSymbol);
	addAttributeWithSetter(RepetitionsAllow, kTypeBoolean);
	
	addAttributeWithSetter(Enable, kTypeBoolean);
	
	addAttribute(Initialized, kTypeBoolean);
	addAttributeProperty(initialized, readOnly, YES);
	addAttributeProperty(initialized, hidden, YES);
	
	addAttributeWithSetter(RangeBounds, kTypeLocalValue);
	addAttributeWithSetter(RangeClipmode, kTypeSymbol);
	
	addAttribute(DynamicInstances, kTypeBoolean);
	addAttributeProperty(dynamicInstances, hidden, YES);
	addAttributeWithSetter(InstanceBounds, kTypeLocalValue);
	addAttributeProperty(instanceBounds, hidden, YES);
	
#ifdef TTDATA_RAMPLIB
	addAttributeWithSetter(RampDrive, kTypeSymbol);
	addAttributeWithSetter(RampFunction, kTypeSymbol);
#endif
	
	addAttributeWithSetter(Dataspace, kTypeSymbol);
	addAttributeWithSetter(DataspaceUnit, kTypeSymbol);
	
	addAttribute(Service, kTypeSymbol);
	addAttributeProperty(service, readOnly, YES);
	
	addMessage(Reset);
	addMessageWithArgument(Inc);
	addMessageWithArgument(Dec);
	addMessageWithArgument(Command);
	addMessageProperty(Command, hidden, YES);
	
	// needed to be handled by a TTTextHandler
	addMessageWithArgument(WriteAsText);
	addMessageProperty(WriteAsText, hidden, YES);
	
	mIsSending = NO;
	
#ifdef TTDATA_RAMPLIB
	mRamper = NULL;
	mRampDataNames = new TTHash();
#endif
}

TTData::~TTData()
{
#ifdef TTDATA_RAMPLIB	
	if (mRamper)
		TTObjectRelease(TTObjectHandle(&mRamper));
#endif
	
	if (mDataspaceConverter)
		TTObjectRelease(TTObjectHandle(&mDataspaceConverter));
	
	if (mReturnValueCallback) {
		delete (TTValuePtr)mReturnValueCallback->getBaton();
		TTObjectRelease(TTObjectHandle(&mReturnValueCallback));
	}
}

TTErr TTData::Reset()
{
	// if valueDefault type is right
	if (checkType(mValueDefault))
		if (!(mValueDefault == kTTValNONE))
			setValue(mValueDefault);
	
	// Set data to be uninitialised
	// to circumvent filtering of repetitions when outputing value from default preset
	mInitialized = NO;
	notifyObservers(kTTSym_initialized, NO);
	
	return kTTErrNone;
}

TTErr TTData::Inc(const TTValue& value)
{
	TTUInt32	i;
	TTFloat64	inc, ramptime, v, vStepsize;
	TTSymbolPtr	ramp;
	TTValue		command;
	
	mValueStepsize.get(0, vStepsize);
	
	switch (value.getSize()) {
			
			// 1 incrementation step	
		case 1 :
		{
			if (value.getType(0) == kTypeFloat32 || value.getType(0)  == kTypeInt32) {
				value.get(0, inc);
				
				for (i=0; i<mValue.getSize(); i++) {
					mValue.get(i, v);
					command.append(v + inc * vStepsize);
				}
			}
			break;
		}
			
			// 1 incrementation step + ramp ramptime
		case 3 :
		{
			if (value.getType(0) == kTypeFloat32 || value.getType(0)  == kTypeInt32) {
				value.get(0, inc);
				
				for (i=0; i<mValue.getSize(); i++) {
					mValue.get(i, v);
					command.append(v + inc * vStepsize);
				}
				
				if (value.getType(1) == kTypeSymbol) {
					value.get(1, &ramp);
					if (ramp == kTTSym_ramp) {
						command.append(ramp);
						if (value.getType(2) == kTypeFloat32 || value.getType(2)  == kTypeInt32) {
							value.get(2, ramptime);
							command.append(ramptime);
						}
					}
				}
				break;	
			}
		}
			
			// no value or wrong value
		default :
		{
			for (i=0; i<mValue.getSize(); i++) {
				mValue.get(i, v);
				command.append(v + vStepsize);
			}
			
			break;	
		}
	}
	
	this->Command(command);
	
	return kTTErrNone;
}

TTErr TTData::Dec(const TTValue& value)
{
	TTUInt32	i;
	TTFloat64	dec, ramptime, v, vStepsize;
	TTSymbolPtr	ramp;
	TTValue		command;
	
	mValueStepsize.get(0, vStepsize);
	
	switch (value.getSize()) {
			
			// 1 decrementation step	
		case 1 :
		{
			if (value.getType(0) == kTypeFloat32 || value.getType(0)  == kTypeInt32) {
				value.get(0, dec);
				
				for (i=0; i<mValue.getSize(); i++) {
					mValue.get(i, v);
					command.append(v - dec * vStepsize);
				}
			}
			break;
		}
			
			// 1 decrementation step + ramp ramptime
		case 3 :
		{
			if (value.getType(0) == kTypeFloat32 || value.getType(0)  == kTypeInt32) {
				value.get(0, dec);
				
				for (i=0; i<mValue.getSize(); i++) {
					mValue.get(i, v);
					command.append(v - dec * vStepsize);
				}
				
				if (value.getType(1) == kTypeSymbol) {
					value.get(1, &ramp);
					if (ramp == kTTSym_ramp) {
						command.append(ramp);
						if (value.getType(2) == kTypeFloat32 || value.getType(2)  == kTypeInt32) {
							value.get(2, ramptime);
							command.append(ramptime);
						}
					}
				}
				break;	
			}
		}
			
			// no value or wrong value
		default :
		{
			for (i=0; i<mValue.getSize(); i++) {
				mValue.get(i, v);
				command.append(v - vStepsize);
			}
			
			break;	
		}
	}
	
	this->Command(command);
	
	return kTTErrNone;
}

TTErr TTData::Command(const TTValue& command)
{
	TTMessagePtr aMessage;
	TTErr		err = kTTErrNone;
#ifdef TTDATA_RAMPLIB
	double		time;
#endif
	int			commandSize;
	TTSymbolPtr	unit, ramp;
	TTValue		aValue, c;
	bool		hasRamp = false;
	bool		hasUnit = false;
	
	// 1. Notify Command observer for value changes only
	///////////////////////////////////////////////////
	c = command;											// protect the command value
	err = this->findMessage(kTTSym_Command, &aMessage);
	if (!err)
		aMessage->sendNotification(kTTSym_notify, c);	// we use kTTSym_notify because we know that observers are TTCallback
	
	// 2. Parse the command to handle unit and ramp
	///////////////////////////////////////////////////
	commandSize = command.getSize();
	switch(commandSize) {
			
		// no value	
		case 0 :
		{
			// nothing to do
			break;	
		}
			
		// 1 value	
		case 1 :
		{
			// nothing to do
			break;	
		}
			
		// 2 values || 1 value + unit
		case 2 :
		{
			// Is the second element is a unit symbol ?
			if (mDataspaceUnit != kTTSym_none)
				if (command.getType(0) != kTypeSymbol && command.getType(1) == kTypeSymbol) {
					hasUnit = true;
					command.get(1, &unit);
				}
			
			break;	
		}
			
		// 3 values || 2 values + unit || 1 value + ramp ramptime
		case 3 :
		{
			// Is the second element is a ramp symbol ?
			if (command.getType(1) == kTypeSymbol) {
				command.get(1, &ramp);
				if (ramp == kTTSym_ramp)
					hasRamp = true;
			}
			// or is the last element is a unit symbol ?
			else if (mDataspace != kTTSym_none && command.getType(0) != kTypeSymbol && command.getType(2) == kTypeSymbol) {
				hasUnit = true;
				command.get(2, &unit);
			}
			
			break;	
		}
			
		// X values || X-1 values + unit || X-2 values + ramp ramptime || X-3 values + unit + ramp ramptime
		default :
		{
			// Is the X-2 element is a ramp symbol ?
			if (command.getType(commandSize - 2) == kTypeSymbol) {
				command.get(commandSize - 2, &ramp);
				if (ramp == kTTSym_ramp)
					hasRamp = true;
			}
			
			// Is the X-3 or last element a unit symbol ?
			if (mDataspace != kTTSym_none && command.getType(0) != kTypeSymbol)
				if (hasRamp)
					if (command.getType(commandSize - 3) == kTypeSymbol) {
						hasUnit = true;
						command.get(commandSize - 3, &unit);
					}
					else
						if (command.getType(commandSize - 1) == kTypeSymbol) {
							hasUnit = true;
							command.get(commandSize - 1, &unit);
						}
			
			break;	
		}
			
	}

	
	// 3. Strip ramp or unit informations if needed
	if (hasRamp && hasUnit) {
		aValue = command;
		aValue.setSize(commandSize - 3);
	}
	else if (hasRamp) {
		aValue = command;
		aValue.setSize(commandSize - 2);
	}
	else if (hasUnit) {
		aValue = command;
		aValue.setSize(commandSize - 1);
	}
	else
		aValue = command;
	
	
	// 4. Set Dataspace input unit and convert the value
	// Note : The current implementation does not override the active unit temporarily or anything fancy.
	// It just sets the input unit and then runs with it...
	// For this initial implementation we are converting the values prior to ramping, as it is easier.
	// Ultimately though, we actually want to convert the units after the ramping, 
	// for example to perform a sweep that is linear vs logarithmic
	////////////////////////////////////////////////////////////////
	if (hasUnit)
		if (mDataspaceConverter) {
			mDataspaceConverter->setAttributeValue(TT("inputUnit"), unit);
			convertUnit(aValue);
		}
	
	
	// 5. Ramp the convertedValue
	/////////////////////////////////
#ifdef TTDATA_RAMPLIB
	if (hasRamp) {
		
		command.get(commandSize - 1, time);

		if (time <= 0) {
			setValue(aValue);
			return kTTErrNone;
		}	
		
		if (!mRepetitionsAllow && mInitialized) {
			
			// float to integer case
			if (mType == kTTSym_integer)
				aValue.truncate();
			
			// integer/float to boolean case
			if (mType == kTTSym_boolean)
				aValue.booleanize();
			
			if (mValue == aValue)
				return kTTErrNone;	// nothing to do
		}
		
		if (mRamper) {
			TTUInt16	i, s = aValue.getSize();
			TTFloat64*	startArray = new TTFloat64[s];		// start to mValue
			TTFloat64*	targetArray = new TTFloat64[s];		// go to convertedValue
			
			if(mValue.getSize() != s)
				mValue.setSize(s);
			
			for (i=0; i<s; i++) {
				startArray[i] = mValue.getFloat64(i);
				targetArray[i] = aValue.getFloat64(i);
			}
			
			mRamper->set(s, startArray);		
			mRamper->go(s, targetArray, time);	
			
			delete [] startArray;
			delete [] targetArray;
		}
	} 
	else {
#endif
		// check repetitions
		if (!mRepetitionsAllow && mInitialized) {
			
			// float to integer case
			if (mType == kTTSym_integer)
				aValue.truncate();
			
			// integer/float to boolean case
			if (mType == kTTSym_boolean)
				aValue.booleanize();
			
			if (mValue == aValue)
				return kTTErrNone;	// nothing to do
		}

#ifdef TTDATA_RAMPLIB
		// stop ramping before to set a value
		if (mRamper)
			mRamper->stop();
#endif
		
		setValue(aValue);
		
#ifdef TTDATA_RAMPLIB
	}
#endif
	
	return kTTErrNone;
}

TTErr TTData::getValue(TTValue& value)
{
	// can't get the value before the data has been initialized
	if (mInitialized) {
		
		value = mValue;
		return kTTErrNone;
	}
	else
		return kTTErrGeneric;
}

TTErr TTData::setValue(const TTValue& value)
{
	TTValue r, n;
	
	if (!mIsSending && mEnable) {
		
		// lock
		mIsSending = YES;
		
		// a kTTValNONE would only return the actual value
		if (value == kTTValNONE) {
			
			// if mType is 'none' we have had our value set at least once
			if (mService == kTTSym_parameter && mType == kTTSym_none && !mInitialized) {
				mInitialized = YES;
				notifyObservers(kTTSym_initialized, YES);
			}
		}
		// otherwise check the type of the incoming value
		else if (checkType(value)) {
			
			// set internal value 
			mValue = value;
			
			// float to integer case
			if (mType == kTTSym_integer)
				mValue.truncate();
			
			// integer/float to boolean case
			if (mType == kTTSym_boolean)
				mValue.booleanize();
			
#ifdef TTDATA_RAMPLIB
			if (clipValue() && mRamper)
				mRamper->stop();
#else
			clipValue();
#endif
		}
		else {
			// unlock
			mIsSending = NO;
			return kTTErrInvalidValue;
		}
		
		// used new values to protect the attribute
		r = mValue;
		n = mValue;
		
		// return the value to his owner
		if (!(mService == kTTSym_return))
			this->mReturnValueCallback->notify(r);
		
		// notify each observers
		//if (!(mService == kTTSym_message))		// to -- to allow message to be mapped for example
			notifyObservers(kTTSym_value, n);
		
		// we have had our value set at least once
		// only parameters notify their initialisation
		if (mService == kTTSym_parameter && !mInitialized) {
			mInitialized = YES;
			notifyObservers(kTTSym_initialized, YES);
		}
		else if (!mInitialized) mInitialized = YES;
		
		// unlock
		mIsSending = NO;
		
		return kTTErrNone;
	}
	
	return kTTErrGeneric;
}

TTErr TTData::getValueDefault(TTValue& value)
{
	value = mValueDefault;
	return kTTErrNone;
}

TTErr TTData::setValueDefault(const TTValue& value)
{
	TTValue n = value;				// use new value to protect the attribute
	mValueDefault = value;
	notifyObservers(kTTSym_valueDefault, n);
	return kTTErrNone;
}

TTErr TTData::getValueStepsize(TTValue& value)
{
	value = mValueStepsize;
	return kTTErrNone;
}

TTErr TTData::setValueStepsize(const TTValue& value)
{
	TTValue n = value;				// use new value to protect the attribute
	mValueStepsize = value;
	notifyObservers(kTTSym_valueStepsize, n);
	return kTTErrNone;
}

TTErr TTData::setType(const TTValue& value)
{
	TTAttributePtr valueAttribute, valueDefaultAttribute, valueStepSizeAttribute;
	// if the new type is different
	if (!(TTValue(mType) == value)) {
		
		TTValue n = value;				// use new value to protect the attribute
		mType = value;
		
		// Get Value, ValueDefault and ValueStepsize attributes
		this->findAttribute(kTTSym_value, &valueAttribute);
		this->findAttribute(kTTSym_value, &valueDefaultAttribute);
		this->findAttribute(kTTSym_valueStepsize, &valueStepSizeAttribute);

		mInstanceBounds.set(0, TTInt16(0));
		mInstanceBounds.set(1, TTInt16(-1));
		
		// register mValue Attribute and prepare memory
		if (mType == kTTSym_integer) {
			valueAttribute->type = kTypeInt32;
			valueDefaultAttribute->type = kTypeInt32;
			valueStepSizeAttribute->type = kTypeInt32;
			mValue = TTValue(0);
			mValueStepsize = TTValue(1);
			mRangeBounds.set(0, TTUInt16(0));
			mRangeBounds.set(1, TTUInt16(1));
		}
		else if (mType == kTTSym_decimal) {
			valueAttribute->type = kTypeFloat64;
			valueDefaultAttribute->type = kTypeFloat64;
			valueStepSizeAttribute->type = kTypeFloat64;
			mValue = TTValue(0.);
			mValueStepsize = TTValue(0.1);
			mRangeBounds.set(0, 0.);
			mRangeBounds.set(1, 1.);
		}
		else if (mType == kTTSym_string) {
			valueAttribute->type = kTypeSymbol;
			valueDefaultAttribute->type = kTypeSymbol;
			valueStepSizeAttribute->type = kTypeSymbol;
			mValue = TTValue(kTTSymEmpty);
			mValueStepsize = kTTValNONE;
			mRangeBounds = kTTValNONE;
		}
		else if (mType == kTTSym_boolean) {
			valueAttribute->type = kTypeBoolean;
			valueDefaultAttribute->type = kTypeBoolean;
			valueStepSizeAttribute->type = kTypeBoolean;
			mValue = TTValue(NO);
			mValueStepsize = TTValue(YES);
			mRangeBounds.set(0, NO);
			mRangeBounds.set(1, YES);
		}
		else if (mType == kTTSym_array) {				// Is this case means something now we have TTValue?
			valueAttribute->type = kTypeFloat64;
			valueDefaultAttribute->type = kTypeFloat64;
			valueStepSizeAttribute->type = kTypeFloat64;
			mValue = TTValue(0.);
			mValueStepsize = TTValue(0.1);
			mRangeBounds.set(0, 0.);
			mRangeBounds.set(1, 1.);
		}
		else if (mType == kTTSym_none) {
			valueAttribute->type = kTypeNone;
			valueDefaultAttribute->type = kTypeNone;
			valueStepSizeAttribute->type = kTypeNone;
			mValue = kTTValNONE;
			mValueStepsize = kTTValNONE;
			mRangeBounds = kTTValNONE;
		}
		else {
			valueAttribute->type = kTypeFloat64;
			valueDefaultAttribute->type = kTypeFloat64;
			valueStepSizeAttribute->type = kTypeFloat64;
			mType = kTTSym_generic;						// Is this case means something now we have TTValue ?
			mValue = TTValue(0.);
			mValueStepsize = TTValue(0.1);
			mRangeBounds = TTValue(0., 1.);
			return kTTErrGeneric;
		}

#ifdef TTDATA_RAMPLIB
		rampSetup();
#endif
		
		notifyObservers(kTTSym_type, n);
	}
	return kTTErrNone;
}

TTErr TTData::setTag(const TTValue& value)
{
	TTValue n = value;				// use new value to protect the attribute
	mTag = value;
	notifyObservers(kTTSym_tag, n);
	return kTTErrNone;
}

TTErr TTData::setRepetitionsAllow(const TTValue& value)
{
	TTValue n = value;				// use new value to protect the attribute
	mRepetitionsAllow = value;
	notifyObservers(kTTSym_repetitionsAllow, n);
	return kTTErrNone;
}

TTErr TTData::setEnable(const TTValue& value)
{
	TTValue n = value;				// use new value to protect the attribute
	mEnable = value;
	notifyObservers(kTTSym_enable, n);
	return kTTErrNone;
}

TTErr TTData::setRangeBounds(const TTValue& value)
{	
	TTValue n;				// use new value to protect the attribute
	mRangeBounds = value;
	
	if (mType == kTTSym_integer)
		mRangeBounds.truncate();
	
	n = mRangeBounds;
	
	notifyObservers(kTTSym_rangeBounds, n);
	return kTTErrNone;
}

TTErr TTData::setRangeClipmode(const TTValue& value)
{
	TTValue n = value;				// use new value to protect the attribute
	mRangeClipmode = value;
	notifyObservers(kTTSym_rangeClipmode, n);
	return kTTErrNone;
}

TTErr TTData::setInstanceBounds(const TTValue& value)
{
	TTValue n;				// use new value to protect the attribute
	TTInt16 vmin, vmax;
	value.get(0, vmin);
	value.get(1, vmax);
	mInstanceBounds.set(0, vmin);
	mInstanceBounds.set(1, vmax);
	
	return kTTErrNone;
}

#ifdef TTDATA_RAMPLIB
TTErr TTData::setRampDrive(const TTValue& value)
{
	TTValue n = value;				// use new value to protect the attribute
	mRampDrive = value;
	
	rampSetup();
	
	notifyObservers(kTTSym_rampDrive, n);
	return kTTErrNone;
}

TTErr TTData::setRampFunction(const TTValue& value)
{
	TTValue n = value;				// use new value to protect the attribute
	mRampFunction = value;
	
	if (mRamper && mRampFunction != kTTSymEmpty && mRampFunction != TT("linear")) {
		
			// set the function of the ramper
			mRamper->setAttributeValue(kTTSym_function, mRampFunction);
			
			 long		n;
			 TTValue		names;
			 TTSymbolPtr	aName;
			 TTString	nameString;
			 
			// cache the function's attribute names
			mRampDataNames->clear();
			mRamper->getFunctionParameterNames(names);
			n = names.getSize();
			for (int i=0; i<n; i++) {
				
				names.get(i, &aName);
				nameString = aName->getString();
				
				if (aName == kTTSym_bypass || aName == kTTSym_mute || aName == kTTSym_maxNumChannels || aName == kTTSym_sampleRate)
					continue;										// don't publish these datas
				
				if (nameString[0] > 64 && nameString[0] < 91) {		// ignore all params not starting with upper-case
					nameString[0] += 32;							// convert first letter to lower-case for Max
					
					TTValue v = aName;
					mRampDataNames->append(TT(nameString.c_str()), v);
				}
			}
	}
	
	notifyObservers(kTTSym_rampFunction, n);
	return kTTErrNone;
}
#endif

TTErr TTData::setDataspace(const TTValue& value)
{
	TTErr	err;
	TTValue v;
	TTValue n = value;				// use new value to protect the attribute
	mDataspace = value;
	
	TTObjectInstantiate(TT("dataspace"),  &mDataspaceConverter, kTTValNONE);
	mDataspaceConverter->setAttributeValue(TT("dataspace"), mDataspace);
	
	// If there is already a unit defined, then we try to use that
	// Otherwise we use the default (neutral) unit.
	err = kTTErrGeneric;
	if (mDataspaceUnit)
		err = mDataspaceConverter->setAttributeValue(TT("outputUnit"), mDataspaceUnit);

	if (err) {
		mDataspaceConverter->getAttributeValue(TT("outputUnit"), v);
		v.get(0, &mDataspaceUnit);
		mDataspaceConverter->setAttributeValue(TT("outputUnit"), mDataspaceUnit);
	}
	
	notifyObservers(kTTSym_dataspace, n);
	return kTTErrNone;
}

TTErr TTData::setDataspaceUnit(const TTValue& value)
{
	TTValue n = value;				// use new value to protect the attribute
	mDataspaceUnit = value;
	if (mDataspaceConverter)
		mDataspaceConverter->setAttributeValue(TT("outputUnit"), mDataspaceUnit);
	
	notifyObservers(kTTSym_dataspaceUnit, n);
	return kTTErrNone;
}

TTBoolean TTData::checkType(const TTValue& value)
{
	if (mType == kTTSym_generic || mType == kTTSym_array) return true;
	
	switch (value.getType()) 
	{
		case kTypeNone :		return mType == kTTSym_none;
		case kTypeFloat32 :		return mType == kTTSym_integer || mType == kTTSym_decimal || mType == kTTSym_boolean;
		case kTypeFloat64 :		return mType == kTTSym_integer || mType == kTTSym_decimal || mType == kTTSym_boolean;
		case kTypeInt8 :		return mType == kTTSym_integer || mType == kTTSym_decimal || mType == kTTSym_boolean;
		case kTypeUInt8 :		return mType == kTTSym_integer || mType == kTTSym_decimal || mType == kTTSym_boolean;
		case kTypeInt16 :		return mType == kTTSym_integer || mType == kTTSym_decimal || mType == kTTSym_boolean;
		case kTypeUInt16 :		return mType == kTTSym_integer || mType == kTTSym_decimal || mType == kTTSym_boolean;
		case kTypeInt32 :		return mType == kTTSym_integer || mType == kTTSym_decimal || mType == kTTSym_boolean;
		case kTypeUInt32 :		return mType == kTTSym_integer || mType == kTTSym_decimal || mType == kTTSym_boolean;
		case kTypeInt64 :		return mType == kTTSym_integer || mType == kTTSym_decimal || mType == kTTSym_boolean;
		case kTypeUInt64 :		return mType == kTTSym_integer || mType == kTTSym_decimal || mType == kTTSym_boolean;
		case kTypeBoolean :		return mType == kTTSym_boolean;
		case kTypeSymbol :		return mType == kTTSym_string;
		case kTypeObject :		return false;
		case kTypePointer :		return false;
		case kTypeString :		return false;
		case kTypeLocalValue :	return false;
		case kNumTTDataTypes :	return false;
		default :				return false;
	}
}

TTBoolean TTData::clipValue()
{
	//bool	didClipAll = false;
	
	// the code regarding didClipAll is supposed to return true when every member of the list has been clipped to its limit
	// that way ramping can be terminated prematurely if it was trying to ramp to something out of range
	// however, this code as it is doesn't work, and it doesn't buy us much anyway
	// so I'm just commenting it out for the time being [TAP]
	
	if (mRangeClipmode != kTTSym_none) {
		
		if (mType == kTTSym_generic || mType == kTTSym_integer || mType == kTTSym_decimal) {
			
			if (mRangeClipmode == kTTSym_low)
				mValue.cliplow(mRangeBounds.getFloat64(0));
			else if (mRangeClipmode == kTTSym_high)
				mValue.cliphigh(mRangeBounds.getFloat64(1));
			else if (mRangeClipmode == kTTSym_both)
				mValue.clip(mRangeBounds.getFloat64(0), mRangeBounds.getFloat64(1));
			//else if (mRangeClipmode == kTTSym_wrap)
				;//mValue.clipwrap(mRangeBounds.getFloat64(0), mRangeBounds.getFloat64(1));
			//else if (mRangeClipmode == kTTSym_fold)
				;//mValue.clipfold(mRangeBounds.getFloat64(0), mRangeBounds.getFloat64(1));
		}
	}
	
	return false;
}

#ifdef TTDATA_RAMPLIB
TTErr TTData::rampSetup()
{

	// 1. destroy the old rampunit
	if (mRamper != NULL) {
		TTObjectRelease(TTObjectHandle(&mRamper));
		mRamper = NULL;
	}
	
	// 2. create the new rampunit
	// For some types ramping doesn't make sense, so they will be set to none
	if (mType == kTTSym_none || mType == kTTSym_string || mType == kTTSym_generic)
		mRampDrive = kTTSym_none;
	else 
		RampLib::createUnit(mRampDrive, &mRamper, &TTDataRampUnitCallback, (void *)this);
	
	if (mRamper == NULL)
		return kTTErrGeneric; //error("jcom.data (%s module): could not allocate memory for ramp unit!", x->common.module_name);
	
	// 3. reset the ramp function
	setRampFunction(mRampFunction);
	
	return kTTErrNone;	
}
#endif

TTErr TTData::convertUnit(TTValue& value)
{
	if (mDataspaceConverter)
		return mDataspaceConverter->sendMessage(TT("convert"), value);

	return kTTErrNone;
}

TTErr TTData::notifyObservers(TTSymbolPtr attrName, const TTValue& value)
{
	TTAttributePtr	anAttribute = NULL;
	TTErr			err;
	
	err = this->findAttribute(attrName, &anAttribute);
	
	if (!err)
		anAttribute->sendNotification(kTTSym_notify, value);	// we use kTTSym_notify because we know that observers are TTCallback
	
	return kTTErrNone;
}

TTErr TTData::WriteAsText(const TTValue& value)
{
	TTTextHandlerPtr aTextHandler;
	ofstream		*file;
	TTValue			toString;
	TTString		line;
	
	value.get(0, (TTPtr*)&aTextHandler);
	file = aTextHandler->mWriter;
	
	// Type
	*file << "\t\t\t<td class =\"instructionType\">" << this->mType->getCString() <<  "</td>";

	// range/bounds
	toString = this->mRangeBounds;
	toString.toString();
	toString.get(0, line);
	
	if ( (this->mType == kTTSym_integer) || (this->mType == kTTSym_boolean) || (this->mType == kTTSym_decimal) || (this->mType == kTTSym_generic) )
		*file << "\t\t\t<td class =\"instructionRangeBounds\">" << line.data() << "</td>";
	else
		*file << "\t\t\t<td class = \"instructionRangeBounds\"> N/A </td>";

	// range/clipmode
	*file << "\t\t\t<td class =\"instructionRangeClipmode\">" << this->mRangeClipmode->getCString() << "</td>";

#ifdef TTDATA_RAMPLIB
	// ramp/drive
	*file << "\t\t\t<td class =\"instructionRampDrive\">" << this->mRampDrive->getCString() << "</td>";
	
	// ramp/function
	*file << "\t\t\t<td class =\"instructionRampFunction\">" << this->mRampFunction->getCString() << "</td>";
#endif
	
	// dataspace
	*file << "\t\t\t<td class =\"instructionDataspace\">" << this->mDataspace->getCString() << "</td>";
	
	// dataspace/unit/native
	*file << "\t\t\t<td class =\"instructionDataspaceUnit\">" << this->mDataspaceUnit->getCString() << "</td>";
	
	// repetitions/allow
	toString = this->mRepetitionsAllow;
	toString.toString();
	toString.get(0, line);
	*file << "\t\t\t<td class =\"instructionRepetitionsAllow\">" << line.data() << "</td>";
	
	// description
	*file << "\t\t\t<td class =\"instructionDescription\">" << this->mDescription->getCString() << "</td>";
	return kTTErrNone;
}

#if 0
#pragma mark -
#pragma mark Some Methods
#endif

#ifdef TTDATA_RAMPLIB
void TTDataRampUnitCallback(void *o, TTUInt32 n, TTFloat64 *rampedArray)
{
	TTDataPtr	aData = (TTDataPtr)o;
	TTValue		rampedValue;
	TTUInt16	i;
	
	rampedValue.setSize(n);
	for (i=0; i<n; i++)
		rampedValue.set(i, rampedArray[i]);
	
	if (aData->mType == kTTSym_integer)
		rampedValue.truncate();
	
	if (!aData->mRepetitionsAllow)
		if (aData->mValue == rampedValue)
			return;
		
	// set internal value
	aData->setValue(rampedValue);
}
#endif

{'fraction_non_alphanumeric': 0.13011124473842453, 'fraction_numerical': 0.010598316295850872, 'mean_word_length': 3.598375529249114, 'pattern_counts': {'":': 0, '<': 179, '<?xml version=': 0, '>': 197, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5423447', 'n_tokens_mistral': 21863, 'n_tokens_neox': 18691, 'n_words': 4439}