sfxParameter.cpp 12 KB

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