Jamoma API  0.6.0.a19
TTDataTypedMethods.cpp
Go to the documentation of this file.
1 /** @file
2  *
3  * @ingroup modularLibrary
4  *
5  * @brief A Data Object
6  *
7  * @details
8  *
9  * @authors Théo de la Hogue
10  *
11  * @copyright © 2010, Théo de la Hogue @n
12  * This code is licensed under the terms of the "New BSD License" @n
13  * http://creativecommons.org/licenses/BSD/
14  */
15 
16 
17 #include "TTData.h"
18 
19 TTErr TTData::setType(const TTValue& value)
20 {
21  TTMessagePtr initMessage;
22  TTAttributePtr valueDefaultAttribute, valueStepSizeAttribute;
23 
24  // Only do stuff if there is a change of type
25  if (!(TTValue(mType) == value)) {
26 
27  TTValue n = value; // Copy to new value to protect the attribute
28  mType = value;
29 
30  // Get ValueDefault and ValueStepsize attributes (because commande message and value attribute are already cached)
31  this->findMessage(kTTSym_Init, &initMessage);
32  this->findAttribute(kTTSym_valueDefault, &valueDefaultAttribute);
33  this->findAttribute(kTTSym_valueStepsize, &valueStepSizeAttribute);
34 
35  mInstanceBounds[0] = TTInt16(0);
36  mInstanceBounds[1] = TTInt16(-1);
37 
38  // Register mValue attribute and prepare memory
39 
40  // The behavior of TTData depends on type.
41  // This is controlled by setting function pointers for the init, command and setter methods.
42 
43  // Type is "integer", "decimal" or "array"
44  if ((mType == kTTSym_integer) || (mType == kTTSym_decimal) || (mType == kTTSym_array)) {
45 
46  // "integer", "decimal" and "array" types can make use of dataspace and ramping, and share the same command and setter methods.
47  commandMethod = (TTMethodValue)&TTData::IntegerDecimalArrayCommand;
48  valueAttribute->setter = (TTSetterMethod)&TTData::setIntegerDecimalArrayValue;
49 
50  if (mType == kTTSym_integer) {
51  initMessage->method = (TTMethod)&TTData::IntegerInit;
52 
53  valueAttribute->type = kTypeInt32;
54  valueDefaultAttribute->type = kTypeInt32;
55  valueStepSizeAttribute->type = kTypeInt32;
56 
57  mValue = TTValue(0);
58  mValueDefault = TTValue(0);
59 
60  // If mValueStepsize still equals to the value passed in constructor
61  if (mValueStepsize == TTValue(0.1))
62  mValueStepsize = TTValue(1);
63 
64  // If mRangeBounds still equals to the value passed in constructor
65  if (mRangeBounds == TTValue(0.0, 1.0)) {
66  mRangeBounds[0] = TTUInt16(0);
67  mRangeBounds[1] = TTUInt16(1);
68  }
69  }
70  else {
71  if (mType == kTTSym_decimal)
72  initMessage->method = (TTMethod)&TTData::DecimalInit;
73  else if (mType == kTTSym_array)
74  initMessage->method = (TTMethod)&TTData::ArrayInit;
75 
76  valueAttribute->type = kTypeFloat64;
77  valueDefaultAttribute->type = kTypeFloat64;
78  valueStepSizeAttribute->type = kTypeFloat64;
79 
80  mValue = TTValue(0.);
81  mValueDefault = TTValue(0.);
82 
83  // Don't reset mValueStepsize as the default values is equals to the value passed in constructor
84  // Don't reset mRangeBounds as the default values is equals to the value passed in constructor
85  }
86 
87  // Update mRampDrive if it is still equal to the value passed in the constructor
88  if (mRampDrive == kTTSym_none)
89  mRampDrive = TTSymbol("max"); // TODO : Move this very Max specific thing elsewhere
90  }
91  // Type is "string"
92  else if (mType == kTTSym_string) {
93  commandMethod = (TTMethodValue)&TTData::StringCommand;
94  initMessage->method = (TTMethod)&TTData::StringInit;
95  valueAttribute->type = kTypeSymbol;
96  valueAttribute->setter = (TTSetterMethod)&TTData::setStringValue;
97  valueDefaultAttribute->type = kTypeSymbol;
98  valueStepSizeAttribute->type = kTypeSymbol;
99  mValue = TTValue(kTTSym_none);
100  mValueDefault = TTValue(kTTSym_none);
101 
102  // If mValueStepsize still equals to the value passed in constructor
103  if (mValueStepsize == TTValue(0.1))
104  mValueStepsize.clear();
105 
106  // If mRangeBounds still equals to the value passed in constructor
107  if (mRangeBounds == TTValue(0.0, 1.0))
108  mRangeBounds.clear();
109 
110  mRampDrive = kTTSym_none;
111  }
112  // Type is "boolean"
113  else if (mType == kTTSym_boolean) {
114  commandMethod = (TTMethodValue)&TTData::BooleanCommand;
115  initMessage->method = (TTMethod)&TTData::BooleanInit;
116  valueAttribute->type = kTypeBoolean;
117  valueAttribute->setter = (TTSetterMethod)&TTData::setBooleanValue;
118  valueDefaultAttribute->type = kTypeBoolean;
119  valueStepSizeAttribute->type = kTypeBoolean;
120  mValue = TTValue(NO);
121  mValueDefault = TTValue(NO);
122 
123  // If mValueStepsize still equals to the value passed in constructor
124  if (mValueStepsize == TTValue(0.1))
125  mValueStepsize = TTValue(YES);
126 
127  // If mRangeBounds still equals to the value passed in constructor
128  if (mRangeBounds == TTValue(0.0, 1.0)) {
129  mRangeBounds[0] = NO;
130  mRangeBounds[1] = YES;
131  }
132 
133  mRampDrive = kTTSym_none;
134  }
135  // Type is "none"
136  else if (mType == kTTSym_none) {
137  commandMethod = (TTMethodValue)&TTData::NoneCommand;
138  initMessage->method = (TTMethod)&TTData::NoneInit;
139  valueAttribute->type = kTypeNone;
140  valueAttribute->setter = (TTSetterMethod)&TTData::setNoneValue;
141  valueDefaultAttribute->type = kTypeNone;
142  valueStepSizeAttribute->type = kTypeNone;
143  mValue.clear();
144  mValueDefault.clear();
145 
146  // If mValueStepsize still equals to the value passed in constructor
147  if (mValueStepsize == TTValue(0.1))
148  mValueStepsize.clear();
149 
150  // If mRangeBounds still equals to the value passed in constructor
151  if (mRangeBounds == TTValue(0.0, 1.0))
152  mRangeBounds.clear();
153 
154  mRampDrive = kTTSym_none;
155  }
156  // Type is "generic"
157  else {
158  commandMethod = (TTMethodValue)&TTData::GenericCommand;
159  initMessage->method = (TTMethod)&TTData::GenericInit;
160  valueAttribute->type = kTypeFloat64;
161  valueAttribute->setter = (TTSetterMethod)&TTData::setGenericValue;
162  valueDefaultAttribute->type = kTypeFloat64;
163  valueStepSizeAttribute->type = kTypeFloat64;
164  mType = kTTSym_generic;
165  mValue = TTValue(0.);
166  mValueDefault = TTValue(0.);
167 
168  // Don't reset mValueStepsize as the default values is equals to the value passed in constructor
169 
170  // Don't reset mRangeBounds as the default values is equals to the value passed in constructor
171 
172  mRampDrive = kTTSym_none;
173  return kTTErrGeneric;
174  }
175 
176  // If TTModularLib is not used with the Max implementation, "max" rampDrive need to be substituted for "system".
177  // TODO : Move this very Max specific thing else where
178  if (mRampDrive == TTSymbol("max"))
179  if (ttEnvironment->isClassRegistered("max"))
180  mRampDrive = TTSymbol("system");
181 
182  rampSetup();
183 
184  this->notifyObservers(kTTSym_type, n);
185  }
186  return kTTErrNone;
187 }
188 
189 TTErr TTData::Command(const TTValue& inputValue, TTValue& outputValue)
190 {
191  externalRampTime = 0;
192 
193  // Is the command already parsed ?
194  if (inputValue.size()) {
195 
196  if (inputValue[0].type() == kTypePointer)
197 
198  // call the specific command method depending on mType
199  return (this->*commandMethod)(inputValue, outputValue);
200  }
201 
202  // else we parse command locally (and we need to free the parsed command afterwards)
203  TTDictionaryBasePtr command = NULL;
204  TTErr err;
205 
206  // for string type : keep only the first element
207  if (mType == kTTSym_string && inputValue.size())
208  command = TTDataParseCommand(inputValue[0], NO);
209 
210  // for integer, decimal or array type : parse unit and ramp
211  else
212  command = TTDataParseCommand(inputValue, mType == kTTSym_integer || mType == kTTSym_decimal || mType == kTTSym_array);
213 
214  if (!command)
215  return kTTErrGeneric;
216 
217  // call the specific command method depending on mType
218  err = (this->*commandMethod)((TTPtr)command, outputValue);
219 
220  // free the command
221  delete command;
222 
223  return err;
224 }
225 
226 TTBoolean TTData::clipValue()
227 {
228  //bool didClipAll = false;
229 
230  // the code regarding didClipAll is supposed to return true when every member of the list has been clipped to its limit
231  // that way ramping can be terminated prematurely if it was trying to ramp to something out of range
232  // however, this code as it is doesn't work, and it doesn't buy us much anyway
233  // so I'm just commenting it out for the time being [TAP]
234 
235  if (mRangeClipmode != kTTSym_none) {
236 
237  if (mRangeClipmode == kTTSym_low)
238  mValue.cliplow(TTFloat64(mRangeBounds[0]));
239  else if (mRangeClipmode == kTTSym_high)
240  mValue.cliphigh(TTFloat64(mRangeBounds[1]));
241  else if (mRangeClipmode == kTTSym_both)
242  mValue.clip(TTFloat64(mRangeBounds[0]), TTFloat64(mRangeBounds[1]));
243  else if (mRangeClipmode == kTTSym_wrap)
244  mValue.wrap(TTFloat64(mRangeBounds[0]), TTFloat64(mRangeBounds[1]));
245  //else if (mRangeClipmode == kTTSym_wrap)
246  //mValue.clipwrap(TTFloat64(mRangeBounds[0]), TTFloat64(mRangeBounds[1]));
247  else if (mRangeClipmode == kTTSym_fold)
248  mValue.fold(TTFloat64(mRangeBounds[0]), TTFloat64(mRangeBounds[1]));
249  }
250 
251  return false;
252 }
253 
254 TTErr TTData::returnValue()
255 {
256  // used a new value to protect the internal value
257  TTValue v = mValue;;
258 
259  // COMMENT: This is a temporary solution to have audio rate ramping outside the #TTData
260  // TODO: JamomaCore #212 : when Trond ports dataspace ramp we need to think about how that works with audio rate ramps
261  if (mRampDrive == kTTSym_external) {
262  if (externalRampTime > 0)
263  v.append(externalRampTime);
264  }
265 
266  // we have had our value set at least once
267  // only parameters notify their initialisation
268  if (mService == kTTSym_parameter && !mInitialized) {
269  mInitialized = YES;
270  initializedAttribute->sendNotification(kTTSym_notify, YES); // we use kTTSym_notify because we know that observers are TTCallback
271  }
272  else if (!mInitialized)
273  mInitialized = YES;
274 
275  // return the value to his owner
276  this->deliver(v);
277 
278  // notify each observers
279  valueAttribute->sendNotification(kTTSym_notify, v); // we use kTTSym_notify because we know that observers are TTCallback
280 
281  return kTTErrNone;
282 }
283 
284 # pragma mark -
285 # pragma mark None type
286 
287 TTErr TTData::NoneCommand(const TTValue& inputValue, TTValue& outputValue)
288 {
289  TTValue none;
290  return this->setNoneValue(none);
291 }
292 
293 TTErr TTData::setNoneValue(const TTValue& value)
294 {
295  if (!mIsSending && mActive) {
296 
297  // lock
298  mIsSending = YES;
299 
300  // we have had our value set at least once
301  if (mService == kTTSym_parameter && !mInitialized) {
302  mInitialized = YES;
303  initializedAttribute->sendNotification(kTTSym_notify, YES);
304  }
305 
306  // return the internal value
307  returnValue();
308 
309  // unlock
310  mIsSending = NO;
311 
312  return kTTErrNone;
313  }
314 
315  return kTTErrGeneric;
316 }
317 
318 TTErr TTData::NoneInit()
319 {
320  // the value is not initialized
321  mInitialized = NO;
322 
323  // notify observers about the initialization state
324  initializedAttribute->sendNotification(kTTSym_notify, mInitialized);
325 
326  return kTTErrNone;
327 }
328 
329 # pragma mark -
330 # pragma mark Generic type
331 
332 TTErr TTData::GenericCommand(const TTValue& inputValue, TTValue& outputValue)
333 {
334  TTDictionaryBasePtr command = NULL;
335  TTValue c, aValue;
336 
337  if (inputValue.size()) {
338 
339  // 1. Get the command TTDictionnary
340  ///////////////////////////////////////////////////
341  if (inputValue[0].type() == kTypePointer)
342  command = TTDictionaryBasePtr((TTPtr)inputValue[0]);
343  else
344  return kTTErrGeneric;
345 
346  // 2. Get the value
347  command->getValue(aValue);
348 
349  // 3. Filter repetitions
350  //////////////////////////////////
351  if (mRepetitionsFilter && mInitialized)
352  if (mValue == aValue)
353  return kTTErrNone; // nothing to do
354 
355  }
356 
357  // 4. Set the value directly
358  return this->setGenericValue(aValue);
359 }
360 
361 TTErr TTData::setGenericValue(const TTValue& value)
362 {
363  if (!mIsSending && mActive) {
364 
365  // lock
366  mIsSending = YES;
367 
368  // don't update the internal value with empty value
369  if (value.size()) {
370 
371  // set internal value
372  mValue = value;
373 
374  // clip the internal value
375  clipValue();
376  }
377 
378  // return the internal value
379  returnValue();
380 
381  // unlock
382  mIsSending = NO;
383 
384  return kTTErrNone;
385  }
386 
387  return kTTErrGeneric;
388 }
389 
390 TTErr TTData::GenericInit()
391 {
392  // reset initialisation state
393  mInitialized = NO;
394 
395  // if valueDefault type is right
396  if (!mValueDefault.empty())
397  {
398  TTValue empty;
399  this->Command(mValueDefault, empty);
400  }
401 
402  // notify observers about the initialization state
403  initializedAttribute->sendNotification(kTTSym_notify, mInitialized);
404 
405  return kTTErrNone;
406 }
407 
408 # pragma mark -
409 # pragma mark Boolean type
410 
411 TTErr TTData::BooleanCommand(const TTValue& inputValue, TTValue& outputValue)
412 {
413  TTDictionaryBasePtr command = NULL;
414  TTSymbol unit;
415  TTFloat64 time;
416  TTBoolean isRunning;
417  TTValue c, v, aValue, none;
418 
419  if (inputValue.size()) {
420 
421  // 1. Get the command TTDictionnary
422  ///////////////////////////////////////////////////
423  if (inputValue[0].type() == kTypePointer)
424  command = TTDictionaryBasePtr((TTPtr)inputValue[0]);
425  else
426  return kTTErrGeneric;
427 
428  // 2. Get the value
429  command->getValue(aValue);
430 
431  // 3. Filter repetitions
432  //////////////////////////////////
433  if (mRepetitionsFilter && mInitialized) {
434 
435  aValue.booleanize();
436 
437  if (mValue == aValue)
438  return kTTErrNone; // nothing to do
439  }
440 
441  // 4. Ramp the convertedValue
442  /////////////////////////////////
443  if (mRamper.valid()) {
444 
445  if (!command->lookup(kTTSym_ramp, v)) {
446 
447  v.get(0, time);
448 
449  if (time > 0) {
450 
451  mRamper.send("Set", mValue);
452  mRamper.send("Target", aValue);
453  mRamper.send(kTTSym_Go, (int)time);
454 
455  // update the ramp status attribute
456  mRamper.get(kTTSym_running, isRunning);
457  if (mRampStatus != isRunning) {
458  mRampStatus = isRunning;
459  notifyObservers(kTTSym_rampStatus, mRampStatus);
460  }
461 
462  return kTTErrNone;
463  }
464  }
465 
466  // in any other cases :
467  // stop ramping before to set a value
468  mRamper.send(kTTSym_Stop);
469 
470  // update the ramp status attribute
471  mRamper.get(kTTSym_running, isRunning);
472  if (mRampStatus != isRunning) {
473  mRampStatus = isRunning;
474  notifyObservers(kTTSym_rampStatus, mRampStatus);
475  }
476  }
477  }
478 
479  // 6. Set the value directly
480  return this->setBooleanValue(aValue);
481 }
482 
483 TTErr TTData::setBooleanValue(const TTValue& value)
484 {
485  if (!mIsSending && mActive) {
486 
487  // lock
488  mIsSending = YES;
489 
490  if (checkBooleanType(value)) {
491 
492  // don't update the internal value with empty value
493  if (value.size() == 1) {
494 
495  // set internal value
496  mValue = value;
497 
498  // booleanize the internal value
499  mValue.booleanize();
500  }
501 
502  // return the internal value
503  returnValue();
504 
505  // unlock
506  mIsSending = NO;
507 
508  return kTTErrNone;
509  }
510  else {
511 
512  // unlock
513  mIsSending = NO;
514 
515  return kTTErrInvalidValue;
516  }
517  }
518 
519  return kTTErrGeneric;
520 }
521 
522 TTBoolean TTData::checkBooleanType(const TTValue& value)
523 {
524  if (value.size() == 0)
525  return YES;
526 
527  TTDataType type = value[0].type();
528 
529  return type == kTypeNone ||
530  type == kTypeBoolean ||
531  type == kTypeFloat32 ||
532  type == kTypeFloat64 ||
533  type == kTypeInt8 ||
534  type == kTypeUInt8 ||
535  type == kTypeInt16 ||
536  type == kTypeUInt16 ||
537  type == kTypeInt32 ||
538  type == kTypeUInt32 ||
539  type == kTypeInt64 ||
540  type == kTypeUInt64;
541 }
542 
543 TTErr TTData::BooleanInit()
544 {
545  // reset initialisation state
546  mInitialized = NO;
547 
548  // if valueDefault type is right
549  if (checkBooleanType(mValueDefault))
550  if (!(mValueDefault.empty()))
551  {
552  TTValue empty;
553  this->Command(mValueDefault, empty);
554  }
555 
556  // notify observers about the initialization state
557  initializedAttribute->sendNotification(kTTSym_notify, mInitialized);
558 
559  return kTTErrNone;
560 }
561 
562 # pragma mark -
563 # pragma mark Integer type
564 
565 TTBoolean TTData::checkIntegerType(const TTValue& value)
566 {
567  if (value.size() == 0)
568  return YES;
569 
570  TTDataType type = value[0].type();
571 
572  return type == kTypeNone ||
573  type == kTypeFloat32 ||
574  type == kTypeFloat64 ||
575  type == kTypeInt8 ||
576  type == kTypeUInt8 ||
577  type == kTypeInt16 ||
578  type == kTypeUInt16 ||
579  type == kTypeInt32 ||
580  type == kTypeUInt32 ||
581  type == kTypeInt64 ||
582  type == kTypeUInt64;
583 }
584 
585 TTErr TTData::IntegerInit()
586 {
587  // reset initialisation state
588  mInitialized = NO;
589 
590  // if valueDefault type is right
591  if (checkIntegerType(mValueDefault))
592  if (!(mValueDefault.empty()))
593  {
594  TTValue empty;
595  this->Command(mValueDefault, empty);
596  }
597 
598  // notify observers about the initialization state
599  initializedAttribute->sendNotification(kTTSym_notify, mInitialized);
600 
601  return kTTErrNone;
602 }
603 
604 # pragma mark -
605 # pragma mark Methods common to Decimal, Integer and Array type
606 
607 TTErr TTData::IntegerDecimalArrayCommand(const TTValue& inputValue, TTValue& outputValue)
608 {
609  TTDictionaryBasePtr command = NULL;
610  TTSymbol unit;
611  TTUInt32 time;
612  TTBoolean isRunning;
613  TTValue c, v, aValue, none;
614 
615  if (inputValue.size()) {
616 
617  // New command terminates any ongoing ramp
618  if (mRamper.valid() && mRampStatus)
619  {
620  mRamper.send(kTTSym_Stop);
621  }
622 
623  // Get the command TTDictionnary
624 
625  // This contains the new value and optional information on ramp and unit
626  if (inputValue[0].type() == kTypePointer)
627  command = TTDictionaryBasePtr((TTPtr)inputValue[0]);
628  else
629  return kTTErrGeneric;
630 
631  // Get the new target value. This might be specified in an overriding unit
632  command->getValue(aValue);
633 
634  // For mType = "decimal" or "integer" it does not make sense to have more than one item in aValue.
635  if ((mType == kTTSym_decimal) || (mType == kTTSym_integer)) {
636  if (aValue.size()>1)
637  aValue.resize(1);
638  }
639 
640  // Set up ramp if requested, this might include overriding dataspace unit
641  if (mRamper.valid()) {
642  if (!command->lookup(kTTSym_ramp, v)) {
643 
644  time = v[0];
645 
646  // Is a valid ramp time requested?
647  if (time > 0) {
648 
649  TTValue rampStartValue;
650 
651  // Is the dataspace unit to be temporarily overridden during the ramp?
652  if ((mDataspaceConverter.valid()) && (!command->lookup(kTTSym_unit, v))) {
653 
654  unit = v[0];
655 
656  mIsOverridingDataspaceUnit = true;
657 
658  // Set up units for conversions
659  mDataspaceConverter.set(kTTSym_inputUnit, unit);
660  mDataspaceInverseConverter.set(kTTSym_outputUnit, unit);
661 
662  // Convert current value to temporary unit, and use as ramp start value
663  inverseConvertUnit(mValue, rampStartValue);
664  }
665 
666  // No dataspace unit conversion is needed during the ramp
667  else {
668  mIsOverridingDataspaceUnit = false;
669 
670  rampStartValue = mValue;
671  }
672 
673  // Set the start ramp value
674  mRamper.send("Set", rampStartValue);
675 
676  // Set the end value using the overriding unit
677  mRamper.send("Target", aValue);
678 
679  // Set ramp time and start the ramp, we don't output any value immediately
680  mRamper.send(kTTSym_Go, (int)time);
681 
682  // Update the ramp status attribute
683  mRamper.get(kTTSym_running, isRunning);
684  if (mRampStatus != isRunning) {
685  mRampStatus = isRunning;
686  notifyObservers(kTTSym_rampStatus, mRampStatus);
687  }
688 
689  return kTTErrNone;
690  }
691  }
692  }
693  // External ramp drive case - this is used with the j.parameter~ abstraction in Max. This is just a prototype until a proper C++ solution can be coded, and we do not bother to deal with ramp function or dataspace.
694  else if (mRampDrive == kTTSym_external) {
695  if (!command->lookup(kTTSym_ramp, v))
696  externalRampTime = v[0];
697  }
698 
699  // No ramping, target vale will be set immediately. This part of the method will only be executed if (a) no ramp was requested, (b) ramp time was less or equal to 0, or (c) we are using an external ramp drive.
700 
701  // Check for overriding unit, convert to default unit if necessary
702  if ((mDataspaceConverter.valid()) && (!command->lookup(kTTSym_unit, v))) {
703 
704  TTValue convertedValue;
705 
706  unit = v[0];
707  mDataspaceConverter.set(kTTSym_inputUnit, unit);
708  convertUnit(aValue, convertedValue);
709  aValue = convertedValue;
710  } else {
711  // Ramp and unit conversion implicitly ensure that type is kTypeFloat64, but if we don't have ramp or unit, we need to ensure that mValue is set as kTypeFloat64. We do not run this test if (aValue.size() == 0.). That will be the case in the Max implementation when sending a "bang" to j.parameter.
712  // TODO: The following need to be reconsidered if this method is to cater for integers and arrays as well.
713  if (mType==kTTSym_decimal) {
714  if (aValue.size())
715  if (aValue[0].type() != kTypeFloat64)
716  aValue = (TTFloat64)aValue[0];
717  }
718  else if (mType==kTTSym_integer) {
719  if (aValue.size())
720  // Cast to int if
721  if (!checkIntegerType(aValue))
722  aValue = (TTInt32)aValue[0];
723  aValue[0].truncate();
724  }
725  else if (mType==kTTSym_array) {
726  for (int i=0; i<aValue.size(); i++)
727  aValue[i] = (TTFloat64)aValue[i];
728  }
729  }
730 
731  mIsOverridingDataspaceUnit = false;
732 
733  // Update the ramp status attribute, unless we use external ramp drive
734  if (mRamper.valid() && mRampDrive != kTTSym_external)
735  {
736  mRamper.get(kTTSym_running, isRunning);
737  if (mRampStatus != isRunning)
738  {
739  mRampStatus = isRunning;
740  notifyObservers(kTTSym_rampStatus, mRampStatus);
741  }
742  }
743  }
744 
745  // Set the value directly
746  return this->setIntegerDecimalArrayValue(aValue);
747 }
748 
749 
750 TTErr TTData::setIntegerDecimalArrayValue(const TTValue& value)
751 {
752  // Storing locally to protect the input value
753  TTValue lValue = value;
754 
755  if (!mIsSending && mActive) {
756 
757  // We are locking while updating, in order to prevent returned values from clients to cause an infinite loop.
758  mIsSending = YES;
759 
760  if (checkIntegerDecimalArrayType(lValue)) {
761 
762  if (lValue.size() > 0) {
763 
764  TTValue lPreviousValue = mValue;
765 
766  // If ramp is performed using non-default dataspace unit, the returned values need to be converted.
767  if (mIsOverridingDataspaceUnit)
768  convertUnit(lValue, mValue);
769  else
770  mValue = lValue;
771 
772  // Clipping
773  clipValue();
774 
775 
776  // Truncate if type is integer
777  if (mType == kTTSym_integer)
778  mValue.truncate();
779 
780  // NOTE: If ramps reach the end prematurely (due to requests for ramp targets beyond the accepted range), the ramp will not be stopped.
781 
782  // Filter repetitions, and return the internal value - this is passing it to the owner of the #TTData and will notify all value observers.
783  if (mRepetitionsFilter) {
784  if (mInitialized) {
785  if (mValue != lPreviousValue)
786  returnValue();
787  }
788  else {
789  returnValue();
790  mInitialized = true;
791  }
792 
793  }
794  else
795  returnValue();
796  } else {
797  // If the value is empty, return current value. This is the case in the Max implementation when a "bang" is sent to j.parameter.
798  returnValue();
799  }
800  // unlock
801  mIsSending = NO;
802 
803  return kTTErrNone;
804  }
805  else {
806 
807  // unlock
808  mIsSending = NO;
809 
810  return kTTErrInvalidValue;
811  }
812  }
813 
814  return kTTErrGeneric;
815 }
816 
817 TTBoolean TTData::checkIntegerDecimalArrayType(const TTValue& value)
818 {
819  if (mType == kTTSym_array)
820  return YES;
821  else if ((mType == kTTSym_integer) || (mType == kTTSym_decimal)) {
822 
823  if (value.size() == 0)
824  return YES;
825 
826  TTDataType type = value[0].type();
827 
828  return type == kTypeFloat32 ||
829  type == kTypeFloat64 ||
830  type == kTypeInt8 ||
831  type == kTypeUInt8 ||
832  type == kTypeInt16 ||
833  type == kTypeUInt16 ||
834  type == kTypeInt32 ||
835  type == kTypeUInt32 ||
836  type == kTypeInt64 ||
837  type == kTypeUInt64;
838  }
839  else
840  return NO;
841 }
842 
843 TTErr TTData::DecimalInit()
844 {
845  // reset initialisation state
846  mInitialized = NO;
847 
848  // if valueDefault type is right
849  if (checkIntegerDecimalArrayType(mValueDefault))
850  if (!(mValueDefault.empty()))
851  {
852  TTValue empty;
853  this->Command(mValueDefault, empty);
854  }
855 
856  // notify observers about the initialization state
857  initializedAttribute->sendNotification(kTTSym_notify, mInitialized);
858 
859  return kTTErrNone;
860 }
861 
862 # pragma mark -
863 # pragma mark Array type
864 
865 TTBoolean TTData::checkArrayType(const TTValue& value)
866 {
867  return true;
868 }
869 
870 TTErr TTData::ArrayInit()
871 {
872  // reset initialisation state
873  mInitialized = NO;
874 
875  // if valueDefault type is right
876  if (checkArrayType(mValueDefault))
877  if (!(mValueDefault.empty()))
878  {
879  TTValue empty;
880  this->Command(mValueDefault, empty);
881  }
882 
883  // notify observers about the initialization state
884  initializedAttribute->sendNotification(kTTSym_notify, mInitialized);
885 
886  return kTTErrNone;
887 }
888 
889 # pragma mark -
890 # pragma mark String type
891 
892 TTErr TTData::StringCommand(const TTValue& inputValue, TTValue& outputValue)
893 {
894  TTDictionaryBasePtr command = NULL;
895  TTValue c, aValue;
896 
897  if (inputValue.size()) {
898 
899  // 1. Get the command TTDictionnary
900  ///////////////////////////////////////////////////
901  if (inputValue[0].type() == kTypePointer)
902  command = TTDictionaryBasePtr((TTPtr)inputValue[0]);
903  else
904  return kTTErrGeneric;
905 
906  // 2. Get the value
907  command->getValue(aValue);
908 
909  // 3. Filter repetitions
910  //////////////////////////////////
911  if (mRepetitionsFilter && mInitialized) {
912 
913  if (mValue == aValue)
914  return kTTErrNone; // nothing to do
915  }
916  }
917 
918  // 4. Set the value directly
919  return this->setStringValue(aValue);
920 }
921 
922 TTErr TTData::setStringValue(const TTValue& value)
923 {
924  if (!mIsSending && mActive) {
925 
926  // lock
927  mIsSending = YES;
928 
929  if (checkStringType(value)) {
930 
931  // don't update the internal value with empty value
932  if (value.size()) {
933 
934  // make other value type into symbol
935  if (value[0].type() != kTypeSymbol) {
936 
937  TTValue parsed;
938  TTString s;
939 
940  parsed = value;
941  parsed.toString();
942  s = TTString(parsed[0]);
943  parsed = TTValue(TTSymbol(s));
944 
945  // unlock
946  mIsSending = NO;
947 
948  // set again with the parsed value
949  return setStringValue(parsed);
950  }
951 
952  TTBoolean found = YES;
953 
954  // check if the string is precised into rangeBounds
955  if (mRangeBounds.size()) {
956 
957  found = NO;
958 
959  for (TTUInt32 i = 0; i < mRangeBounds.size(); i++) {
960 
961  found = value[0] == mRangeBounds[i];
962 
963  if (found == YES)
964  break;
965  }
966  }
967 
968  // set internal value
969  if (found)
970  mValue = value;
971  }
972 
973  // return the internal value
974  returnValue();
975 
976  // unlock
977  mIsSending = NO;
978 
979  return kTTErrNone;
980  }
981  else {
982 
983  // unlock
984  mIsSending = NO;
985  return kTTErrInvalidValue;
986  }
987 
988  // unlock
989  mIsSending = NO;
990 
991  return kTTErrNone;
992  }
993 
994  return kTTErrGeneric;
995 }
996 
997 TTBoolean TTData::checkStringType(const TTValue& value)
998 {
999  return true;
1000 }
1001 
1002 TTErr TTData::StringInit()
1003 {
1004  // reset initialisation state
1005  mInitialized = NO;
1006 
1007  // if valueDefault type is right
1008  if (checkStringType(mValueDefault))
1009  if (!(mValueDefault.empty()))
1010  {
1011  TTValue empty;
1012  this->Command(mValueDefault, empty);
1013  }
1014 
1015  // notify observers about the initialization state
1016  initializedAttribute->sendNotification(kTTSym_notify, mInitialized);
1017 
1018  return kTTErrNone;
1019 }
TTErr(TTObjectBase::* TTSetterMethod)(const TTAttribute &attribute, const TTValue &value)
A type that can be used to store a pointer to a message for an object.
Definition: TTObjectBase.h:78
bool TTBoolean
Boolean flag, same as Boolean on the Mac.
Definition: TTBase.h:167
void booleanize()
Booleanize numerical elements.
Definition: TTValue.h:342
std::uint16_t TTUInt16
16 bit unsigned integer
Definition: TTBase.h:176
An inappropriate value was specified for an attribute or variable.
Definition: TTBase.h:349
TTDictionaryBase * TTDictionaryBasePtr
Pointer to a TTDictionary.
TTErr send(const TTSymbol aName)
Send a message to this object with no arguments.
Definition: TTObject.cpp:135
This class represents a single message, as used by the TTObjectBase class.
Definition: TTMessage.h:55
TTString toString(TTBoolean quotes=YES) const
Return the content as a single string with spaces between elements.
Definition: TTValue.h:351
TTDataType type
The data type of the attribute value.
Definition: TTAttribute.h:84
A type that contains a key and a value.
TTFOUNDATION_EXPORT TTEnvironment * ttEnvironment
The environment object has one instance, which is global in scope.
A Data Object.
8-bit unsigned integer, range is 0 through 255.
Definition: TTBase.h:274
64-bit unsigned integer, range is 0 through 18,446,744,073,709,551,615.
Definition: TTBase.h:280
size_type size() const noexcept
Return the number of elements.
TTMethod method
method associated with this message.
Definition: TTMessage.h:60
TTDataType
TTBlue Data Types Enumeration of data types used through out TTBlue, including the TTValue class and ...
Definition: TTBase.h:269
TTErr sendNotification(const TTSymbol name, const TTValue &arguments)
Send a notification.
TTErr(TTObjectBase::* TTMethodValue)(const TTValue &anInputValue, TTValue &anOutputValue)
A type that can be used to call a message for an object that does not declare the name argument...
Definition: TTObjectBase.h:52
void fold(const TTFloat64 &aLowBound, const TTFloat64 &aHighBound)
Fold numerical values to remain between low and high boundaries.
Definition: TTValue.h:310
This class represents a single attribute, as used by the TTObjectBase class.
Definition: TTAttribute.h:79
TTSetterMethod setter
Method to set the attribute value.
Definition: TTAttribute.h:87
TTErr findMessage(const TTSymbol name, TTMessage **message)
Find a message registered with this object.
TTErr(TTObjectBase::* TTMethod)(const TTSymbol methodName, const TTValue &anInputValue, TTValue &anOutputValue)
A type that can be used to store a pointer to a message for an object.
Definition: TTObjectBase.h:46
Symbol type.
Definition: TTBase.h:282
double TTFloat64
64 bit floating point number
Definition: TTBase.h:188
16-bit unsigned integer, range is 0 through 65,535.
Definition: TTBase.h:276
void append(const T &anElementValueToAppend)
Insert a single TTElement at the end.
Definition: TTValue.h:243
void * TTPtr
A generic pointer.
Definition: TTBase.h:248
std::int16_t TTInt16
16 bit signed integer
Definition: TTBase.h:175
TTErr get(const TTSymbol aName, T &aReturnedValue) const
Get an attribute value for an object.
void cliplow(const TTFloat64 &aLowBound)
Clip numerical values below a specified boundary.
Definition: TTValue.h:289
16-bit signed integer, range is −32,768 through 32,767.
Definition: TTBase.h:275
64-bit floating point
Definition: TTBase.h:272
TTErr set(const TTSymbol aName, T aValue)
Set an attribute value for an object.
The TTSymbol class is used to represent a string and efficiently pass and compare that string...
Definition: TTSymbol.h:26
void get(const TTUInt16 index, T &returnedElementValue) const
DEPRECATED.
Definition: TTValue.h:591
Boolean (1/0) or (true/false) flag.
Definition: TTBase.h:281
TTDictionaryBasePtr TTMODULAR_EXPORT TTDataParseCommand(const TTValue &commandValue, TTBoolean parseUnitAndRamp=YES)
Format the command to update the value of TTData as a TTDictionary.
Definition: TTData.cpp:703
void cliphigh(const TTFloat64 &aHighBound)
Clip numerical values above a specified boundary.
Definition: TTValue.h:299
TTErr lookup(const TTSymbol key, TTValue &value) const
Find the value for the given key.
std::int32_t TTInt32
32 bit signed integer
Definition: TTBase.h:177
void clip(const TTFloat64 &aLowBound, const TTFloat64 &aHighBound)
Clip numerical values between low and high boundaries.
Definition: TTValue.h:279
64-bit signed integer, ragne is −9,223,372,036,854,775,808 through 9,223,372,036,854,775,807
Definition: TTBase.h:279
32-bit floating point
Definition: TTBase.h:271
void clear()
Clear all values from the vector, leaving with size of 0.
Definition: TTValue.h:131
32-bit signed integer, range is -2,147,483,648 through 2,147,483,647.
Definition: TTBase.h:277
Something went wrong, but what exactly is not known. Typically used for context-specific problems...
Definition: TTBase.h:344
TTErr
Jamoma Error Codes Enumeration of error codes that might be returned by any of the TTBlue functions a...
Definition: TTBase.h:342
void wrap(const TTFloat64 &aLowBound, const TTFloat64 &aHighBound)
Wrap numerical values to remain between low and high boundaries.
Definition: TTValue.h:321
std::uint32_t TTUInt32
32 bit unsigned integer
Definition: TTBase.h:178
Pointer type.
Definition: TTBase.h:284
32-bit unsigned integer, range is 0 through 4,294,967,295.
Definition: TTBase.h:278
8-bit signed integer, range is -128 through 127.
Definition: TTBase.h:273
No Error.
Definition: TTBase.h:343
TTErr isClassRegistered(const TTSymbol &className)
Check if a class is registered.
The TTString class is used to represent a string.
Definition: TTString.h:34
void truncate()
Truncate float & double elements so that only whole number remains.
Definition: TTValue.h:334
TTBoolean valid() const
Determine if the object contained by this TTObject is truly ready for use.
Definition: TTObject.cpp:179
void resize(size_type n)
Change the number of elements.
[doxygenAppendixC_copyExample]
Definition: TTValue.h:34
TTErr findAttribute(const TTSymbol name, TTAttribute **attr)
Find an attribute.
TTErr getValue(TTValue &returnedValue) const
TODO: Add documentation.