// DSP Method
void gain_dsp(t_gain *x, t_signal **sp, short *count)
{
	short		i, j, k, l=0;
	void**		audioVectors = NULL;
	TTUInt16	numChannels = 0;
	TTUInt16	vs = 0;
	
	audioVectors = (void**)sysmem_newptr(sizeof(void*) * ((x->numChannels * 3) + 1));
	audioVectors[l] = x;
	l++;
	
	// audioVectors[] passed to gain_perform() as:
	//	{x, audioInL[0], audioInR[0], audioOut[0], audioInL[1], audioInR[1], audioOut[1],...}
	for(i=0; i < x->numChannels; i++){
		j = x->numChannels + i;
		k = x->numChannels*2 + i;
		if(count[i] && count[j] && count[k]){
			numChannels++;
			if(sp[i]->s_n > vs)
				vs = sp[i]->s_n;
			
			audioVectors[l] = sp[i]->s_vec;
			l++;
			audioVectors[l] = sp[j]->s_vec;
			l++;
			audioVectors[l] = sp[k]->s_vec;
			l++;
		}
	}
	
	x->signalIn->setNumChannels(TTUInt16(numChannels*2));
	x->signalOut->setNumChannels(numChannels);
	x->signalTemp->setNumChannels(numChannels);
	
	x->signalIn->setVectorSizeWithInt(vs);
	x->signalOut->setVectorSizeWithInt(vs);
	x->signalTemp->setVectorSizeWithInt(vs);

	//signalIn will be set in the perform method
	x->signalOut->alloc();
	x->signalTemp->alloc();
	
	x->xfade->setAttributeValue(kTTSym_sampleRate, sp[0]->s_sr);
	x->gain->setAttributeValue(kTTSym_sampleRate, sp[0]->s_sr);
	x->gain->setAttributeValue(TT("interpolated"), true);
	
	dsp_addv(gain_perform, l, audioVectors);
	sysmem_freeptr(audioVectors);
}
TTErr TTData::setType(const TTValue& value)
{
	TTMessagePtr    initMessage;
	TTAttributePtr  valueDefaultAttribute, valueStepSizeAttribute;
	
	// Only do stuff if there is a change of type
	if (!(TTValue(mType) == value)) {
		
		TTValue n = value;				// Copy to new value to protect the attribute
		mType = value;
		
		// Get ValueDefault and ValueStepsize attributes (because commande message and value attribute are already cached)
		this->findMessage(kTTSym_Init, &initMessage);
		this->findAttribute(kTTSym_valueDefault, &valueDefaultAttribute);
		this->findAttribute(kTTSym_valueStepsize, &valueStepSizeAttribute);
		
		mInstanceBounds[0] = TTInt16(0);
		mInstanceBounds[1] = TTInt16(-1);
		
		// Register mValue attribute and prepare memory
		
		// The behavior of TTData depends on type.
		// This is controlled by setting function pointers for the init, command and setter methods.
		
		// Type is "integer", "decimal" or "array"
		if ((mType == kTTSym_integer) || (mType == kTTSym_decimal) || (mType == kTTSym_array)) {
			
			// "integer", "decimal" and "array" types can make use of dataspace and ramping, and share the same command and setter methods.
			commandMethod = (TTMethodValue)&TTData::IntegerDecimalArrayCommand;
			valueAttribute->setter = (TTSetterMethod)&TTData::setIntegerDecimalArrayValue;
			
			if (mType == kTTSym_integer) {
				initMessage->method = (TTMethod)&TTData::IntegerInit;
				
				valueAttribute->type		 = kTypeInt32;
				valueDefaultAttribute->type  = kTypeInt32;
				valueStepSizeAttribute->type = kTypeInt32;
				
				mValue		  = TTValue(0);
				mValueDefault = TTValue(0);
				
				// If mValueStepsize still equals to the value passed in constructor
				if (mValueStepsize == TTValue(0.1))
					mValueStepsize = TTValue(1);
				
				// If mRangeBounds still equals to the value passed in constructor
				if (mRangeBounds == TTValue(0.0, 1.0)) {
					mRangeBounds[0] = TTUInt16(0);
					mRangeBounds[1] = TTUInt16(1);
				}
			}
			else {
				if (mType == kTTSym_decimal)
					initMessage->method = (TTMethod)&TTData::DecimalInit;
				else if (mType == kTTSym_array)
					initMessage->method = (TTMethod)&TTData::ArrayInit;
				
				valueAttribute->type		 = kTypeFloat64;
				valueDefaultAttribute->type  = kTypeFloat64;
				valueStepSizeAttribute->type = kTypeFloat64;
				
				mValue		  = TTValue(0.);
				mValueDefault = TTValue(0.);
				
				// Don't reset mValueStepsize as the default values is equals to the value passed in constructor
				// Don't reset mRangeBounds as the default values is equals to the value passed in constructor
			}
			
			// Update mRampDrive if it is still equal to the value passed in the constructor
			if (mRampDrive == kTTSym_none)
				mRampDrive = TTSymbol("max");   // TODO : Move this very Max specific thing elsewhere
		}
		// Type is "string"
		else if (mType == kTTSym_string) {
			commandMethod = (TTMethodValue)&TTData::StringCommand;
			initMessage->method = (TTMethod)&TTData::StringInit;
			valueAttribute->type = kTypeSymbol;
			valueAttribute->setter = (TTSetterMethod)&TTData::setStringValue;
			valueDefaultAttribute->type = kTypeSymbol;
			valueStepSizeAttribute->type = kTypeSymbol;
			mValue = TTValue(kTTSym_none);
			mValueDefault = TTValue(kTTSym_none);
			
			// If mValueStepsize still equals to the value passed in constructor
			if (mValueStepsize == TTValue(0.1))
				mValueStepsize.clear();
			
			// If mRangeBounds still equals to the value passed in constructor
			if (mRangeBounds == TTValue(0.0, 1.0))
				mRangeBounds.clear();
			
			mRampDrive = kTTSym_none;
		}
		// Type is "boolean"
		else if (mType == kTTSym_boolean) {
			commandMethod = (TTMethodValue)&TTData::BooleanCommand;
			initMessage->method = (TTMethod)&TTData::BooleanInit;
			valueAttribute->type = kTypeBoolean;
			valueAttribute->setter = (TTSetterMethod)&TTData::setBooleanValue;
			valueDefaultAttribute->type = kTypeBoolean;
			valueStepSizeAttribute->type = kTypeBoolean;
			mValue = TTValue(NO);
			mValueDefault = TTValue(NO);
			
			// If mValueStepsize still equals to the value passed in constructor
			if (mValueStepsize == TTValue(0.1))
				mValueStepsize = TTValue(YES);
			
			// If mRangeBounds still equals to the value passed in constructor
			if (mRangeBounds == TTValue(0.0, 1.0)) {
				mRangeBounds[0] = NO;
				mRangeBounds[1] = YES;
			}
			
			mRampDrive = kTTSym_none;
		}
		// Type is "none"
		else if (mType == kTTSym_none) {
			commandMethod = (TTMethodValue)&TTData::NoneCommand;
			initMessage->method = (TTMethod)&TTData::NoneInit;
			valueAttribute->type = kTypeNone;
			valueAttribute->setter = (TTSetterMethod)&TTData::setNoneValue;
			valueDefaultAttribute->type = kTypeNone;
			valueStepSizeAttribute->type = kTypeNone;
			mValue.clear();
			mValueDefault.clear();
			
			// If mValueStepsize still equals to the value passed in constructor
			if (mValueStepsize == TTValue(0.1))
				mValueStepsize.clear();
			
			// If mRangeBounds still equals to the value passed in constructor
			if (mRangeBounds == TTValue(0.0, 1.0))
				mRangeBounds.clear();
			
			mRampDrive = kTTSym_none;
		}
		// Type is "generic"
		else {
			commandMethod = (TTMethodValue)&TTData::GenericCommand;
			initMessage->method = (TTMethod)&TTData::GenericInit;
			valueAttribute->type = kTypeFloat64;
			valueAttribute->setter = (TTSetterMethod)&TTData::setGenericValue;
			valueDefaultAttribute->type = kTypeFloat64;
			valueStepSizeAttribute->type = kTypeFloat64;
			mType = kTTSym_generic;
			mValue = TTValue(0.);
			mValueDefault = TTValue(0.);
			
			// Don't reset mValueStepsize as the default values is equals to the value passed in constructor
			
			// Don't reset mRangeBounds as the default values is equals to the value passed in constructor
			
			mRampDrive = kTTSym_none;
			return kTTErrGeneric;
		}
		
		// If TTModularLib is not used with the Max implementation, "max" rampDrive need to be substituted for "system".
		// TODO : Move this very Max specific thing else where
		if (mRampDrive == TTSymbol("max"))
			if (ttEnvironment->isClassRegistered("max"))
				mRampDrive = TTSymbol("system");
		
		rampSetup();
		
		this->notifyObservers(kTTSym_type, n);
	}
	return kTTErrNone;
}