sfxParameter.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  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. docsURL;
  102. addGroup( "Sound" );
  103. addProtectedField( "value", TypeF32, Offset( mValue, SFXParameter ),
  104. &SFXParameter::_setValue, &defaultProtectedGetFn,
  105. "Current value of the audio parameter.\n"
  106. "All attached sources are notified when this value changes." );
  107. addProtectedField( "range", TypePoint2F, Offset( mRange, SFXParameter ),
  108. &SFXParameter::_setRange, &defaultProtectedGetFn,
  109. "Permitted range for #value.\n"
  110. "Minimum and maximum allowed value for the parameter. Both inclusive.\n\n"
  111. "For all but the User0-3 channels, this property is automatically set up by SFXParameter." );
  112. addProtectedField( "channel", TYPEID< SFXChannel >(), Offset( mChannel, SFXParameter ),
  113. &SFXParameter::_setChannel, &defaultProtectedGetFn,
  114. "Channel that the parameter controls.\n"
  115. "This controls which property of the sources it is attached to the parameter controls." );
  116. addProtectedField( "defaultValue", TypeF32, Offset( mDefaultValue, SFXParameter ),
  117. &SFXParameter::_setDefaultValue, &defaultProtectedGetFn,
  118. "Value to which the parameter is initially set.\n"
  119. "When the parameter is first added to the system, #value will be set to #defaultValue." );
  120. addField( "description", TypeRealString,Offset( mDescription, SFXParameter ),
  121. "Textual description of the parameter.\n"
  122. "Primarily for use in the Audio Parameters dialog of the editor to allow for easier identification "
  123. "of parameters." );
  124. endGroup( "Sound" );
  125. Parent::initPersistFields();
  126. }
  127. //-----------------------------------------------------------------------------
  128. bool SFXParameter::_setValue( void *object, const char *index, const char *data )
  129. {
  130. reinterpret_cast< SFXParameter* >( object )->setValue( dAtof( data ) );
  131. return false;
  132. }
  133. //-----------------------------------------------------------------------------
  134. bool SFXParameter::_setRange( void *object, const char *index, const char *data )
  135. {
  136. Point2F range = EngineUnmarshallData< Point2F >()( data );
  137. reinterpret_cast< SFXParameter* >( object )->setRange( range );
  138. return false;
  139. }
  140. //-----------------------------------------------------------------------------
  141. bool SFXParameter::_setChannel( void *object, const char *index, const char *data )
  142. {
  143. SFXChannel channel = EngineUnmarshallData< SFXChannel >()( data );
  144. reinterpret_cast< SFXParameter* >( object )->setChannel( channel );
  145. return false;
  146. }
  147. //-----------------------------------------------------------------------------
  148. bool SFXParameter::_setDefaultValue( void *object, const char *index, const char *data )
  149. {
  150. reinterpret_cast< SFXParameter* >( object )->setDefaultValue( dAtof( data ) );
  151. return false;
  152. }
  153. //-----------------------------------------------------------------------------
  154. bool SFXParameter::onAdd()
  155. {
  156. if( !Parent::onAdd() )
  157. return false;
  158. mValue = mDefaultValue;
  159. // Make sure the parameter has a name.
  160. if( !getInternalName() || !getInternalName()[ 0 ] )
  161. {
  162. Con::errorf( "SFXParameter::onAdd - %i (%s): parameter object does not have a name", getId(), getName() );
  163. return false;
  164. }
  165. // Make sure the parameter has a unique name.
  166. if( find( getInternalName() ) )
  167. {
  168. Con::errorf( "SFXParameter::onAdd - %i (%s): a parameter called '%s' already exists", getId(), getName(), getInternalName() );
  169. return false;
  170. }
  171. // Add us to the SFXParameter group.
  172. Sim::getSFXParameterGroup()->addObject( this );
  173. return true;
  174. }
  175. //-----------------------------------------------------------------------------
  176. void SFXParameter::onRemove()
  177. {
  178. mEventSignal.trigger( this, SFXParameterEvent_Deleted );
  179. Parent::onRemove();
  180. }
  181. //-----------------------------------------------------------------------------
  182. void SFXParameter::update()
  183. {
  184. onUpdate_callback();
  185. }
  186. //-----------------------------------------------------------------------------
  187. void SFXParameter::reset()
  188. {
  189. setValue( mDefaultValue );
  190. }
  191. //-----------------------------------------------------------------------------
  192. void SFXParameter::setValue( F32 value )
  193. {
  194. if( value == mValue )
  195. return;
  196. mValue = mClampF( value, mRange.x, mRange.y );
  197. mEventSignal.trigger( this, SFXParameterEvent_ValueChanged );
  198. }
  199. //-----------------------------------------------------------------------------
  200. void SFXParameter::setDefaultValue( F32 value )
  201. {
  202. mDefaultValue = mClampF( value, mRange.x, mRange.y );
  203. }
  204. //-----------------------------------------------------------------------------
  205. void SFXParameter::setRange( const Point2F& range )
  206. {
  207. if( range == mRange )
  208. return;
  209. mRange = range;
  210. F32 value = mClampF( mValue, mRange.x, mRange.y );
  211. if( value != mValue )
  212. setValue( value );
  213. mDefaultValue = mClampF( mDefaultValue, mRange.x, mRange.y );
  214. }
  215. //-----------------------------------------------------------------------------
  216. void SFXParameter::setChannel( SFXChannel channel )
  217. {
  218. if( mChannel == channel )
  219. return;
  220. mChannel = channel;
  221. F32 value = mValue;
  222. switch( channel )
  223. {
  224. case SFXChannelVolume:
  225. case SFXChannelConeOutsideVolume:
  226. setRange( 0.f, 1.0f );
  227. break;
  228. case SFXChannelConeInsideAngle:
  229. case SFXChannelConeOutsideAngle:
  230. setRange( 0.f, 360.f );
  231. break;
  232. case SFXChannelPitch:
  233. case SFXChannelMinDistance:
  234. case SFXChannelMaxDistance:
  235. case SFXChannelCursor:
  236. setRange( 0.f, TypeTraits< F32 >::MAX );
  237. break;
  238. case SFXChannelStatus:
  239. setRange( F32( SFXStatusPlaying ), F32( SFXStatusStopped ) );
  240. break;
  241. default:
  242. setRange( TypeTraits< F32 >::MIN, TypeTraits< F32 >::MAX );
  243. break;
  244. }
  245. // If the range setting did not result in the value already
  246. // being changed, fire a value-change signal now so that sources
  247. // can catch on to the new semantics. Unfortunately, we can't
  248. // do something about the old semantic's value having been
  249. // changed by us.
  250. if( mValue == value )
  251. mEventSignal.trigger( this, SFXParameterEvent_ValueChanged );
  252. }
  253. //-----------------------------------------------------------------------------
  254. SFXParameter* SFXParameter::find( StringTableEntry name )
  255. {
  256. return dynamic_cast< SFXParameter* >(
  257. Sim::getSFXParameterGroup()->findObjectByInternalName( name )
  258. );
  259. }
  260. //=============================================================================
  261. // Console Methods.
  262. //=============================================================================
  263. // MARK: ---- Console Methods ----
  264. //-----------------------------------------------------------------------------
  265. DefineEngineMethod( SFXParameter, getParameterName, String, (),,
  266. "Get the name of the parameter.\n"
  267. "@return The paramete name." )
  268. {
  269. return object->getInternalName();
  270. }
  271. //-----------------------------------------------------------------------------
  272. DefineEngineMethod( SFXParameter, reset, void, (),,
  273. "Reset the parameter's value to its default.\n"
  274. "@see SFXParameter::defaultValue\n" )
  275. {
  276. object->reset();
  277. }