sfxParameter.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "sfx/sfxParameter.h"
  23. #include "console/consoleTypes.h"
  24. #include "console/simBase.h"
  25. #include "console/engineAPI.h"
  26. #include "console/simSet.h"
  27. #include "math/mMathFn.h"
  28. #include "math/mathTypes.h"
  29. #include "math/mathIO.h"
  30. #include "core/stream/bitStream.h"
  31. #include "platform/typetraits.h"
  32. IMPLEMENT_CONOBJECT( SFXParameter );
  33. ConsoleDocClass( SFXParameter,
  34. "@brief A sound channel value that can be bound to multiple sound sources.\n\n"
  35. "Parameters are special objects that isolate a specific property that sound sources can have and allows to bind "
  36. "this isolated instance to multiple sound sources such that when the value of the parameter changes, all sources "
  37. "bound to the parameter will have their respective property changed.\n\n"
  38. "Parameters are identified and referenced by their #internalName. This means that the SFXDescription::parameters and "
  39. "SFXTrack::parameters fields should contain the #internalNames of the SFXParameter objects which should be attached to "
  40. "the SFXSources when they are created. No two SFXParameters should have the same #internalName.\n\n"
  41. "All SFXParameter instances are automatically made children of the SFXParameterGroup.\n\n"
  42. "@note To simply control the volume and/or pitch levels of a group of sounds, it is easier and more efficient to use "
  43. "a sound source group rather than SFXParameters (see @ref SFXSource_hierarchies). Simply create a SFXSource object representing the group, assign "
  44. "SFXDescription::sourceGroup of the sounds appropriately, and then set the volume and/or pitch level of the group to "
  45. "modulate the respective properties of all children.\n\n"
  46. "@section SFXParameter_updates Parameter Updates\n"
  47. "Parameters are periodically allowed to update their own values. This makes it possible to attach custom logic to a parameter "
  48. "and have individual parameters synchronize their values autonomously. Use the onUpdate() callback to attach "
  49. "script code to a parameter update.\n\n"
  50. "@tsexample\n"
  51. "new SFXParameter( EngineRPMLevel )\n"
  52. "{\n"
  53. " // Set the name by which this parameter is identified.\n"
  54. " internalName = \"EngineRPMLevel\";\n"
  55. "\n"
  56. " // Let this parameter control the pitch of attached sources to simulate engine RPM ramping up and down.\n"
  57. " channel = \"Pitch\";\n"
  58. "\n"
  59. " // Start out with unmodified pitch.\n"
  60. " defaultValue = 1;\n"
  61. "\n"
  62. " // Add a texture description of what this parameter does.\n"
  63. " description = \"Engine RPM Level\";\n"
  64. "};\n"
  65. "\n"
  66. "// Create a description that automatically attaches the engine RPM parameter.\n"
  67. "singleton SFXDescription( EngineRPMSound : AudioLoop2D )\n"
  68. "{\n"
  69. " parameters[ 0 ] = \"EngineRPMLevel\";\n"
  70. "};\n"
  71. "\n"
  72. "// Create sound sources for the engine.\n"
  73. "sfxCreateSource( EngineRPMSound, \"art/sound/engine/enginePrimary\" );\n"
  74. "sfxCreateSource( EngineRPMSound, \"art/sound/engine/engineSecondary\" );\n"
  75. "\n"
  76. "// Setting the parameter value will now affect the pitch level of both sound sources.\n"
  77. "EngineRPMLevel.value = 0.5;\n"
  78. "EngineRPMLevel.value = 1.5;\n"
  79. "@endtsexample\n\n"
  80. "@ref SFX_interactive\n\n"
  81. "@ingroup SFX"
  82. );
  83. IMPLEMENT_CALLBACK( SFXParameter, onUpdate, void, (), (),
  84. "Called when the sound system triggers an update on the parameter.\n"
  85. "This occurs periodically during system operation." );
  86. //-----------------------------------------------------------------------------
  87. SFXParameter::SFXParameter()
  88. : mValue( 1.f ),
  89. mRange( 0.f, 1.f ),
  90. mChannel( SFXChannelVolume ),
  91. mDefaultValue( 1.f )
  92. {
  93. }
  94. //-----------------------------------------------------------------------------
  95. SFXParameter::~SFXParameter()
  96. {
  97. }
  98. //-----------------------------------------------------------------------------
  99. void SFXParameter::initPersistFields()
  100. {
  101. addGroup( "Sound" );
  102. addProtectedField( "value", TypeF32, Offset( mValue, SFXParameter ),
  103. &SFXParameter::_setValue, &defaultProtectedGetFn,
  104. "Current value of the audio parameter.\n"
  105. "All attached sources are notified when this value changes." );
  106. addProtectedField( "range", TypePoint2F, Offset( mRange, SFXParameter ),
  107. &SFXParameter::_setRange, &defaultProtectedGetFn,
  108. "Permitted range for #value.\n"
  109. "Minimum and maximum allowed value for the parameter. Both inclusive.\n\n"
  110. "For all but the User0-3 channels, this property is automatically set up by SFXParameter." );
  111. addProtectedField( "channel", TYPEID< SFXChannel >(), Offset( mChannel, SFXParameter ),
  112. &SFXParameter::_setChannel, &defaultProtectedGetFn,
  113. "Channel that the parameter controls.\n"
  114. "This controls which property of the sources it is attached to the parameter controls." );
  115. addProtectedField( "defaultValue", TypeF32, Offset( mDefaultValue, SFXParameter ),
  116. &SFXParameter::_setDefaultValue, &defaultProtectedGetFn,
  117. "Value to which the parameter is initially set.\n"
  118. "When the parameter is first added to the system, #value will be set to #defaultValue." );
  119. addField( "description", TypeRealString,Offset( mDescription, SFXParameter ),
  120. "Textual description of the parameter.\n"
  121. "Primarily for use in the Audio Parameters dialog of the editor to allow for easier identification "
  122. "of parameters." );
  123. endGroup( "Sound" );
  124. Parent::initPersistFields();
  125. }
  126. //-----------------------------------------------------------------------------
  127. bool SFXParameter::_setValue( void *object, const char *index, const char *data )
  128. {
  129. reinterpret_cast< SFXParameter* >( object )->setValue( dAtof( data ) );
  130. return false;
  131. }
  132. //-----------------------------------------------------------------------------
  133. bool SFXParameter::_setRange( void *object, const char *index, const char *data )
  134. {
  135. Point2F range = EngineUnmarshallData< Point2F >()( data );
  136. reinterpret_cast< SFXParameter* >( object )->setRange( range );
  137. return false;
  138. }
  139. //-----------------------------------------------------------------------------
  140. bool SFXParameter::_setChannel( void *object, const char *index, const char *data )
  141. {
  142. SFXChannel channel = EngineUnmarshallData< SFXChannel >()( data );
  143. reinterpret_cast< SFXParameter* >( object )->setChannel( channel );
  144. return false;
  145. }
  146. //-----------------------------------------------------------------------------
  147. bool SFXParameter::_setDefaultValue( void *object, const char *index, const char *data )
  148. {
  149. reinterpret_cast< SFXParameter* >( object )->setDefaultValue( dAtof( data ) );
  150. return false;
  151. }
  152. //-----------------------------------------------------------------------------
  153. bool SFXParameter::onAdd()
  154. {
  155. if( !Parent::onAdd() )
  156. return false;
  157. mValue = mDefaultValue;
  158. // Make sure the parameter has a name.
  159. if( !getInternalName() || !getInternalName()[ 0 ] )
  160. {
  161. Con::errorf( "SFXParameter::onAdd - %i (%s): parameter object does not have a name", getId(), getName() );
  162. return false;
  163. }
  164. // Make sure the parameter has a unique name.
  165. if( find( getInternalName() ) )
  166. {
  167. Con::errorf( "SFXParameter::onAdd - %i (%s): a parameter called '%s' already exists", getId(), getName(), getInternalName() );
  168. return false;
  169. }
  170. // Add us to the SFXParameter group.
  171. Sim::getSFXParameterGroup()->addObject( this );
  172. return true;
  173. }
  174. //-----------------------------------------------------------------------------
  175. void SFXParameter::onRemove()
  176. {
  177. mEventSignal.trigger( this, SFXParameterEvent_Deleted );
  178. Parent::onRemove();
  179. }
  180. //-----------------------------------------------------------------------------
  181. void SFXParameter::update()
  182. {
  183. onUpdate_callback();
  184. }
  185. //-----------------------------------------------------------------------------
  186. void SFXParameter::reset()
  187. {
  188. setValue( mDefaultValue );
  189. }
  190. //-----------------------------------------------------------------------------
  191. void SFXParameter::setValue( F32 value )
  192. {
  193. if( value == mValue )
  194. return;
  195. mValue = mClampF( value, mRange.x, mRange.y );
  196. mEventSignal.trigger( this, SFXParameterEvent_ValueChanged );
  197. }
  198. //-----------------------------------------------------------------------------
  199. void SFXParameter::setDefaultValue( F32 value )
  200. {
  201. mDefaultValue = mClampF( value, mRange.x, mRange.y );
  202. }
  203. //-----------------------------------------------------------------------------
  204. void SFXParameter::setRange( const Point2F& range )
  205. {
  206. if( range == mRange )
  207. return;
  208. mRange = range;
  209. F32 value = mClampF( mValue, mRange.x, mRange.y );
  210. if( value != mValue )
  211. setValue( value );
  212. mDefaultValue = mClampF( mDefaultValue, mRange.x, mRange.y );
  213. }
  214. //-----------------------------------------------------------------------------
  215. void SFXParameter::setChannel( SFXChannel channel )
  216. {
  217. if( mChannel == channel )
  218. return;
  219. mChannel = channel;
  220. F32 value = mValue;
  221. switch( channel )
  222. {
  223. case SFXChannelVolume:
  224. case SFXChannelConeOutsideVolume:
  225. setRange( 0.f, 1.0f );
  226. break;
  227. case SFXChannelConeInsideAngle:
  228. case SFXChannelConeOutsideAngle:
  229. setRange( 0.f, 360.f );
  230. break;
  231. case SFXChannelPitch:
  232. case SFXChannelMinDistance:
  233. case SFXChannelMaxDistance:
  234. case SFXChannelCursor:
  235. setRange( 0.f, TypeTraits< F32 >::MAX );
  236. break;
  237. case SFXChannelStatus:
  238. setRange( F32( SFXStatusPlaying ), F32( SFXStatusStopped ) );
  239. break;
  240. default:
  241. setRange( TypeTraits< F32 >::MIN, TypeTraits< F32 >::MAX );
  242. break;
  243. }
  244. // If the range setting did not result in the value already
  245. // being changed, fire a value-change signal now so that sources
  246. // can catch on to the new semantics. Unfortunately, we can't
  247. // do something about the old semantic's value having been
  248. // changed by us.
  249. if( mValue == value )
  250. mEventSignal.trigger( this, SFXParameterEvent_ValueChanged );
  251. }
  252. //-----------------------------------------------------------------------------
  253. SFXParameter* SFXParameter::find( StringTableEntry name )
  254. {
  255. return dynamic_cast< SFXParameter* >(
  256. Sim::getSFXParameterGroup()->findObjectByInternalName( name )
  257. );
  258. }
  259. //=============================================================================
  260. // Console Methods.
  261. //=============================================================================
  262. // MARK: ---- Console Methods ----
  263. //-----------------------------------------------------------------------------
  264. DefineEngineMethod( SFXParameter, getParameterName, String, (),,
  265. "Get the name of the parameter.\n"
  266. "@return The paramete name." )
  267. {
  268. return object->getInternalName();
  269. }
  270. //-----------------------------------------------------------------------------
  271. DefineEngineMethod( SFXParameter, reset, void, (),,
  272. "Reset the parameter's value to its default.\n"
  273. "@see SFXParameter::defaultValue\n" )
  274. {
  275. object->reset();
  276. }