Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2751 lines
81 KiB

  1. //===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose: Main control for any streaming sound output device.
  4. //
  5. //===========================================================================//
  6. #include "audio_pch.h"
  7. #include "const.h"
  8. #include "cdll_int.h"
  9. #include "sound.h"
  10. #include "client_class.h"
  11. #include "icliententitylist.h"
  12. #include "con_nprint.h"
  13. #include "tier0/icommandline.h"
  14. #include "vox_private.h"
  15. #include "../../traceinit.h"
  16. #include "../../cmd.h"
  17. #include "toolframework/itoolframework.h"
  18. #include "vstdlib/random.h"
  19. #include "vstdlib/jobthread.h"
  20. #include "vaudio/ivaudio.h"
  21. #include "../../client.h"
  22. #include "../../cl_main.h"
  23. #include "utldict.h"
  24. #include "mempool.h"
  25. #include "../../enginetrace.h" // for traceline
  26. #include "../../public/bspflags.h" // for traceline
  27. #include "../../public/gametrace.h" // for traceline
  28. #include "vphysics_interface.h" // for surface props
  29. #include "../../ispatialpartitioninternal.h" // for entity enumerator
  30. #include "../../debugoverlay.h"
  31. #include "icliententity.h"
  32. #include "../../cmodel_engine.h"
  33. #include "../../staticpropmgr.h"
  34. #include "../../server.h"
  35. #include "edict.h"
  36. #include "../../pure_server.h"
  37. #include "filesystem/IQueuedLoader.h"
  38. #include "voice.h"
  39. #include "snd_dma.h"
  40. #include "snd_mixgroups.h"
  41. #if defined( _X360 )
  42. #include "xbox/xbox_console.h"
  43. #include "xmp.h"
  44. #include "avi/ibik.h"
  45. extern IBik *bik;
  46. #endif
  47. // memdbgon must be the last include file in a .cpp file!!!
  48. #include "tier0/memdbgon.h"
  49. extern ConVar dsp_volume;
  50. // extern ConVar volume;
  51. extern ConVar snd_duckerattacktime;
  52. extern ConVar snd_duckerreleasetime;
  53. //--------------------------------------------------------------------------------------------------------------
  54. // sound mixers
  55. int g_csoundmixers = 0; // total number of soundmixers found
  56. int g_cgrouprules = 0; // total number of group rules found
  57. int g_cgroupclass = 0;
  58. int g_cmixlayers = 0;
  59. // these are temporary until I can put them into the mixer proper
  60. ConVar snd_mixerMasterLevel("snd_mixer_master_level", "1.0", FCVAR_CHEAT );
  61. ConVar snd_mixerMasterDSP("snd_mixer_master_dsp", "1.0", FCVAR_CHEAT );
  62. ConVar snd_showclassname("snd_showclassname", "0", FCVAR_CHEAT ); // if 1, show classname of ent making sound
  63. // if 2, show all mixgroup matches
  64. // if 3, show all mixgroup matches with current soundmixer for ent
  65. ConVar snd_list( "snd_list", "", FCVAR_CHEAT ); // lists all sounds played that match substring filter arg. "" = none, "*" = all
  66. ConVar snd_showmixer("snd_showmixer", "0", FCVAR_CHEAT ); // set to 1 to show mixer every frame
  67. static ConVar snd_disable_mixer_solo("snd_disable_mixer_solo", "0", FCVAR_CHEAT ); // for finding soloing bugs
  68. //------------------------------------------------------------------------------
  69. //
  70. // Sound Mixers
  71. //
  72. // Sound mixers are referenced by name from Soundscapes, and are used to provide
  73. // custom volume control over various sound categories, called 'mix groups'
  74. //
  75. // see scripts/soundmixers.txt for data format
  76. //------------------------------------------------------------------------------
  77. #define CMXRGROUPMAX 128 // up to n mixgroups
  78. #define CMXRGROUPRULESMAX (CMXRGROUPMAX + 16) // max number of group rules
  79. #define CMXRSOUNDMIXERSMAX 32 // up to n sound mixers per project
  80. #define CMXRMIXLAYERSMAX 16 // up to n mix layers per project
  81. // mix groups - these equivalent to submixes on an audio mixer
  82. #define CMXRMATCHMAX 8
  83. // list of rules for determining sound membership in mix groups.
  84. // All conditions which are not null are ANDed together
  85. #define CMXRCLASSMAX 16
  86. #define CMXRNAMEMAX 32
  87. struct classlistelem_t
  88. {
  89. char szclassname[CMXRNAMEMAX]; // name of entities' class, such as CAI_BaseNPC or CHL2_Player
  90. };
  91. struct grouprule_t
  92. {
  93. char szmixgroup[CMXRNAMEMAX]; // mix group name
  94. int mixgroupid; // mix group unique id
  95. char szdir[CMXRNAMEMAX]; // substring to search for in ch->sfx
  96. int classId; // index of classname
  97. int chantype; // channel type (CHAN_WEAPON, etc)
  98. int soundlevel_min; // min soundlevel
  99. int soundlevel_max; // max soundlevel
  100. int priority; // 0..100 higher priority sound groups duck all lower pri groups if enabled
  101. short is_ducked; // if 1, sound group is ducked by all higher priority 'causes_duck" sounds
  102. short is_voice;
  103. int causes_ducking; // if 1, sound group ducks other 'is_ducked' sounds of lower priority
  104. float duck_target_pct; // if sound group is ducked, target percent of original volume
  105. float total_vol; // total volume of all sounds in this group, if group can cause ducking
  106. float ducker_threshold; // ducking is caused by this group if total_vol > ducker_threshold
  107. float trigger_vol; // total volume of all sounds in this group, use for triggers
  108. // and causes_ducking is enabled.
  109. float duck_target_vol; // target volume while ducking
  110. float duck_ramp_val; // current value of ramp - moves towards duck_target_vol
  111. };
  112. // sound mixer
  113. struct soundmixer_t
  114. {
  115. float mixAmount;
  116. char szsoundmixer[CMXRNAMEMAX]; // name of this soundmixer
  117. float ALIGN128 mapMixgroupidToVolume[CMXRGROUPMAX]; // sparse array of mix group volume values for this soundmixer
  118. float ALIGN128 mapMixgroupidToLevel[CMXRGROUPMAX]; // sparse array of mix group volume values for this soundmixer
  119. float ALIGN128 mapMixgroupidToDsp[CMXRGROUPMAX]; // sparse array of mix group volume values for this soundmixer
  120. float ALIGN128 mapMixgroupidToSolo[CMXRGROUPMAX]; // sparse array of mix group solo values for this soundmixer
  121. float ALIGN128 mapMixgroupidToMute[CMXRGROUPMAX]; // sparse array of mix group mute values for this soundmixer
  122. };
  123. #define CMXRTRIGGEREDLAYERMAX 16
  124. /*struct layertrigger_t
  125. {
  126. int cmixlayers; // number of layers
  127. int imixlayer[CMXRTRIGGEREDLAYERMAX]; // triggering mix group
  128. float fthreshold[CMXRTRIGGEREDLAYERMAX]; //
  129. float fmixamount[CMXRTRIGGEREDLAYERMAX]; //
  130. float fattack[CMXRTRIGGEREDLAYERMAX]; //
  131. float frelease[CMXRTRIGGEREDLAYERMAX]; //
  132. };*/
  133. struct layertrigger_t
  134. {
  135. bool bhastrigger;
  136. bool bistrigger[CMXRGROUPMAX];
  137. float fthreshold[CMXRGROUPMAX]; //
  138. float fmixamount[CMXRGROUPMAX]; //
  139. float fattack[CMXRGROUPMAX]; //
  140. float frelease[CMXRGROUPMAX]; //
  141. };
  142. float g_soloActive = 0.0; // are any soundmixers solo'd?
  143. int g_mapMixgroupidToGrouprulesid[CMXRGROUPMAX]; // map mixgroupid (one per unique group name)
  144. // back to 1st entry of this name in g_grouprules
  145. // sound mixer globals
  146. classlistelem_t g_groupclasslist[CMXRCLASSMAX];
  147. soundmixer_t g_soundmixers[CMXRSOUNDMIXERSMAX]; // all sound mixers
  148. soundmixer_t g_mixlayers[CMXRMIXLAYERSMAX]; // all mix layers
  149. soundmixer_t g_mastermixlayer;
  150. grouprule_t g_grouprules[CMXRGROUPRULESMAX]; // all rules for determining mix group membership
  151. layertrigger_t g_layertriggers[CMXRMIXLAYERSMAX]; //
  152. // set current soundmixer index g_isoundmixer, search for match in soundmixers
  153. // Only change current soundmixer if new name is different from current name.
  154. int g_isoundmixer = -1; // index of current sound mixer
  155. char g_szsoundmixer_cur[64]; // current soundmixer name
  156. ConVar snd_soundmixer("snd_soundmixer", "Default_Mix"); // current soundmixer name
  157. void MXR_SetCurrentSoundMixer( const char *szsoundmixer )
  158. {
  159. // if soundmixer name is not different from current name, return
  160. if ( !Q_stricmp(szsoundmixer, g_szsoundmixer_cur) )
  161. {
  162. return;
  163. }
  164. for (int i = 0; i < g_csoundmixers; i++)
  165. {
  166. if ( !Q_stricmp(g_soundmixers[i].szsoundmixer, szsoundmixer) )
  167. {
  168. g_isoundmixer = i;
  169. // save new current sound mixer name
  170. V_strcpy_safe(g_szsoundmixer_cur, szsoundmixer);
  171. return;
  172. }
  173. }
  174. }
  175. static void MXR_AccumulateMasterMixLayer(void)
  176. {
  177. // we are doing a weighted average in the case of multiple entries,
  178. // but we only want it averaged by the number of entries per mix group
  179. // if there is no mix group entry the defaults give zero difference
  180. // this whole thing could be optimized for memory but it's insignificant
  181. float totalAmount[CMXRGROUPMAX];
  182. for (int j = 0; j < CMXRGROUPMAX; j++)
  183. {
  184. // defaults
  185. g_mastermixlayer.mapMixgroupidToVolume[j] = 1.0;
  186. g_mastermixlayer.mapMixgroupidToLevel[j] = 1.0;
  187. g_mastermixlayer.mapMixgroupidToDsp[j] = 1.0;
  188. g_mastermixlayer.mapMixgroupidToSolo[j] = 0.0;
  189. g_mastermixlayer.mapMixgroupidToMute[j] = 0.0;
  190. totalAmount[j] = 0.0;
  191. for (int i = 0; i < CMXRMIXLAYERSMAX; i++)
  192. {
  193. // mix layers
  194. soundmixer_t *pmixlayer = &g_mixlayers[i];
  195. // volume entry < 0 = no entry
  196. if(pmixlayer->mapMixgroupidToVolume[j] >= 0.0)
  197. totalAmount[j] += pmixlayer->mixAmount;
  198. }
  199. }
  200. // using the accumulated "amounts" we can do a weighted average of the actual layer values
  201. for (int i = 0; i < CMXRMIXLAYERSMAX; i++)
  202. {
  203. // mix layers
  204. soundmixer_t *pmixlayer = &g_mixlayers[i];
  205. for (int j = 0; j < CMXRGROUPMAX; j++)
  206. {
  207. if(!(totalAmount[j] > 0.0))
  208. continue;
  209. float amount = pmixlayer->mixAmount * (pmixlayer->mixAmount / totalAmount[j]);
  210. // -1 entry volume = no entry
  211. if(pmixlayer->mapMixgroupidToVolume[j] < 0.0)
  212. continue;
  213. g_mastermixlayer.mapMixgroupidToVolume[j] = clamp(g_mastermixlayer.mapMixgroupidToVolume[j] + ((pmixlayer->mapMixgroupidToVolume[j] - 1.0) * amount), 0.0, 1.0);
  214. g_mastermixlayer.mapMixgroupidToLevel[j] = clamp(g_mastermixlayer.mapMixgroupidToLevel[j] + ((pmixlayer->mapMixgroupidToLevel[j] - 1.0) * amount), 0.0, 1.0);
  215. g_mastermixlayer.mapMixgroupidToDsp[j] = clamp(g_mastermixlayer.mapMixgroupidToDsp[j] + ((pmixlayer->mapMixgroupidToDsp[j] - 1.0) * amount), 0.0, 1.0);
  216. g_mastermixlayer.mapMixgroupidToSolo[j] = clamp(g_mastermixlayer.mapMixgroupidToSolo[j] + (pmixlayer->mapMixgroupidToSolo[j] * amount), 0.0, 1.0);
  217. g_mastermixlayer.mapMixgroupidToMute[j] = clamp(g_mastermixlayer.mapMixgroupidToMute[j] + (pmixlayer->mapMixgroupidToMute[j] * amount), 0.0, 1.0);
  218. }
  219. }
  220. }
  221. #if defined( _X360 )
  222. // 360 SIMD version of function above.
  223. // could be squeezed a little more with some hardcoded registers.
  224. static void MXR_AccumulateMasterMixLayerVMX(void)
  225. {
  226. // we are doing a weighted average in the case of multiple entries,
  227. // but we only want it averaged by the number of entries per mix group
  228. // if there is no mix group entry the defaults give zero difference.
  229. // make sure these constants are divisible by sixteen floats (the optimizations
  230. // below depend on that, but they can be rewritten if necessary)
  231. COMPILE_TIME_ASSERT( CMXRGROUPMAX % 16 == 0 );
  232. COMPILE_TIME_ASSERT( CMXRMIXLAYERSMAX % 16 == 0 );
  233. // g_mastermixlayer must be simd-aligned.
  234. AssertMsg( (((unsigned int)((char *)g_mastermixlayer.mapMixgroupidToVolume)) & 0x7F) == 0 &&
  235. (((unsigned int)((char *)g_mastermixlayer.mapMixgroupidToLevel)) & 0x7F) == 0 &&
  236. (((unsigned int)((char *)g_mastermixlayer.mapMixgroupidToDsp)) & 0x7F) == 0 &&
  237. (((unsigned int)((char *)g_mastermixlayer.mapMixgroupidToSolo)) & 0x7F) == 0 &&
  238. (((unsigned int)((char *)g_mastermixlayer.mapMixgroupidToMute)) & 0x7F) == 0,
  239. "Float vectors in g_mastermixlayer not 128-byte aligned!" );
  240. // // Initialize cache.
  241. // The results will be written into the g_mastermixlayer array; first, zero it out
  242. // to haul it into cache, then initialize.
  243. // We do the first output layer here, but the rest are interleaved with the
  244. // layer total weight computation below (to spread out the memory bandwidth)
  245. //for ( int mixgroup = 0 ; mixgroup < CMXRGROUPMAX ; mixgroup += 16 )
  246. {
  247. const int mixgroup = 0;
  248. // use dcbz, which brings memory into cache,
  249. // but also zeroes it out -- that way you don't
  250. // have to wait for it to actually load; you
  251. // just mark it dirty.
  252. // __dcbz128( int offset, void * base );
  253. __dcbz128( 0, g_mastermixlayer.mapMixgroupidToVolume + mixgroup );
  254. __dcbz128( 0, g_mastermixlayer.mapMixgroupidToLevel + mixgroup );
  255. __dcbz128( 0, g_mastermixlayer.mapMixgroupidToDsp + mixgroup );
  256. __dcbz128( 0, g_mastermixlayer.mapMixgroupidToSolo + mixgroup );
  257. __dcbz128( 0, g_mastermixlayer.mapMixgroupidToMute + mixgroup );
  258. }
  259. const fltx4 Ones = Four_Ones;
  260. const fltx4 Zeroes = Four_Zeros;
  261. float ALIGN128 totalAmount[CMXRGROUPMAX];
  262. // // compute the total weights for each mix layer.
  263. // we do one loop by hand, and then iterate the rest
  264. // int layer = 0 -- implicit initialization of total to zero
  265. {
  266. soundmixer_t * RESTRICT pmixlayer = &g_mixlayers[ 0 ];
  267. soundmixer_t * RESTRICT pNextLayer = &g_mixlayers[ 1 ]; // prefetch the next layer
  268. AssertMsg( (((unsigned int) ((char *)(&pmixlayer->mapMixgroupidToVolume))) & 0x07) == 0,
  269. "Float members of mixlayers are not SIMD-aligned." );
  270. fltx4 mixAmount = ReplicateX4(&pmixlayer->mixAmount); // pull the mixamount into all four of a fltx4
  271. for ( int group = 0 ; group < CMXRGROUPMAX ; group+=4 ) // do groups four at a time
  272. {
  273. if ( (group & 0x07) == 0 )
  274. {
  275. __dcbt( 0, &pNextLayer->mapMixgroupidToVolume[group] );
  276. // blow out the output vectors
  277. __dcbz128( 0, g_mastermixlayer.mapMixgroupidToVolume + group );
  278. __dcbz128( 0, g_mastermixlayer.mapMixgroupidToLevel + group );
  279. __dcbz128( 0, g_mastermixlayer.mapMixgroupidToDsp + group );
  280. __dcbz128( 0, g_mastermixlayer.mapMixgroupidToSolo + group );
  281. __dcbz128( 0, g_mastermixlayer.mapMixgroupidToMute + group );
  282. }
  283. fltx4 mgidToVolume = LoadAlignedSIMD( &pmixlayer->mapMixgroupidToVolume[group] );
  284. fltx4 total;
  285. fltx4 mgidToVolumeIsGreaterThanZero = CmpGeSIMD( mgidToVolume, Zeroes );
  286. // if greater than zero, accumulate the mixamount into TotalAmount.
  287. // volume entry < 0 means do not accumulate.
  288. total = MaskedAssign( mgidToVolumeIsGreaterThanZero, // if vol >=0 ..
  289. mixAmount, // total = mixamount
  290. Zeroes ); // total = 0
  291. // save total back out
  292. StoreAlignedSIMD( totalAmount+group, total );
  293. }
  294. }
  295. for ( int layer = 1; layer < CMXRMIXLAYERSMAX ; layer++ )
  296. {
  297. soundmixer_t * RESTRICT pmixlayer = &g_mixlayers[ layer ];
  298. soundmixer_t * RESTRICT pNextLayer = &g_mixlayers[ layer+1 ]; // prefetch the next layer
  299. const fltx4 mixAmount = ReplicateX4(&pmixlayer->mixAmount); // pull the mixamount into all four of a fltx4
  300. for ( int group = 0 ; group < CMXRGROUPMAX ; group+=16 ) // do groups SIXTEEN at a time (to hide the long VMX pipeline)
  301. {
  302. __dcbt( 0, &pNextLayer->mapMixgroupidToVolume[group] ); // prefetch volumes for next layer
  303. // unroll the loop a little by hand. This is different from iterating because
  304. // an explicitly unrolled loop like this will put each element of mgidToVolume[4]
  305. // on its own register, rather than in an array; that way we can have four operations
  306. // in flight simultaneously.
  307. fltx4 mgidToVolume[4];
  308. mgidToVolume[0] = LoadAlignedSIMD( &pmixlayer->mapMixgroupidToVolume[group + 0] ); // each of these
  309. mgidToVolume[1] = LoadAlignedSIMD( &pmixlayer->mapMixgroupidToVolume[group + 4] ); // loads takes
  310. mgidToVolume[2] = LoadAlignedSIMD( &pmixlayer->mapMixgroupidToVolume[group + 8] ); // about 12 ops
  311. mgidToVolume[3] = LoadAlignedSIMD( &pmixlayer->mapMixgroupidToVolume[group + 12] ); // to finish.
  312. fltx4 total[4];
  313. total[0] = LoadAlignedSIMD( totalAmount + group + 0 );
  314. total[1] = LoadAlignedSIMD( totalAmount + group + 4 );
  315. total[2] = LoadAlignedSIMD( totalAmount + group + 8 );
  316. total[3] = LoadAlignedSIMD( totalAmount + group + 12 );
  317. // volume entry < 0 means no entry.
  318. fltx4 mgidToVolumeIsGreaterThanZero[4];
  319. mgidToVolumeIsGreaterThanZero[0] = CmpGeSIMD( mgidToVolume[0], Zeroes ); // four bools.
  320. mgidToVolumeIsGreaterThanZero[1] = CmpGeSIMD( mgidToVolume[1], Zeroes );
  321. mgidToVolumeIsGreaterThanZero[2] = CmpGeSIMD( mgidToVolume[2], Zeroes );
  322. mgidToVolumeIsGreaterThanZero[3] = CmpGeSIMD( mgidToVolume[3], Zeroes );
  323. // if greater than zero, accumulate the mixamount into TotalAmount.
  324. // volume entry < 0 means do not accumulate.
  325. total[0] = MaskedAssign( mgidToVolumeIsGreaterThanZero[0], // if vol >=0 ..
  326. AddSIMD( total[0], mixAmount ), // total+= mixamount
  327. total[0] ); // total = total
  328. total[1] = MaskedAssign( mgidToVolumeIsGreaterThanZero[1], // if vol >=0 ..
  329. AddSIMD( total[1], mixAmount ), // total+= mixamount
  330. total[1] ); // total = total
  331. total[2] = MaskedAssign( mgidToVolumeIsGreaterThanZero[2], // if vol >=0 ..
  332. AddSIMD( total[2], mixAmount ), // total+= mixamount
  333. total[2] ); // total = total
  334. total[3] = MaskedAssign( mgidToVolumeIsGreaterThanZero[3], // if vol >=0 ..
  335. AddSIMD( total[3], mixAmount ), // total+= mixamount
  336. total[3] ); // total = total
  337. // save total back out
  338. StoreAlignedSIMD( totalAmount + group + 0, total[0] );
  339. StoreAlignedSIMD( totalAmount + group + 4, total[1] );
  340. StoreAlignedSIMD( totalAmount + group + 8, total[2] );
  341. StoreAlignedSIMD( totalAmount + group + 12, total[3] );
  342. }
  343. }
  344. // // using the accumulated "amounts" we can do a weighted average of the actual layer values
  345. // first compute reciprocals of all the weights. It's okay to divide by zero -- in this case
  346. // we'll replace the reciprocal with -1, to indicate that this group should be skipped.
  347. // we work the groups four at a time, or the individual floats sixteen at a time, because
  348. // of the latency of the reciprocal operation.
  349. fltx4 totalAmountRecip4s[CMXRGROUPMAX / 4];
  350. for ( int i = 0 ; i < (CMXRGROUPMAX / 4) ; i += 4 )
  351. {
  352. fltx4 total[4];
  353. const int iTimesFour = i << 2; // shift is cheap
  354. total[0] = LoadAlignedSIMD( totalAmount + iTimesFour + 0 );
  355. total[1] = LoadAlignedSIMD( totalAmount + iTimesFour + 4 );
  356. total[2] = LoadAlignedSIMD( totalAmount + iTimesFour + 8 );
  357. total[3] = LoadAlignedSIMD( totalAmount + iTimesFour + 12 );
  358. fltx4 totalRecip[4];
  359. totalRecip[0] = ReciprocalSIMD( total[0] );
  360. totalRecip[1] = ReciprocalSIMD( total[1] );
  361. totalRecip[2] = ReciprocalSIMD( total[2] );
  362. totalRecip[3] = ReciprocalSIMD( total[3] );
  363. fltx4 totalIsGreaterThanZero[4];
  364. totalIsGreaterThanZero[0] = CmpGtSIMD( total[0], Zeroes );
  365. totalIsGreaterThanZero[1] = CmpGtSIMD( total[1], Zeroes );
  366. totalIsGreaterThanZero[2] = CmpGtSIMD( total[2], Zeroes );
  367. totalIsGreaterThanZero[3] = CmpGtSIMD( total[3], Zeroes );
  368. totalAmountRecip4s[ i + 0 ] = MaskedAssign( totalIsGreaterThanZero[0], totalRecip[0], Four_NegativeOnes );
  369. totalAmountRecip4s[ i + 1 ] = MaskedAssign( totalIsGreaterThanZero[1], totalRecip[1], Four_NegativeOnes );
  370. totalAmountRecip4s[ i + 2 ] = MaskedAssign( totalIsGreaterThanZero[2], totalRecip[2], Four_NegativeOnes );
  371. totalAmountRecip4s[ i + 3 ] = MaskedAssign( totalIsGreaterThanZero[3], totalRecip[3], Four_NegativeOnes );
  372. // oh, and meanwhile, prefetch the first mix layer that we're going to process below.
  373. soundmixer_t * RESTRICT pnextlayer = &g_mixlayers[0];
  374. unsigned int offset = sizeof(float) * iTimesFour;
  375. // __dcbt( 0, pnextlayer->mapMixgroupidToVolume + iTimesFour ); // volumes are already in cache
  376. __dcbt( offset, pnextlayer->mapMixgroupidToLevel );
  377. __dcbt( offset, pnextlayer->mapMixgroupidToDsp );
  378. __dcbt( offset, pnextlayer->mapMixgroupidToSolo );
  379. __dcbt( offset, pnextlayer->mapMixgroupidToMute );
  380. }
  381. // // with the reciprocals computed, now work out the mix levels.
  382. // We cook the groups four at a time.
  383. // do one loop to write in the initialized default values ..
  384. {
  385. const int layer = 0;
  386. // mix layers
  387. soundmixer_t * RESTRICT pmixlayer = &g_mixlayers[layer];
  388. fltx4 mixLayerAmountSq = ReplicateX4( pmixlayer->mixAmount ); // pmixplayer->mixAmount * pmixplayer->mixAmount
  389. mixLayerAmountSq = MulSIMD( mixLayerAmountSq, mixLayerAmountSq );
  390. // prefetch the groups for the next layer
  391. soundmixer_t * RESTRICT pnextlayer = &g_mixlayers[layer+1];
  392. for ( unsigned int group = 0; group < CMXRGROUPMAX; group+=4 )
  393. {
  394. // once per 16 floats (128 bytes)...
  395. if ((group & 0x07) == 0)
  396. {
  397. // __dcbt( 0, pnextlayer->mapMixgroupidToVolume + group ); // volumes have already been fetched
  398. unsigned int offset = sizeof(float) * group;
  399. __dcbt( offset, pnextlayer->mapMixgroupidToLevel );
  400. __dcbt( offset, pnextlayer->mapMixgroupidToDsp );
  401. __dcbt( offset, pnextlayer->mapMixgroupidToSolo );
  402. __dcbt( offset, pnextlayer->mapMixgroupidToMute );
  403. }
  404. // we only write groups where the total weight > 0
  405. // and pmixlayer->mapMixGroupidToVolume[n] >= 0
  406. fltx4 gidToVolume = LoadAlignedSIMD( pmixlayer->mapMixgroupidToVolume + group );
  407. fltx4 bShouldTouch;
  408. const unsigned int groupDivFour = group >> 2;
  409. bShouldTouch = CmpGtSIMD( totalAmountRecip4s[groupDivFour], Zeroes );
  410. fltx4 amount = MulSIMD( mixLayerAmountSq, totalAmountRecip4s[groupDivFour] ); // pmixplayer->mixAmount * pmixplayer->mixAmount / totalAmount[group]
  411. bShouldTouch = AndSIMD( bShouldTouch, CmpGeSIMD( gidToVolume, Zeroes ) ); //bShouldTouch[x] = totalAmountRecip4s[x] > 0 && gidToVolume[x] >=0
  412. fltx4 gidToLevel = LoadAlignedSIMD( pmixlayer->mapMixgroupidToLevel + group );
  413. fltx4 gidToDsp = LoadAlignedSIMD( pmixlayer->mapMixgroupidToDsp + group );
  414. fltx4 gidToSolo = LoadAlignedSIMD( pmixlayer->mapMixgroupidToSolo + group );
  415. fltx4 gidToMute = LoadAlignedSIMD( pmixlayer->mapMixgroupidToMute + group );
  416. // the master mix values, which are also the output.
  417. // start with the defaults.
  418. fltx4 mastergidToVolume = Ones;
  419. fltx4 mastergidToLevel = Ones;
  420. fltx4 mastergidToDsp = Ones;
  421. fltx4 mastergidToSolo = Zeroes;
  422. fltx4 mastergidToMute = Zeroes;
  423. gidToVolume = SubSIMD(gidToVolume, Ones); // pmixlayer->mapMixgroupidToVolume[j] - 1.0f
  424. gidToLevel = SubSIMD(gidToLevel, Ones); // pmixlayer->mapMixgroupidToLevel[j] - 1.0f
  425. gidToDsp = SubSIMD(gidToDsp, Ones); // pmixlayer->mapMixgroupidToDsp[j] - 1.0f
  426. // let the subs cook for a little bit...
  427. gidToSolo = MaddSIMD( amount, gidToSolo, mastergidToSolo ); // pmixlayer->mapMixgroupidToSolo[j] * amount + g_mastermixlayer.mapMixgroupidToSolo[j]
  428. gidToMute = MaddSIMD( amount, gidToMute, mastergidToMute );
  429. gidToVolume = MaddSIMD( amount, gidToVolume, mastergidToVolume );
  430. gidToLevel = MaddSIMD( amount, gidToLevel, mastergidToLevel );
  431. gidToDsp = MaddSIMD( amount, gidToDsp, mastergidToDsp );
  432. // clamp to between zero and one
  433. gidToSolo = ClampVectorSIMD( gidToSolo, Zeroes, Ones );
  434. gidToMute = ClampVectorSIMD( gidToMute, Zeroes, Ones );
  435. gidToVolume = ClampVectorSIMD( gidToVolume, Zeroes, Ones );
  436. gidToLevel = ClampVectorSIMD( gidToLevel, Zeroes, Ones );
  437. gidToDsp = ClampVectorSIMD( gidToDsp, Zeroes, Ones );
  438. // write out the appropriate groups
  439. mastergidToSolo = MaskedAssign( bShouldTouch, gidToSolo, mastergidToSolo );
  440. mastergidToMute = MaskedAssign( bShouldTouch, gidToMute, mastergidToMute );
  441. mastergidToVolume = MaskedAssign( bShouldTouch, gidToVolume, mastergidToVolume );
  442. mastergidToLevel = MaskedAssign( bShouldTouch, gidToLevel, mastergidToLevel );
  443. mastergidToDsp = MaskedAssign( bShouldTouch, gidToDsp, mastergidToDsp );
  444. StoreAlignedSIMD( g_mastermixlayer.mapMixgroupidToSolo + group, mastergidToSolo );
  445. StoreAlignedSIMD( g_mastermixlayer.mapMixgroupidToMute + group, mastergidToMute );
  446. StoreAlignedSIMD( g_mastermixlayer.mapMixgroupidToVolume + group, mastergidToVolume );
  447. StoreAlignedSIMD( g_mastermixlayer.mapMixgroupidToLevel + group, mastergidToLevel );
  448. StoreAlignedSIMD( g_mastermixlayer.mapMixgroupidToDsp + group, mastergidToDsp );
  449. }
  450. }
  451. // iterate over the remaining layers.
  452. for ( int layer = 1; layer < CMXRMIXLAYERSMAX; layer++ )
  453. {
  454. // mix layers
  455. soundmixer_t * RESTRICT pmixlayer = &g_mixlayers[layer];
  456. fltx4 mixLayerAmountSq = ReplicateX4( &pmixlayer->mixAmount ); // pmixplayer->mixAmount * pmixplayer->mixAmount
  457. mixLayerAmountSq = MulSIMD( mixLayerAmountSq, mixLayerAmountSq );
  458. // prefetch the groups for the next layer
  459. soundmixer_t * RESTRICT pnextlayer = &g_mixlayers[layer+1];
  460. for ( unsigned int group = 0; group < CMXRGROUPMAX; group+=4 )
  461. {
  462. // once per 16 floats (128 bytes)...
  463. if ((group & 0x07) == 0)
  464. {
  465. // __dcbt( 0, pnextlayer->mapMixgroupidToVolume + group ); // volumes already fetched.
  466. unsigned int offset = group * sizeof(float);
  467. __dcbt( offset, pnextlayer->mapMixgroupidToLevel );
  468. __dcbt( offset, pnextlayer->mapMixgroupidToDsp );
  469. __dcbt( offset, pnextlayer->mapMixgroupidToSolo );
  470. __dcbt( offset, pnextlayer->mapMixgroupidToMute );
  471. }
  472. // we only write groups where the total weight > 0
  473. // and pmixlayer->mapMixGroupidToVolume[n] >= 0
  474. fltx4 gidToVolume = LoadAlignedSIMD( pmixlayer->mapMixgroupidToVolume + group );
  475. fltx4 bShouldTouch;
  476. const unsigned int groupDivFour = group >> 2;
  477. bShouldTouch = CmpGtSIMD( totalAmountRecip4s[groupDivFour], Zeroes );
  478. fltx4 amount = MulSIMD( mixLayerAmountSq, totalAmountRecip4s[groupDivFour] ); // pmixplayer->mixAmount * pmixplayer->mixAmount / totalAmount[group]
  479. bShouldTouch = AndSIMD( bShouldTouch, CmpGeSIMD( gidToVolume, Zeroes ) ); //bShouldTouch[x] = totalAmountRecip4s[x] > 0 && gidToVolume[x] >=0
  480. fltx4 gidToLevel = LoadAlignedSIMD( pmixlayer->mapMixgroupidToLevel + group );
  481. fltx4 gidToDsp = LoadAlignedSIMD( pmixlayer->mapMixgroupidToDsp + group );
  482. fltx4 gidToSolo = LoadAlignedSIMD( pmixlayer->mapMixgroupidToSolo + group );
  483. fltx4 gidToMute = LoadAlignedSIMD( pmixlayer->mapMixgroupidToMute + group );
  484. // the master mix values, which are also the output
  485. fltx4 mastergidToVolume = LoadAlignedSIMD( g_mastermixlayer.mapMixgroupidToVolume + group );
  486. fltx4 mastergidToLevel = LoadAlignedSIMD( g_mastermixlayer.mapMixgroupidToLevel + group );
  487. fltx4 mastergidToDsp = LoadAlignedSIMD( g_mastermixlayer.mapMixgroupidToDsp + group );
  488. fltx4 mastergidToSolo = LoadAlignedSIMD( g_mastermixlayer.mapMixgroupidToSolo + group );
  489. fltx4 mastergidToMute = LoadAlignedSIMD( g_mastermixlayer.mapMixgroupidToMute + group );
  490. gidToVolume = SubSIMD(gidToVolume, Ones); // pmixlayer->mapMixgroupidToVolume[j] - 1.0f
  491. gidToLevel = SubSIMD(gidToLevel, Ones); // pmixlayer->mapMixgroupidToLevel[j] - 1.0f
  492. gidToDsp = SubSIMD(gidToDsp, Ones); // pmixlayer->mapMixgroupidToDsp[j] - 1.0f
  493. // let the subs cook for a little bit...
  494. gidToSolo = MaddSIMD( amount, gidToSolo, mastergidToSolo ); // pmixlayer->mapMixgroupidToSolo[j] * amount + g_mastermixlayer.mapMixgroupidToSolo[j]
  495. gidToMute = MaddSIMD( amount, gidToMute, mastergidToMute );
  496. gidToVolume = MaddSIMD( amount, gidToVolume, mastergidToVolume );
  497. gidToLevel = MaddSIMD( amount, gidToLevel, mastergidToLevel );
  498. gidToDsp = MaddSIMD( amount, gidToDsp, mastergidToDsp );
  499. // clamp to between zero and one
  500. gidToSolo = ClampVectorSIMD( gidToSolo, Zeroes, Ones );
  501. gidToMute = ClampVectorSIMD( gidToMute, Zeroes, Ones );
  502. gidToVolume = ClampVectorSIMD( gidToVolume, Zeroes, Ones );
  503. gidToLevel = ClampVectorSIMD( gidToLevel, Zeroes, Ones );
  504. gidToDsp = ClampVectorSIMD( gidToDsp, Zeroes, Ones );
  505. // write out the appropriate groups
  506. mastergidToSolo = MaskedAssign( bShouldTouch, gidToSolo, mastergidToSolo );
  507. mastergidToMute = MaskedAssign( bShouldTouch, gidToMute, mastergidToMute );
  508. mastergidToVolume = MaskedAssign( bShouldTouch, gidToVolume, mastergidToVolume );
  509. mastergidToLevel = MaskedAssign( bShouldTouch, gidToLevel, mastergidToLevel );
  510. mastergidToDsp = MaskedAssign( bShouldTouch, gidToDsp, mastergidToDsp );
  511. StoreAlignedSIMD( g_mastermixlayer.mapMixgroupidToSolo + group, mastergidToSolo );
  512. StoreAlignedSIMD( g_mastermixlayer.mapMixgroupidToMute + group, mastergidToMute );
  513. StoreAlignedSIMD( g_mastermixlayer.mapMixgroupidToVolume + group, mastergidToVolume );
  514. StoreAlignedSIMD( g_mastermixlayer.mapMixgroupidToLevel + group, mastergidToLevel );
  515. StoreAlignedSIMD( g_mastermixlayer.mapMixgroupidToDsp + group, mastergidToDsp );
  516. }
  517. }
  518. }
  519. ConVar snd_use_vmx("snd_use_vmx", "1");
  520. #endif
  521. // Check in advance if ANY groups are solo'd
  522. void MXR_SetSoloActive(void)
  523. {
  524. #ifdef _X360
  525. if (snd_use_vmx.GetBool())
  526. MXR_AccumulateMasterMixLayerVMX();
  527. else
  528. MXR_AccumulateMasterMixLayer();
  529. #else
  530. MXR_AccumulateMasterMixLayer();
  531. #endif
  532. g_soloActive = 0.0;
  533. if ( !snd_disable_mixer_solo.GetBool() )
  534. {
  535. soundmixer_t *pmixer = &g_soundmixers[g_isoundmixer];
  536. // for every entry in mapMixgroupidToSolo which is not 0
  537. for (int i = 0; i < CMXRGROUPMAX; i++)
  538. {
  539. g_soloActive = MAX(g_soloActive, pmixer->mapMixgroupidToSolo[i]);
  540. g_soloActive = MAX(g_soloActive, g_mastermixlayer.mapMixgroupidToSolo[i]);
  541. }
  542. }
  543. }
  544. ClientClass *GetClientClass( SoundSource soundsource )
  545. {
  546. IClientEntity *pClientEntity = NULL;
  547. if ( entitylist )
  548. {
  549. pClientEntity = entitylist->GetClientEntity( soundsource );
  550. if ( pClientEntity )
  551. {
  552. ClientClass *pClientClass = pClientEntity->GetClientClass();
  553. // check npc sounds
  554. return pClientClass;
  555. }
  556. }
  557. return NULL;
  558. }
  559. // get the client class name if an entity was specified
  560. const char *GetClientClassname( SoundSource soundsource )
  561. {
  562. IClientEntity *pClientEntity = NULL;
  563. if ( entitylist )
  564. {
  565. pClientEntity = entitylist->GetClientEntity( soundsource );
  566. if ( pClientEntity )
  567. {
  568. ClientClass *pClientClass = pClientEntity->GetClientClass();
  569. // check npc sounds
  570. if ( pClientClass )
  571. {
  572. return pClientClass->GetName();
  573. }
  574. }
  575. }
  576. return NULL;
  577. }
  578. // builds a cached list of rules that match the directory name on the sound
  579. int MXR_GetMixGroupListFromDirName( const char *pDirname, byte *pList, int listMax )
  580. {
  581. // if we call this before the groups are parsed we'll get bad data
  582. Assert(g_cgrouprules>0);
  583. int count = 0;
  584. for ( int i = 0; i < listMax; i++ )
  585. {
  586. pList[i] = 255;
  587. }
  588. for ( int i = 0; i < g_cgrouprules; i++ )
  589. {
  590. grouprule_t *prule = &g_grouprules[i];
  591. if ( prule->szdir[ 0 ] && V_strstr( pDirname, prule->szdir ) )
  592. {
  593. pList[count] = i;
  594. count++;
  595. if ( count >= listMax )
  596. return count;
  597. }
  598. }
  599. return count;
  600. }
  601. bool MXR_IsMusicGroup( int ruleIndex )
  602. {
  603. if ( ruleIndex != 255 && ruleIndex >= 0 && ruleIndex < g_cgrouprules )
  604. {
  605. grouprule_t *prule = &g_grouprules[ruleIndex];
  606. if ( Q_stristr(prule->szmixgroup, "music") )
  607. return true;
  608. }
  609. return false;
  610. }
  611. // determine which mixgroups sound is in, and save those mixgroupids in sound.
  612. // use current soundmixer indicated with g_isoundmixer, and contents of g_rgpgrouprules.
  613. // Algorithm:
  614. // 1. all conditions in a row are AND conditions,
  615. // 2. all rows sharing the same groupname are OR conditions.
  616. // so - if a sound matches all conditions of a row, it is given that row's mixgroup id
  617. // if a sound doesn't match all conditions of a row, the next row is checked.
  618. // returns 0, default mixgroup if no match
  619. void MXR_GetMixGroupFromSoundsource( channel_t *pchan )
  620. {
  621. grouprule_t *prule;
  622. bool fmatch;
  623. bool classMatch[CMXRCLASSMAX];
  624. // init all mixgroups for channel
  625. for ( int i = 0; i < ARRAYSIZE( pchan->mixgroups ); i++ )
  626. {
  627. pchan->mixgroups[i] = -1;
  628. }
  629. char sndname[MAX_PATH];
  630. pchan->sfx->getname(sndname, sizeof(sndname));
  631. // Use forward slashes here
  632. Q_FixSlashes( sndname, '/' );
  633. const char *pszclassname = GetClientClassname( pchan->soundsource );
  634. if ( snd_showclassname.GetInt() == 1 && pszclassname )
  635. {
  636. // utility: show classname of ent making sound
  637. DevMsg( "(%s:%s) \n", pszclassname, sndname);
  638. }
  639. // check for player
  640. bool bIsPlayer = g_pSoundServices->IsPlayer( pchan->soundsource );
  641. for ( int i = 0; i < g_cgroupclass; i++ )
  642. {
  643. classMatch[i] = ( pszclassname && Q_stristr( pszclassname, g_groupclasslist[i].szclassname ) ||
  644. ( bIsPlayer && !Q_strcmp( g_groupclasslist[i].szclassname, "localPlayer") ) );
  645. }
  646. // check all group rules for a match, save
  647. // up to CMXRMATCHMAX matches in channel mixgroup.
  648. int cmixgroups = 0;
  649. if ( !pchan->sfx->m_bMixGroupsCached )
  650. {
  651. pchan->sfx->OnNameChanged( sndname );
  652. }
  653. // since this is a sorted list (in group rule order) we only need to test against the next matching rule
  654. // this avoids a search inside the loop
  655. int currentDirRuleIndex = 0;
  656. int currentDirRule = pchan->sfx->m_mixGroupList[0];
  657. for ( int i = 0; i < g_cgrouprules; i++)
  658. {
  659. prule = &g_grouprules[i];
  660. fmatch = true;
  661. // check directory or name substring
  662. #ifdef _DEBUG
  663. // check dir table is correct in CSfxTable cache
  664. if ( prule->szdir[ 0 ] && Q_stristr( sndname, prule->szdir ) )
  665. {
  666. Assert(currentDirRule == i);
  667. }
  668. else
  669. {
  670. Assert(currentDirRule != i);
  671. }
  672. if ( prule->classId >= 0 )
  673. {
  674. // rule has a valid class id and table is correct
  675. Assert(prule->classId < g_cgroupclass);
  676. bool bShouldBeTrue = ( pszclassname && Q_stristr(pszclassname, g_groupclasslist[prule->classId].szclassname) ) ||
  677. ( !Q_strcmp( g_groupclasslist[prule->classId].szclassname, "localPlayer" ) && bIsPlayer );
  678. if ( bShouldBeTrue )
  679. {
  680. Assert(classMatch[prule->classId] == true);
  681. }
  682. else
  683. {
  684. Assert(classMatch[prule->classId] == false);
  685. }
  686. }
  687. #endif
  688. // this is the next matching dir for this sound, no need to search
  689. // becuse the list is sorted and we visit all elements
  690. if ( currentDirRule == i )
  691. {
  692. Assert(prule->szdir[0]);
  693. currentDirRuleIndex++;
  694. currentDirRule = 255;
  695. if ( currentDirRuleIndex < pchan->sfx->m_mixGroupCount )
  696. {
  697. currentDirRule = pchan->sfx->m_mixGroupList[currentDirRuleIndex];
  698. }
  699. }
  700. else if ( prule->szdir[ 0 ] )
  701. {
  702. fmatch = false; // substring doesn't match, keep looking
  703. }
  704. // check class name
  705. if ( fmatch && prule->classId >= 0 )
  706. {
  707. fmatch = classMatch[prule->classId];
  708. }
  709. // check channel type
  710. if ( fmatch && prule->chantype >= 0 )
  711. {
  712. if ( pchan->entchannel != prule->chantype )
  713. fmatch = false; // channel type doesn't match, keep looking
  714. }
  715. float soundlevel = pchan->m_flSoundLevel;
  716. // check sndlvlmin/max
  717. if ( fmatch && prule->soundlevel_min >= 0 )
  718. {
  719. if ( soundlevel < prule->soundlevel_min )
  720. fmatch = false; // soundlevel is less than min, keep looking
  721. }
  722. if ( fmatch && prule->soundlevel_max >= 0)
  723. {
  724. if ( soundlevel > prule->soundlevel_max )
  725. fmatch = false; // soundlevel is greater than max, keep looking
  726. }
  727. if ( fmatch )
  728. {
  729. pchan->mixgroups[cmixgroups] = prule->mixgroupid;
  730. cmixgroups++;
  731. // only print the first match
  732. if ( cmixgroups == 1 )
  733. {
  734. // filtered listing of sounds
  735. const char *filter = snd_list.GetString();
  736. if ( filter[0] )
  737. {
  738. // utility: show classname of ent making sound
  739. if ( Q_stristr( sndname, filter ))
  740. {
  741. DevMsg( "%s", sndname );
  742. // show main mixgroup for this sound
  743. mixervalues_t mValues;
  744. int lastMixGroup;
  745. MXR_GetVolFromMixGroup( pchan, &mValues, &lastMixGroup );
  746. if ( prule->szmixgroup[0] )
  747. {
  748. DevMsg(" : %s : vol: %4.2f, sndlvl: %4.2f \n", prule->szmixgroup, mValues.volume, soundlevel);
  749. }
  750. }
  751. }
  752. }
  753. // a member of CMXRMATCHMAX groups?
  754. if ( cmixgroups >= CMXRMATCHMAX )
  755. return; // too many matches, stop looking
  756. }
  757. if (fmatch && snd_showclassname.GetInt() >= 2)
  758. {
  759. // show all mixgroups for this sound
  760. if (cmixgroups == 1)
  761. {
  762. DevMsg("\n%s:%s: ", g_szsoundmixer_cur, sndname);
  763. }
  764. if (prule->szmixgroup[0])
  765. {
  766. // int rgmixgroupid[CMXRMATCHMAX];
  767. // for (int i = 0; i < CMXRMATCHMAX; i++)
  768. // rgmixgroupid[i] = -1;
  769. // rgmixgroupid[0] = prule->mixgroupid;
  770. // float vol = MXR_GetVolFromMixGroup( rgmixgroupid );
  771. // DevMsg("%s(%1.2f) ", prule->szmixgroup, vol);
  772. DevMsg("%s ", prule->szmixgroup);
  773. }
  774. }
  775. }
  776. }
  777. ConVar snd_disable_mixer_duck("snd_disable_mixer_duck", "0", FCVAR_CHEAT ); // if 1, soundmixer ducking is disabled
  778. // given mix group id, return current duck volume
  779. float MXR_GetDuckVolume( int mixgroupid )
  780. {
  781. if ( snd_disable_mixer_duck.GetInt() )
  782. return 1.0;
  783. Assert ( mixgroupid < g_cgrouprules );
  784. Assert( mixgroupid >= 0 && mixgroupid < ARRAYSIZE(g_mapMixgroupidToGrouprulesid) );
  785. int grouprulesid = g_mapMixgroupidToGrouprulesid[mixgroupid];
  786. // if this mixgroup is not ducked, return 1.0
  787. if ( !g_grouprules[grouprulesid].is_ducked )
  788. return 1.0;
  789. // return current duck value for this group, scaled by current fade in/out ramp
  790. return g_grouprules[grouprulesid].duck_ramp_val;
  791. }
  792. #define SND_DUCKER_UPDATETIME 0.1 // seconds to wait between ducker updates
  793. double g_mxr_ducktime = 0.0; // time of last update to ducker
  794. // Get total volume currently playing in all groups,
  795. // process duck volumes for all groups
  796. // Call once per frame - updates occur at 10hz
  797. void MXR_UpdateAllDuckerVolumes( void )
  798. {
  799. if ( snd_disable_mixer_duck.GetInt() )
  800. return;
  801. // check timer since last update, only update at 10hz
  802. double dtime = g_pSoundServices->GetHostTime();
  803. // don't update until timer expires
  804. if (fabs(dtime - g_mxr_ducktime) < SND_DUCKER_UPDATETIME)
  805. return;
  806. g_mxr_ducktime = dtime;
  807. // clear out all total volume values for groups
  808. for ( int i = 0; i < g_cgrouprules; i++)
  809. {
  810. g_grouprules[i].total_vol = 0.0;
  811. g_grouprules[i].trigger_vol = 0.0;
  812. }
  813. // for every channel in a mix group which can cause ducking:
  814. // get total volume, store total in grouprule:
  815. CChannelList list;
  816. int ch_idx;
  817. channel_t *pchan;
  818. bool b_found_ducked_channel = false;
  819. g_ActiveChannels.GetActiveChannels( list );
  820. for ( int i = 0; i < list.Count(); i++ )
  821. {
  822. ch_idx = list.GetChannelIndex(i);
  823. pchan = &channels[ch_idx];
  824. if (pchan->last_vol > 0.0)
  825. {
  826. // account for all mix groups this channel belongs to...
  827. for (int j = 0; j < CMXRMATCHMAX; j++)
  828. {
  829. int imixgroup = pchan->mixgroups[j];
  830. if (imixgroup < 0)
  831. continue;
  832. int grouprulesid = g_mapMixgroupidToGrouprulesid[imixgroup];
  833. if (g_grouprules[grouprulesid].causes_ducking)
  834. g_grouprules[grouprulesid].total_vol += pchan->last_vol;
  835. g_grouprules[grouprulesid].trigger_vol += pchan->last_vol;
  836. if (g_grouprules[grouprulesid].is_ducked)
  837. b_found_ducked_channel = true;
  838. }
  839. }
  840. }
  841. // we're going to hanld triggers here because it's convenient
  842. // this is all a bit messy and should be cleaned up at some point
  843. // layer trigger defaults
  844. for ( int i = 0; i < CMXRMIXLAYERSMAX; i++ )
  845. {
  846. layertrigger_t *playertriggers = &g_layertriggers[i];
  847. if(!playertriggers->bhastrigger)
  848. continue;
  849. soundmixer_t *pmixlayer = &g_mixlayers[i];
  850. float curMixLevel = pmixlayer->mixAmount;
  851. float maxNewLevel = 0.0;
  852. float maxTriggerLevel = 0.0;
  853. float maxAttack = 0.0;
  854. float maxRelease = 0.0;
  855. for( int j = 0; j < CMXRGROUPMAX; j++)
  856. {
  857. if(!playertriggers->bistrigger[j])
  858. continue;
  859. int grouprulesid = g_mapMixgroupidToGrouprulesid[j];
  860. maxTriggerLevel = MAX(maxTriggerLevel, playertriggers->fmixamount[j]);
  861. if(g_grouprules[grouprulesid].trigger_vol > playertriggers->fthreshold[j])
  862. {
  863. maxNewLevel = MAX(maxNewLevel, playertriggers->fmixamount[j]);
  864. }
  865. maxAttack = MAX(playertriggers->fattack[j], maxAttack);
  866. maxRelease = MAX(playertriggers->frelease[j], maxRelease);
  867. }
  868. if(maxNewLevel != curMixLevel)
  869. {
  870. float ramptime = (maxNewLevel > curMixLevel) ? maxAttack : maxRelease;
  871. // delta is volume change per update (we can do this
  872. // since we run at an approximate fixed update rate of 10hz)
  873. // only if we have a fade
  874. if(ramptime > 0.0)
  875. {
  876. float delta = maxTriggerLevel;
  877. delta *= ( SND_DUCKER_UPDATETIME / ramptime );
  878. if (curMixLevel > maxNewLevel)
  879. delta = -delta;
  880. // update ramps
  881. float updatedMixLevel = curMixLevel + delta;
  882. if (updatedMixLevel < maxNewLevel && delta < 0)
  883. updatedMixLevel = maxNewLevel;
  884. if (updatedMixLevel > maxNewLevel && delta > 0)
  885. updatedMixLevel = maxNewLevel;
  886. maxNewLevel = updatedMixLevel;
  887. }
  888. }
  889. pmixlayer->mixAmount = maxNewLevel;
  890. }
  891. // TODO: THIS IS DESIGNED FOR MULTIPLE TRIGGERS (MAX(a,b) IS PROBABLY BEST)
  892. /* for (int i = 0; i < CMXRGROUPMAX; i++)
  893. {
  894. int grouprulesid = g_mapMixgroupidToGrouprulesid[i];
  895. layertrigger_t *playertrigger = &g_layertriggers[i];
  896. for(int j = 0; j < playertrigger->cmixlayers; j++)
  897. {
  898. int layergroupid = playertrigger->imixlayer[j];
  899. if(layergroupid > -1 && layergroupid < g_cmixlayers)
  900. {
  901. soundmixer_t *pmixlayer = &g_mixlayers[layergroupid];
  902. bool trig = false;
  903. float mixAmount = 0.0;
  904. if(g_grouprules[grouprulesid].trigger_vol > playertrigger->fthreshold[j])
  905. {
  906. mixAmount = playertrigger->fmixamount[j];
  907. DevMsg("***LAYERTRIGGER!!!\n");
  908. trig = true;
  909. }
  910. float ramptime = (mixAmount >= pmixlayer->mixAmount) ? playertrigger->fattack[j] : playertrigger->frelease[j];
  911. // delta is volume change per update (we can do this
  912. // since we run at an approximate fixed update rate of 10hz)
  913. float delta = playertrigger->fmixamount[j];
  914. delta *= ( SND_DUCKER_UPDATETIME / ramptime );
  915. if (pmixlayer->mixAmount > mixAmount)
  916. delta = -delta;
  917. // update ramps
  918. pmixlayer->mixAmount += delta;
  919. if (pmixlayer->mixAmount < mixAmount && delta < 0)
  920. pmixlayer->mixAmount = mixAmount;
  921. if (pmixlayer->mixAmount > mixAmount && delta > 0)
  922. pmixlayer->mixAmount = mixAmount;
  923. if(trig)
  924. DevMsg("%f\n", pmixlayer->mixAmount);
  925. }
  926. }
  927. }
  928. */
  929. // if no channels playing which may be ducked, do nothing
  930. if ( !b_found_ducked_channel )
  931. return;
  932. // for all groups that can be ducked:
  933. // see if a higher priority sound group has a volume > threshold,
  934. // if so, then duck this group by setting duck_target_vol to duck_target_pct.
  935. // if no sound group is causing ducking in this group, reset duck_target_vol to 1.0
  936. for (int i = 0; i < g_cgrouprules; i++)
  937. {
  938. if (g_grouprules[i].is_ducked)
  939. {
  940. int priority = g_grouprules[i].priority;
  941. float duck_volume = 1.0; // clear to 1.0 if no channel causing ducking
  942. // make sure we interact appropriately with global voice ducking...
  943. // if global voice ducking is active, skip sound group ducking and just set duck_volume target to 1.0
  944. if ( g_DuckScale >= 1.0 )
  945. {
  946. // check all sound groups for higher priority duck trigger
  947. for (int j = 0; j < g_cgrouprules; j++)
  948. {
  949. if (g_grouprules[j].priority > priority &&
  950. g_grouprules[j].causes_ducking &&
  951. g_grouprules[j].total_vol > g_grouprules[j].ducker_threshold)
  952. {
  953. // a higher priority group is causing this group to be ducked
  954. // set duck volume target to the ducked group's duck target percent
  955. // and break
  956. duck_volume = g_grouprules[i].duck_target_pct;
  957. // UNDONE: to prevent edge condition caused by crossing threshold, may need to have secondary
  958. // UNDONE: timer which allows ducking at 0.2 hz
  959. break;
  960. }
  961. }
  962. }
  963. g_grouprules[i].duck_target_vol = duck_volume;
  964. }
  965. }
  966. // update all ducker ramps if current duck value is not target
  967. // if ramp is greater than duck_volume, approach at 'attack rate'
  968. // if ramp is less than duck_volume, approach at 'decay rate'
  969. for ( int i = 0; i < g_cgrouprules; i++ )
  970. {
  971. float target = g_grouprules[i].duck_target_vol;
  972. float current = g_grouprules[i].duck_ramp_val;
  973. if (g_grouprules[i].is_ducked && (current != target))
  974. {
  975. float ramptime = target < current ? snd_duckerattacktime.GetFloat() : snd_duckerreleasetime.GetFloat();
  976. // delta is volume change per update (we can do this
  977. // since we run at an approximate fixed update rate of 10hz)
  978. float delta = (1.0 - g_grouprules[i].duck_target_pct);
  979. delta *= ( SND_DUCKER_UPDATETIME / ramptime );
  980. if (current > target)
  981. delta = -delta;
  982. // update ramps
  983. current += delta;
  984. if (current < target && delta < 0)
  985. current = target;
  986. if (current > target && delta > 0)
  987. current = target;
  988. g_grouprules[i].duck_ramp_val = current;
  989. }
  990. }
  991. }
  992. //-----------------------------------------------------------------
  993. //
  994. // Setting mixer values
  995. //
  996. //-----------------------------------------------------------------
  997. bool bPrintSetMixerDebug = false;
  998. // this will set every mix group who's name contains the passed string
  999. void S_SetIndexedMixGroupOfMixer( int imixgroup, const char *szparam, float val, soundmixer_t *pmixer, int setMixerType )
  1000. {
  1001. // TODO: need to lose these string compares for cdllint as well, make int enums!!
  1002. if(imixgroup >= 0)
  1003. {
  1004. if(!Q_stricmp("vol", szparam))
  1005. {
  1006. pmixer->mapMixgroupidToVolume[imixgroup] = val;
  1007. }
  1008. else if(!Q_stricmp("level", szparam))
  1009. {
  1010. pmixer->mapMixgroupidToLevel[imixgroup] = val;
  1011. }
  1012. else if(!Q_stricmp("dsp", szparam))
  1013. {
  1014. pmixer->mapMixgroupidToDsp[imixgroup] = val;
  1015. }
  1016. else if(!Q_stricmp("mute", szparam))
  1017. {
  1018. pmixer->mapMixgroupidToMute[imixgroup] = val;
  1019. }
  1020. else if(!Q_stricmp("solo", szparam))
  1021. {
  1022. pmixer->mapMixgroupidToSolo[imixgroup] = val;
  1023. }
  1024. else if(!Q_stricmp("mix", szparam))
  1025. {
  1026. pmixer->mixAmount = val;
  1027. }
  1028. }
  1029. }
  1030. void S_SetIndexedMixGroupOfMixer( int imixgroup, MXRMixGroupFields_t nMixGroupField, float val, soundmixer_t *pmixer )
  1031. {
  1032. if(imixgroup >= 0)
  1033. {
  1034. switch( nMixGroupField )
  1035. {
  1036. case MXR_MIXGROUP_VOL:
  1037. pmixer->mapMixgroupidToVolume[imixgroup] = val;
  1038. break;
  1039. case MXR_MIXGROUP_LEVEL:
  1040. pmixer->mapMixgroupidToLevel[imixgroup] = val;
  1041. break;
  1042. case MXR_MIXGROUP_DSP:
  1043. pmixer->mapMixgroupidToDsp[imixgroup] = val;
  1044. break;
  1045. case MXR_MIXGROUP_SOLO:
  1046. pmixer->mapMixgroupidToSolo[imixgroup] = val;
  1047. break;
  1048. case MXR_MIXGROUP_MUTE:
  1049. pmixer->mapMixgroupidToMute[imixgroup] = val;
  1050. break;
  1051. }
  1052. }
  1053. }
  1054. void S_SetMixGroupOfMixer( const char *szgroupname, const char *szparam, float val, soundmixer_t *pmixer, int setMixerType )
  1055. {
  1056. if ( !szgroupname )
  1057. return;
  1058. if ( Q_strlen(szgroupname) == 0 )
  1059. return;
  1060. // scan group rules for mapping from name to id
  1061. for (int i = 0; i < g_cgrouprules; i++)
  1062. {
  1063. // if the mix groups name contains the string we set it
  1064. if ( Q_stristr( g_grouprules[i].szmixgroup, szgroupname ) )
  1065. {
  1066. if(bPrintSetMixerDebug)
  1067. DevMsg("Setting Mixer %s: MixGroup %s: %s : %f\n", pmixer->szsoundmixer, g_grouprules[i].szmixgroup, szparam, val );
  1068. S_SetIndexedMixGroupOfMixer(g_grouprules[i].mixgroupid, szparam, val, pmixer, setMixerType );
  1069. }
  1070. }
  1071. }
  1072. // this will set every mix group who's name contains the passed string
  1073. void S_SetMixGroupOfCurrentMixer( const char *szgroupname, const char *szparam, float val, int setMixerType )
  1074. {
  1075. // get current mixer
  1076. if ( g_isoundmixer < 0 )
  1077. return;
  1078. soundmixer_t *pmixer = &g_soundmixers[g_isoundmixer];
  1079. S_SetMixGroupOfMixer( szgroupname, szparam, val, pmixer, setMixerType );
  1080. }
  1081. // this will set every mix group who's name contains the passed string
  1082. void S_SetMixGroupOfMixLayer( int nMixGroupIndex, int nMixLayerIndex, MXRMixGroupFields_t nMixGroupField, float flValue )
  1083. {
  1084. soundmixer_t *pMixLayer = &g_mixlayers[ nMixLayerIndex ];
  1085. S_SetIndexedMixGroupOfMixer( nMixGroupIndex, nMixGroupField, flValue, pMixLayer );
  1086. }
  1087. int S_GetMixGroupIndex( const char *pMixGroupName )
  1088. {
  1089. int imixgroupid = MXR_GetMixgroupFromName( pMixGroupName );
  1090. if( imixgroupid < 0 || imixgroupid >= CMXRGROUPMAX )
  1091. {
  1092. DevWarning( "Error: MixGroup %s cannot be resolved!\n", pMixGroupName );
  1093. return -1;
  1094. }
  1095. return imixgroupid;
  1096. }
  1097. int MXR_GetMixLayerIndexFromName( const char *szmixlayername )
  1098. {
  1099. for (int i = 0; i < CMXRMIXLAYERSMAX; i++)
  1100. {
  1101. // sound mixers
  1102. soundmixer_t *pmixer = &g_mixlayers[i];
  1103. if ( !Q_stricmp( pmixer->szsoundmixer, szmixlayername ) )
  1104. {
  1105. return i;
  1106. }
  1107. }
  1108. return -1;
  1109. }
  1110. int S_GetMixLayerIndex(const char *szmixlayername)
  1111. {
  1112. return MXR_GetMixLayerIndexFromName(szmixlayername);
  1113. }
  1114. void S_SetMixLayerLevel(int index, float level)
  1115. {
  1116. soundmixer_t *pmixlayer = &g_mixlayers[index];
  1117. pmixlayer->mixAmount = level;
  1118. }
  1119. void S_SetMixLayerTriggerFactor( int nMixLayerIndex, int nMixGroupIndex, float flFactor )
  1120. {
  1121. Assert( nMixLayerIndex != -1 );
  1122. layertrigger_t *playertriggers = &g_layertriggers[ nMixLayerIndex ];
  1123. if( nMixGroupIndex < 0 || nMixGroupIndex >= CMXRGROUPMAX)
  1124. {
  1125. DevMsg("Error: MixGroup %i, in LayerTriggers cannot be resolved!\n", nMixGroupIndex );
  1126. return;
  1127. }
  1128. if( ! playertriggers->bistrigger[ nMixGroupIndex ] || !playertriggers->bhastrigger )
  1129. {
  1130. // error here
  1131. return;
  1132. }
  1133. playertriggers->fmixamount[ nMixGroupIndex ] = flFactor;
  1134. }
  1135. void S_SetMixLayerTriggerFactor( const char *pMixLayerName, const char *pMixGroupName, float flFactor )
  1136. {
  1137. int nMixLayerIndex = MXR_GetMixLayerIndexFromName( pMixLayerName );
  1138. int nMixGroupIndex = MXR_GetMixgroupFromName( pMixGroupName );
  1139. if( nMixGroupIndex < 0 || nMixGroupIndex >= CMXRGROUPMAX)
  1140. {
  1141. DevMsg("Error: MixGroup %s, in LayerTriggers cannot be resolved!\n", pMixGroupName );
  1142. return;
  1143. }
  1144. S_SetMixLayerTriggerFactor( nMixLayerIndex, nMixGroupIndex, flFactor );
  1145. }
  1146. //-----------------------------------------------------------------------
  1147. //
  1148. // ConCommands to set mixer values
  1149. //
  1150. //-----------------------------------------------------------------------
  1151. static void MXR_SetSoundMixer( const CCommand &args )
  1152. {
  1153. if ( args.ArgC() != 4 )
  1154. {
  1155. DevMsg("Parameters: mix group name, [vol, mute, solo], value");
  1156. return;
  1157. }
  1158. const char *szgroupname = args[1];
  1159. const char *szparam = args[2];
  1160. float val = atof( args[3] );
  1161. bPrintSetMixerDebug = true;
  1162. S_SetMixGroupOfCurrentMixer(szgroupname, szparam, val, MIXER_SET );
  1163. bPrintSetMixerDebug = false;
  1164. }
  1165. static ConCommand snd_setmixer("snd_setmixer", MXR_SetSoundMixer, "Set named Mixgroup of current mixer to mix vol, mute, solo.", FCVAR_CHEAT );
  1166. // set the named mixgroup volume to vol for the current soundmixer
  1167. static void MXR_SetMixLayer( const CCommand &args )
  1168. {
  1169. if ( args.ArgC() != 5 )
  1170. {
  1171. DevMsg("Parameters: mix group name, layer name, [vol, mute, solo], value, amount");
  1172. return;
  1173. }
  1174. const char *szlayername = args[1];
  1175. const char *szgroupname = args[2];
  1176. const char *szparam = args[3];
  1177. float val = atof( args[4] );
  1178. bPrintSetMixerDebug = true;
  1179. for( int i = 0; i < g_cmixlayers; i++)
  1180. {
  1181. soundmixer_t *pmixlayer = &g_mixlayers[i];
  1182. if(!Q_stricmp(pmixlayer->szsoundmixer, szlayername))
  1183. {
  1184. DevMsg("Setting MixLayer %s\n", pmixlayer->szsoundmixer);
  1185. S_SetMixGroupOfMixer(szgroupname, szparam, val, pmixlayer, MIXER_SET );
  1186. }
  1187. }
  1188. bPrintSetMixerDebug = false;
  1189. }
  1190. static ConCommand snd_setmixlayer("snd_setmixlayer", MXR_SetMixLayer, "Set named Mixgroup of named mix layer to mix vol, mute, solo.", FCVAR_CHEAT );
  1191. static void MXR_SetMixLayerAmount( const CCommand &args )
  1192. {
  1193. if ( args.ArgC() != 3 )
  1194. {
  1195. DevMsg("Parameters: mixer name, mix amount");
  1196. return;
  1197. }
  1198. const char *szlayername = args[1];
  1199. float val = atof( args[2] );
  1200. for( int i = 0; i < g_cmixlayers; i++)
  1201. {
  1202. soundmixer_t *pmixlayer = &g_mixlayers[i];
  1203. if(!Q_stricmp(pmixlayer->szsoundmixer, szlayername))
  1204. {
  1205. DevMsg("Setting MixLayer %s : mix %f\n", pmixlayer->szsoundmixer, val);
  1206. pmixlayer->mixAmount = val;
  1207. break;
  1208. }
  1209. }
  1210. }
  1211. static ConCommand snd_setmixlayeramount("snd_setmixlayer_amount", MXR_SetMixLayerAmount, "Set named mix layer mix amount.", FCVAR_CHEAT );
  1212. static void MXR_SetMixLayerTriggerFactor( const CCommand &args )
  1213. {
  1214. if ( args.ArgC() != 4 )
  1215. {
  1216. DevMsg("Parameters: mix layer name, mix group name, trigger amount");
  1217. return;
  1218. }
  1219. const char *szlayername = args[1];
  1220. const char *szgroupname = args[2];
  1221. float val = atof( args[3] );
  1222. S_SetMixLayerTriggerFactor( szlayername, szgroupname, val );
  1223. }
  1224. static ConCommand snd_soundmixer_set_trigger_factor("snd_soundmixer_set_trigger_factor", MXR_SetMixLayerTriggerFactor, "Set named mix layer / mix group, trigger amount.", FCVAR_CHEAT );
  1225. int MXR_GetFirstValidMixGroup( channel_t *pChannel )
  1226. {
  1227. for (int i = 0; i < CMXRMATCHMAX; i++)
  1228. {
  1229. int imixgroup = pChannel->mixgroups[i];
  1230. //rgmixgroupid[i];
  1231. if (imixgroup < 0)
  1232. continue;
  1233. return imixgroup;
  1234. }
  1235. return -1;
  1236. }
  1237. // ---------------------------------------------------------------------
  1238. // given array of groupids (ie: the sound is in these groups),
  1239. // return a mix volume.
  1240. // return first mixgroup id in the provided array
  1241. // which maps to a non -1 volume value for this
  1242. // sound mixer
  1243. // ---------------------------------------------------------------------
  1244. soundmixer_t *MXR_GetCurrentMixer( )
  1245. {
  1246. // if no soundmixer currently set, return 1.0 volume
  1247. if (g_isoundmixer < 0)
  1248. {
  1249. return NULL;
  1250. }
  1251. if (g_csoundmixers)
  1252. {
  1253. soundmixer_t *pmixer = &g_soundmixers[g_isoundmixer];
  1254. if (pmixer)
  1255. {
  1256. return pmixer;
  1257. }
  1258. }
  1259. return NULL;
  1260. }
  1261. void MXR_GetValuesFromMixGroupIndex( mixervalues_t *mixValues, int imixgroup )
  1262. {
  1263. // save lowest duck gain value for any of the mix groups this sound is in
  1264. Assert(imixgroup < CMXRGROUPMAX);
  1265. soundmixer_t *pmixer = MXR_GetCurrentMixer( );
  1266. if( !pmixer )
  1267. {
  1268. // NEEDS ERROR!
  1269. return;
  1270. }
  1271. if ( pmixer->mapMixgroupidToVolume[imixgroup] >= 0)
  1272. {
  1273. // level
  1274. mixValues->level = pmixer->mapMixgroupidToLevel[imixgroup] * g_mastermixlayer.mapMixgroupidToLevel[imixgroup];
  1275. mixValues->level *= snd_mixerMasterLevel.GetFloat();
  1276. // dsp
  1277. mixValues->dsp = pmixer->mapMixgroupidToDsp[imixgroup] * g_mastermixlayer.mapMixgroupidToDsp[imixgroup];
  1278. mixValues->dsp *= snd_mixerMasterDSP.GetFloat();
  1279. // modify gain with ducker settings for this group
  1280. mixValues->volume = pmixer->mapMixgroupidToVolume[imixgroup] * g_mastermixlayer.mapMixgroupidToVolume[imixgroup];
  1281. // check for muting
  1282. mixValues->volume *= (1.0 - (MAX(pmixer->mapMixgroupidToMute[imixgroup], g_mastermixlayer.mapMixgroupidToMute[imixgroup])));
  1283. // If any group is solo'd && not this one, mute.
  1284. if(g_soloActive > 0.0)
  1285. {
  1286. // by definition current solo value is less than g_soloActive (max of mixer)
  1287. // not positive this math is the right approach
  1288. float factor = 1.0 - ((1.0 - (MAX(pmixer->mapMixgroupidToSolo[imixgroup], g_mastermixlayer.mapMixgroupidToSolo[imixgroup]) / g_soloActive)) * g_soloActive);
  1289. mixValues->volume *= factor;
  1290. }
  1291. }
  1292. }
  1293. void MXR_GetVolFromMixGroup( channel_t *ch, mixervalues_t *mixValues, int *plast_mixgroupid )
  1294. {
  1295. soundmixer_t *pmixer = MXR_GetCurrentMixer( );
  1296. if( !pmixer )
  1297. {
  1298. // NEEDS ERROR!
  1299. *plast_mixgroupid = 0;
  1300. mixValues->volume = 1.0;
  1301. return;
  1302. }
  1303. float duckgain = 1.0;
  1304. // search mixgroupid array, return first match (non -1)
  1305. for (int i = 0; i < CMXRMATCHMAX; i++)
  1306. {
  1307. int imixgroup = ch->mixgroups[i];
  1308. //rgmixgroupid[i];
  1309. if (imixgroup < 0)
  1310. continue;
  1311. // save lowest duck gain value for any of the mix groups this sound is in
  1312. float duckgain_new = MXR_GetDuckVolume( imixgroup );
  1313. if ( duckgain_new < duckgain)
  1314. duckgain = duckgain_new;
  1315. Assert(imixgroup < CMXRGROUPMAX);
  1316. // return first mixgroup id in the passed in array
  1317. // that maps to a non -1 volume value for this
  1318. // sound mixer
  1319. if ( pmixer->mapMixgroupidToVolume[imixgroup] >= 0)
  1320. {
  1321. *plast_mixgroupid = imixgroup;
  1322. MXR_GetValuesFromMixGroupIndex( mixValues, imixgroup );
  1323. // apply ducking although this isn't fully working because it doesn't collect up the lowest duck amount
  1324. // Did this get broken on L4D1?
  1325. mixValues->volume *= duckgain;
  1326. return;
  1327. }
  1328. }
  1329. *plast_mixgroupid = 0;
  1330. mixValues->volume = duckgain;
  1331. return;
  1332. }
  1333. // get id of mixgroup name
  1334. int MXR_GetMixgroupFromName( const char *pszgroupname )
  1335. {
  1336. // scan group rules for mapping from name to id
  1337. if ( !pszgroupname )
  1338. return -1;
  1339. if ( Q_strlen(pszgroupname) == 0 )
  1340. return -1;
  1341. for (int i = 0; i < g_cgrouprules; i++)
  1342. {
  1343. if ( !Q_stricmp(g_grouprules[i].szmixgroup, pszgroupname ) )
  1344. return g_grouprules[i].mixgroupid;
  1345. }
  1346. return -1;
  1347. }
  1348. // get mixgroup name from id
  1349. char *MXR_GetGroupnameFromId( int mixgroupid)
  1350. {
  1351. // scan group rules for mapping from name to id
  1352. if (mixgroupid < 0)
  1353. return NULL;
  1354. for (int i = 0; i < g_cgrouprules; i++)
  1355. {
  1356. if ( g_grouprules[i].mixgroupid == mixgroupid)
  1357. return g_grouprules[i].szmixgroup;
  1358. }
  1359. return NULL;
  1360. }
  1361. // assign a unique mixgroup id to each unique named mix group
  1362. // within grouprules. Note: all mixgroupids in grouprules must be -1
  1363. // when this routine starts.
  1364. void MXR_AssignGroupIds( void )
  1365. {
  1366. int cmixgroupid = 0;
  1367. for (int i = 0; i < g_cgrouprules; i++)
  1368. {
  1369. int mixgroupid = MXR_GetMixgroupFromName( g_grouprules[i].szmixgroup );
  1370. if (mixgroupid == -1)
  1371. {
  1372. // groupname is not yet assigned, provide a unique mixgroupid.
  1373. g_grouprules[i].mixgroupid = cmixgroupid;
  1374. // save reverse mapping, from mixgroupid to the first grouprules entry for this name
  1375. g_mapMixgroupidToGrouprulesid[cmixgroupid] = i;
  1376. cmixgroupid++;
  1377. }
  1378. else
  1379. {
  1380. g_grouprules[i].mixgroupid = mixgroupid;
  1381. }
  1382. }
  1383. }
  1384. int MXR_AddClassname( const char *pName )
  1385. {
  1386. char szclassname[CMXRNAMEMAX];
  1387. Q_strncpy( szclassname, pName, CMXRNAMEMAX );
  1388. for ( int i = 0; i < g_cgroupclass; i++ )
  1389. {
  1390. if ( !Q_stricmp( szclassname, g_groupclasslist[i].szclassname ) )
  1391. return i;
  1392. }
  1393. if ( g_cgroupclass >= CMXRCLASSMAX )
  1394. {
  1395. Assert(g_cgroupclass < CMXRCLASSMAX);
  1396. return -1;
  1397. }
  1398. Q_memcpy(g_groupclasslist[g_cgroupclass].szclassname, pName, MIN(CMXRNAMEMAX-1, strlen(pName)));
  1399. g_cgroupclass++;
  1400. return g_cgroupclass-1;
  1401. }
  1402. ConVar snd_soundmixer_version("snd_soundmixer_version", "2" );
  1403. #define CHAR_LEFT_PAREN '{'
  1404. #define CHAR_RIGHT_PAREN '}'
  1405. // load group rules and sound mixers from file
  1406. void S_FlushMixers( const CCommand &args )
  1407. {
  1408. MXR_LoadAllSoundMixers();
  1409. }
  1410. static ConCommand SoundMixersFlush("snd_soundmixer_flush", S_FlushMixers, "Reload soundmixers.txt file.", FCVAR_CHEAT );
  1411. enum
  1412. {
  1413. MXRPARSE_NONE,
  1414. MXRPARSE_MIXGROUPS,
  1415. MXRPARSE_SOUNDMIXERS,
  1416. MXRPARSE_SOUNDMIXERGROUPS,
  1417. MXRPARSE_MIXLAYERS,
  1418. MXRPARSE_MIXLAYERGROUPS,
  1419. MXRPARSE_LAYERTRIGGERS
  1420. };
  1421. #define MIXGROUPS_STRING "MixGroups"
  1422. #define SOUNDMIXERS_STRING "SoundMixers"
  1423. #define MIXLAYERS_STRING "MixLayers"
  1424. #define LAYERTRIGGERS_STRING "LayerTriggers"
  1425. ConVar DebugMXRParse("snd_soundmixer_parse_debug", "0");
  1426. const char *MXR_ParseMixGroup(const char *pstart)
  1427. {
  1428. int parse_debug = DebugMXRParse.GetInt();
  1429. grouprule_t *pgroup = &g_grouprules[g_cgrouprules];
  1430. // copy mixgroup name, directory, classname
  1431. // if no value specified, set to 0 length string
  1432. if(parse_debug)
  1433. DevMsg("MixGroup %s:\n", com_token);
  1434. Q_memcpy(pgroup->szmixgroup, com_token, MIN(CMXRNAMEMAX-1, strlen(com_token)));
  1435. // make sure all copied strings are null terminated
  1436. pgroup->szmixgroup[CMXRNAMEMAX-1] = 0;
  1437. // path rule string
  1438. if(COM_TokenWaiting( pstart ))
  1439. {
  1440. pstart = COM_Parse( pstart );
  1441. if (com_token[0])
  1442. {
  1443. Q_memcpy(pgroup->szdir, com_token, MIN(CMXRNAMEMAX-1, strlen(com_token)));
  1444. V_strlower( pgroup->szdir );
  1445. // HACK to find group that affects voice channels
  1446. if ( V_strstr(pgroup->szdir, "?voice") )
  1447. {
  1448. pgroup->is_voice = 1;
  1449. }
  1450. }
  1451. }
  1452. else
  1453. DevMsg("Error: Parsing soundmixers.txt, mixgroup rules incomplete!\n");
  1454. // make sure all copied strings are null terminated
  1455. pgroup->szdir[CMXRNAMEMAX-1] = 0;
  1456. // classname
  1457. pgroup->classId = -1;
  1458. if(COM_TokenWaiting( pstart ))
  1459. {
  1460. pstart = COM_Parse( pstart );
  1461. if (com_token[0])
  1462. {
  1463. pgroup->classId = MXR_AddClassname( com_token );
  1464. }
  1465. }
  1466. else
  1467. DevMsg("Error: Parsing soundmixers.txt, mixgroup rules incomplete!\n");
  1468. // lookup chan
  1469. if(COM_TokenWaiting( pstart ))
  1470. {
  1471. pstart = COM_Parse( pstart );
  1472. if (com_token[0])
  1473. {
  1474. if (!Q_stricmp(com_token, "CHAN_STATIC"))
  1475. pgroup->chantype = CHAN_STATIC;
  1476. else if (!Q_stricmp(com_token, "CHAN_WEAPON"))
  1477. pgroup->chantype = CHAN_WEAPON;
  1478. else if (!Q_stricmp(com_token, "CHAN_VOICE"))
  1479. pgroup->chantype = CHAN_VOICE;
  1480. else if (!Q_stricmp(com_token, "CHAN_BODY"))
  1481. pgroup->chantype = CHAN_BODY;
  1482. else if (!Q_stricmp(com_token, "CHAN_ITEM"))
  1483. pgroup->chantype = CHAN_ITEM;
  1484. }
  1485. else
  1486. pgroup->chantype = -1;
  1487. }
  1488. else
  1489. DevMsg("Error: Parsing soundmixers.txt, mixgroup rules incomplete!\n");
  1490. // get sndlvls
  1491. // soundlevel min
  1492. if(COM_TokenWaiting( pstart ))
  1493. {
  1494. pstart = COM_Parse( pstart );
  1495. if (com_token[0])
  1496. pgroup->soundlevel_min = atoi(com_token);
  1497. else
  1498. pgroup->soundlevel_min = -1;
  1499. }
  1500. else
  1501. DevMsg("Error: Parsing soundmixers.txt, mixgroup rules incomplete!\n");
  1502. // soundlevel max
  1503. if(COM_TokenWaiting( pstart ))
  1504. {
  1505. pstart = COM_Parse( pstart );
  1506. if (com_token[0])
  1507. pgroup->soundlevel_max = atoi(com_token);
  1508. else
  1509. pgroup->soundlevel_max = -1;
  1510. }
  1511. else
  1512. DevMsg("Error: Parsing soundmixers.txt, mixgroup rules incomplete!\n");
  1513. // get duck priority, IsDucked, Causes_ducking, duck_target_pct
  1514. if(COM_TokenWaiting( pstart ))
  1515. {
  1516. pstart = COM_Parse( pstart );
  1517. if (com_token[0])
  1518. pgroup->priority = atoi(com_token);
  1519. else
  1520. pgroup->priority = 50;
  1521. }
  1522. else
  1523. DevMsg("Error: Parsing soundmixers.txt, mixgroup rules incomplete!\n");
  1524. // mix group is ducked
  1525. if(COM_TokenWaiting( pstart ))
  1526. {
  1527. pstart = COM_Parse( pstart );
  1528. if (com_token[0])
  1529. pgroup->is_ducked = atoi(com_token);
  1530. else
  1531. pgroup->is_ducked = 0;
  1532. }
  1533. else
  1534. DevMsg("Error: Parsing soundmixers.txt, mixgroup rules incomplete!\n");
  1535. // mix group causes ducking
  1536. if(COM_TokenWaiting( pstart ))
  1537. {
  1538. pstart = COM_Parse( pstart );
  1539. if (com_token[0])
  1540. pgroup->causes_ducking = atoi(com_token);
  1541. else
  1542. pgroup->causes_ducking = 0;
  1543. }
  1544. else
  1545. DevMsg("Error: Parsing soundmixers.txt, mixgroup rules incomplete!\n");
  1546. // ducking target pct
  1547. if(COM_TokenWaiting( pstart ))
  1548. {
  1549. pstart = COM_Parse( pstart );
  1550. if (com_token[0])
  1551. pgroup->duck_target_pct = ((float)(atoi(com_token))) / 100.0f;
  1552. else
  1553. pgroup->duck_target_pct = 0.5f;
  1554. }
  1555. else
  1556. DevMsg("Error: Parsing soundmixers.txt, mixgroup rules incomplete!\n");
  1557. // ducking target pct
  1558. if(COM_TokenWaiting( pstart ))
  1559. {
  1560. pstart = COM_Parse( pstart );
  1561. if (com_token[0])
  1562. pgroup->ducker_threshold = ((float)(atoi(com_token))) / 100.0f;
  1563. else
  1564. pgroup->ducker_threshold = 0.5f;
  1565. }
  1566. else
  1567. DevMsg("Error: Parsing soundmixers.txt, mixgroup rules incomplete!\n");
  1568. // set default values
  1569. pgroup->duck_ramp_val = 1.0;
  1570. pgroup->duck_target_vol = 1.0;
  1571. pgroup->total_vol = 0.0;
  1572. pgroup->trigger_vol = 0.0;
  1573. // set mixgroup id to -1
  1574. pgroup->mixgroupid = -1;
  1575. // update rule count
  1576. g_cgrouprules++;
  1577. return pstart;
  1578. }
  1579. const char *MXR_ParseSoundMixer(const char *pstart, soundmixer_t *pmixer)
  1580. {
  1581. int parse_debug = DebugMXRParse.GetInt();
  1582. // lookup mixgroupid for groupname
  1583. char szgroupname[CMXRNAMEMAX];
  1584. V_strcpy_safe(szgroupname, com_token);
  1585. // int mixgroupid = MXR_GetMixgroupFromName( com_token );
  1586. float volume = 1.0;
  1587. float level = 1.0;
  1588. float dsp = 1.0;
  1589. float solo = 0.0;
  1590. float mute = 0.0;
  1591. // get mix value
  1592. if(COM_TokenWaiting( pstart ))
  1593. {
  1594. pstart = COM_Parse( pstart );
  1595. if( com_token[0] )
  1596. volume = atof( com_token );
  1597. else
  1598. volume = 1.0;
  1599. }
  1600. else
  1601. DevMsg("Error: Parsing soundmixers.txt, soundmixer mix group values incomplete!\n");
  1602. // are we using new soundmixer features?
  1603. if(snd_soundmixer_version.GetInt() >= 2)
  1604. {
  1605. // checking for new mixer features
  1606. if(COM_TokenWaiting( pstart ))
  1607. {
  1608. // get "level" value
  1609. pstart = COM_Parse( pstart );
  1610. level = atof( com_token );
  1611. }
  1612. if(COM_TokenWaiting( pstart ))
  1613. {
  1614. // get "dsp" value
  1615. pstart = COM_Parse( pstart );
  1616. dsp = atof( com_token );
  1617. }
  1618. if(COM_TokenWaiting( pstart ))
  1619. {
  1620. pstart = COM_Parse( pstart );
  1621. solo = atof( com_token );
  1622. }
  1623. if(COM_TokenWaiting( pstart ))
  1624. {
  1625. pstart = COM_Parse( pstart );
  1626. mute = atof( com_token );
  1627. }
  1628. }
  1629. // scan group rules for mapping from name to id
  1630. for (int i = 0; i < g_cgrouprules; i++)
  1631. {
  1632. // if the mix groups name contains the string we set it
  1633. if ( !Q_strcmp( g_grouprules[i].szmixgroup, szgroupname ) )
  1634. {
  1635. // sanity check mix group
  1636. if(g_grouprules[i].mixgroupid < 0 || g_grouprules[i].mixgroupid >= CMXRGROUPMAX)
  1637. {
  1638. DevMsg("Error: MixGroup %s, in SoundMixer %s, cannot be resolved!\n", com_token, pmixer->szsoundmixer);
  1639. }
  1640. else
  1641. {
  1642. if(parse_debug)
  1643. DevMsg("MixGroup %s: %f : %f : %f : %f : %f \n", szgroupname, volume, level, dsp, solo, mute);
  1644. // store value for mixgroupid
  1645. pmixer->mapMixgroupidToVolume[g_grouprules[i].mixgroupid] = MAX(volume, 0.0);
  1646. pmixer->mapMixgroupidToLevel[g_grouprules[i].mixgroupid] = MAX(level, 0.0);
  1647. pmixer->mapMixgroupidToDsp[g_grouprules[i].mixgroupid] = MAX(dsp, 0.0);
  1648. pmixer->mapMixgroupidToSolo[g_grouprules[i].mixgroupid] = MAX(solo, 0.0);
  1649. pmixer->mapMixgroupidToMute[g_grouprules[i].mixgroupid] = MAX(mute, 0.0);
  1650. }
  1651. }
  1652. }
  1653. return pstart;
  1654. }
  1655. const char *MXR_ParseLayerTriggers(const char *pstart)
  1656. {
  1657. int parse_debug = DebugMXRParse.GetInt();
  1658. // copy mixgroup name, directory, classname
  1659. // if no value specified, set to 0 length string
  1660. if(parse_debug)
  1661. DevMsg("MixLayer triggered %s:\n", com_token);
  1662. int imixlayerid = MXR_GetMixLayerIndexFromName( com_token );
  1663. if ( imixlayerid == -1 )
  1664. {
  1665. Warning( "Failed to get mix layer %s!\n", com_token );
  1666. return pstart;
  1667. }
  1668. layertrigger_t *playertriggers = &g_layertriggers[imixlayerid];
  1669. // sanity check mix group
  1670. int imixgroupid = -1;
  1671. // path rule string
  1672. if(COM_TokenWaiting( pstart ))
  1673. {
  1674. pstart = COM_Parse( pstart );
  1675. if (com_token[0])
  1676. {
  1677. imixgroupid = MXR_GetMixgroupFromName( com_token );
  1678. }
  1679. }
  1680. else
  1681. {
  1682. DevMsg("Error: MixLayer Trigger entries require minimum 2 arguments\n");
  1683. return pstart;
  1684. }
  1685. if(imixgroupid < 0 || imixgroupid >= CMXRGROUPMAX)
  1686. {
  1687. DevMsg("Error: MixGroup %s, in LayerTriggers cannot be resolved!\n", com_token);
  1688. return pstart;
  1689. }
  1690. playertriggers->bistrigger[imixgroupid] = true;
  1691. playertriggers->bhastrigger = true;
  1692. // threshold
  1693. if(COM_TokenWaiting( pstart ))
  1694. {
  1695. pstart = COM_Parse( pstart );
  1696. if (com_token[0])
  1697. {
  1698. playertriggers->fthreshold[imixgroupid] = atof( com_token );
  1699. }
  1700. }
  1701. else
  1702. return pstart;
  1703. // mixamount
  1704. if(COM_TokenWaiting( pstart ))
  1705. {
  1706. pstart = COM_Parse( pstart );
  1707. if (com_token[0])
  1708. {
  1709. playertriggers->fmixamount[imixgroupid] = atof( com_token );
  1710. }
  1711. }
  1712. else
  1713. return pstart;
  1714. // attack
  1715. if(COM_TokenWaiting( pstart ))
  1716. {
  1717. pstart = COM_Parse( pstart );
  1718. if (com_token[0])
  1719. {
  1720. playertriggers->fattack[imixgroupid] = atof( com_token );
  1721. }
  1722. }
  1723. else
  1724. return pstart;
  1725. // release
  1726. if(COM_TokenWaiting( pstart ))
  1727. {
  1728. pstart = COM_Parse( pstart );
  1729. if (com_token[0])
  1730. {
  1731. playertriggers->frelease[imixgroupid] = atof( com_token );
  1732. }
  1733. }
  1734. return pstart;
  1735. }
  1736. bool MXR_LoadAllSoundMixers( void )
  1737. {
  1738. // init soundmixer globals
  1739. g_isoundmixer = -1;
  1740. g_szsoundmixer_cur[0] = 0;
  1741. g_csoundmixers = 0; // total number of soundmixers found
  1742. g_cmixlayers = 0; // total number of soundmixers found
  1743. g_cgrouprules = 0; // total number of group rules found
  1744. Q_memset(g_soundmixers, 0, sizeof(g_soundmixers));
  1745. Q_memset(g_mixlayers, 0, sizeof(g_mixlayers));
  1746. Q_memset(g_grouprules, 0, sizeof(g_grouprules));
  1747. // init all mix group mixer values to -1.
  1748. for (int i = 0; i < CMXRSOUNDMIXERSMAX; i++)
  1749. {
  1750. // sound mixers
  1751. soundmixer_t *pmixer = &g_soundmixers[i];
  1752. V_strcpy_safe(pmixer->szsoundmixer, "");
  1753. // sound mixers default to full on
  1754. pmixer->mixAmount = 1.0;
  1755. for (int j = 0; j < CMXRGROUPMAX; j++)
  1756. {
  1757. pmixer->mapMixgroupidToVolume[j] = -1.0;
  1758. pmixer->mapMixgroupidToLevel[j] = 1.0;
  1759. pmixer->mapMixgroupidToDsp[j] = 1.0;
  1760. pmixer->mapMixgroupidToSolo[j] = 0.0;
  1761. pmixer->mapMixgroupidToMute[j] = 0.0;
  1762. }
  1763. }
  1764. for (int i = 0; i < CMXRMIXLAYERSMAX; i++)
  1765. {
  1766. // mix layers
  1767. soundmixer_t *pmixlayers = &g_mixlayers[i];
  1768. V_strcpy_safe(pmixlayers->szsoundmixer, "");
  1769. // mix layers default to all off
  1770. pmixlayers->mixAmount = 0.0;
  1771. for (int j = 0; j < CMXRGROUPMAX; j++)
  1772. {
  1773. pmixlayers->mapMixgroupidToVolume[j] = -1.0;
  1774. pmixlayers->mapMixgroupidToLevel[j] = 1.0;
  1775. pmixlayers->mapMixgroupidToDsp[j] = 1.0;
  1776. pmixlayers->mapMixgroupidToSolo[j] = 0.0;
  1777. pmixlayers->mapMixgroupidToMute[j] = 0.0;
  1778. }
  1779. }
  1780. // layer trigger defaults
  1781. for (int i = 0; i < CMXRMIXLAYERSMAX; i++)
  1782. {
  1783. layertrigger_t *playertriggers = &g_layertriggers[i];
  1784. playertriggers->bhastrigger = false;
  1785. for( int j = 0; j < CMXRGROUPMAX; j++)
  1786. {
  1787. playertriggers->bistrigger[j] = false;
  1788. playertriggers->fthreshold[j] = 0.0;
  1789. playertriggers->fmixamount[j] = 1.0;
  1790. playertriggers->fattack[j] = 0.0;
  1791. playertriggers->frelease[j] = 0.0;
  1792. }
  1793. }
  1794. // sound mixers file
  1795. char szFile[MAX_OSPATH];
  1796. const char *pstart;
  1797. bool bResult = false;
  1798. char *pbuffer;
  1799. Q_snprintf( szFile, sizeof( szFile ), "scripts/soundmixers.txt" );
  1800. pbuffer = (char *)COM_LoadFile( szFile, 5, NULL ); // Use malloc - free at end of this routine
  1801. if ( !pbuffer )
  1802. {
  1803. Error( "MXR_LoadAllSoundMixers: unable to open '%s'\n", szFile );
  1804. return bResult;
  1805. }
  1806. pstart = pbuffer;
  1807. int parse_debug = DebugMXRParse.GetInt();
  1808. int currentMxrParse = MXRPARSE_NONE;
  1809. int currentParseLevel = 0;
  1810. // check for first CHAR_LEFT_PAREN
  1811. while (1)
  1812. {
  1813. pstart = COM_Parse( pstart );
  1814. if ( strlen(com_token) <= 0)
  1815. break; // eof
  1816. // handle in and out of brackets
  1817. if ( com_token[0] == CHAR_LEFT_PAREN )
  1818. {
  1819. currentParseLevel++;
  1820. //if(parse_debug)
  1821. //DevMsg("parse level %i:\n", currentParseLevel);
  1822. continue;
  1823. }
  1824. else if ( com_token[0] == CHAR_RIGHT_PAREN )
  1825. {
  1826. // do any clean up processing for the previous block
  1827. currentParseLevel--;
  1828. //if(parse_debug)
  1829. //DevMsg("parse level %i:\n", currentParseLevel);
  1830. if(currentMxrParse == MXRPARSE_MIXGROUPS)
  1831. {
  1832. // now process all groupids in groups, such that
  1833. // each mixgroup gets a unique id.
  1834. MXR_AssignGroupIds();
  1835. currentMxrParse = MXRPARSE_NONE;
  1836. if(parse_debug)
  1837. DevMsg("Total Mix Groups Rules: %i\n", g_cgrouprules);
  1838. }
  1839. else if(currentMxrParse == MXRPARSE_SOUNDMIXERGROUPS)
  1840. {
  1841. currentMxrParse = MXRPARSE_SOUNDMIXERS;
  1842. g_csoundmixers++;
  1843. }
  1844. else if(currentMxrParse == MXRPARSE_SOUNDMIXERS)
  1845. {
  1846. currentMxrParse = MXRPARSE_NONE;
  1847. }
  1848. else if(currentMxrParse == MXRPARSE_MIXLAYERGROUPS)
  1849. {
  1850. currentMxrParse = MXRPARSE_MIXLAYERS;
  1851. g_cmixlayers++;
  1852. }
  1853. else if(currentMxrParse == MXRPARSE_MIXLAYERS)
  1854. {
  1855. currentMxrParse = MXRPARSE_NONE;
  1856. if(parse_debug)
  1857. DevMsg("Total Mix Layers: %i\n", g_cmixlayers);
  1858. }
  1859. else if(currentMxrParse == MXRPARSE_LAYERTRIGGERS)
  1860. {
  1861. currentMxrParse = MXRPARSE_NONE;
  1862. }
  1863. continue;
  1864. }
  1865. // parsing the outside?
  1866. if( currentMxrParse == MXRPARSE_NONE)
  1867. {
  1868. if (!Q_strcmp( com_token, MIXGROUPS_STRING ) )
  1869. {
  1870. currentMxrParse = MXRPARSE_MIXGROUPS;
  1871. if(parse_debug)
  1872. DevMsg("Parsing MixGroups:\n");
  1873. continue;
  1874. }
  1875. else if (!Q_strcmp( com_token, SOUNDMIXERS_STRING ) )
  1876. {
  1877. currentMxrParse = MXRPARSE_SOUNDMIXERS;
  1878. if(parse_debug)
  1879. DevMsg("Parsing SoundMixers:\n");
  1880. continue;
  1881. }
  1882. else if (!Q_strcmp( com_token, MIXLAYERS_STRING ) )
  1883. {
  1884. currentMxrParse = MXRPARSE_MIXLAYERS;
  1885. if(parse_debug)
  1886. DevMsg("Parsing MixLayers:\n");
  1887. continue;
  1888. }
  1889. else if (!Q_strcmp( com_token, LAYERTRIGGERS_STRING ) )
  1890. {
  1891. currentMxrParse = MXRPARSE_LAYERTRIGGERS;
  1892. if(parse_debug)
  1893. DevMsg("Parsing LayerTriggers:\n");
  1894. continue;
  1895. }
  1896. }
  1897. else if ( currentMxrParse == MXRPARSE_MIXGROUPS )
  1898. {
  1899. if (g_cgrouprules > CMXRGROUPRULESMAX)
  1900. {
  1901. // UNDONE: error! too many rules
  1902. DevMsg("Error: Too many mix groups! MixGroup %s ignored\n", com_token);
  1903. continue;
  1904. }
  1905. pstart = MXR_ParseMixGroup(pstart);
  1906. }
  1907. else if ( currentMxrParse == MXRPARSE_LAYERTRIGGERS )
  1908. {
  1909. /*if (g_cgrouprules > CMXRGROUPRULESMAX)
  1910. {
  1911. // UNDONE: error! too many rules
  1912. DevMsg("Error: Too many mix groups! MixGroup %s ignored\n", com_token);
  1913. continue;
  1914. }*/
  1915. pstart = MXR_ParseLayerTriggers(pstart);
  1916. }
  1917. else if ( currentMxrParse == MXRPARSE_SOUNDMIXERS && currentParseLevel < 2 )
  1918. {
  1919. // save name in soundmixer
  1920. if (g_csoundmixers > CMXRSOUNDMIXERSMAX)
  1921. {
  1922. DevMsg("Error: Too many sound mixers! SoundMixer %s ignored\n", com_token);
  1923. continue;
  1924. }
  1925. soundmixer_t *pmixer = &g_soundmixers[g_csoundmixers];
  1926. if(parse_debug)
  1927. DevMsg("SoundMixer %s:\n", com_token);
  1928. currentMxrParse = MXRPARSE_SOUNDMIXERGROUPS;
  1929. Q_memcpy(pmixer->szsoundmixer, com_token, MIN(CMXRNAMEMAX-1, strlen(com_token)));
  1930. }
  1931. else if ( currentMxrParse == MXRPARSE_SOUNDMIXERGROUPS && currentParseLevel == 2 )
  1932. {
  1933. soundmixer_t *pmixer = &g_soundmixers[g_csoundmixers];
  1934. pstart = MXR_ParseSoundMixer(pstart, pmixer );
  1935. }
  1936. // mix layers
  1937. else if ( currentMxrParse == MXRPARSE_MIXLAYERS && currentParseLevel < 2 )
  1938. {
  1939. // save name in soundmixer
  1940. if (g_cmixlayers > CMXRMIXLAYERSMAX)
  1941. {
  1942. DevMsg("Error: Too many mix layers! MixLayer %s ignored\n", com_token);
  1943. continue;
  1944. }
  1945. soundmixer_t *pmixlayer = &g_mixlayers[g_cmixlayers];
  1946. if(parse_debug)
  1947. DevMsg("MixLayers %s:\n", com_token);
  1948. currentMxrParse = MXRPARSE_MIXLAYERGROUPS;
  1949. Q_memcpy(pmixlayer->szsoundmixer, com_token, MIN(CMXRNAMEMAX-1, strlen(com_token)));
  1950. }
  1951. else if ( currentMxrParse == MXRPARSE_MIXLAYERGROUPS && currentParseLevel == 2 )
  1952. {
  1953. soundmixer_t *pmixlayer = &g_mixlayers[g_cmixlayers];
  1954. pstart = MXR_ParseSoundMixer(pstart, pmixlayer);
  1955. }
  1956. }
  1957. bResult = true;
  1958. // loadmxr_exit:
  1959. free( pbuffer );
  1960. return bResult;
  1961. }
  1962. void MXR_ReleaseMemory( void )
  1963. {
  1964. // free all resources
  1965. }
  1966. //--------------------------------------------------------------------------------------------
  1967. //
  1968. // Debug and diagnostics
  1969. //
  1970. //---------------------------------------------------------------------------------------------
  1971. static void MXR_PrintMixGroups( soundmixer_t *pmixer )
  1972. {
  1973. int imixgroup = 0;
  1974. int nMixGroupId = 0;
  1975. for( int i = 0; i < g_cgrouprules; i++ )
  1976. {
  1977. imixgroup = g_grouprules[i].mixgroupid;
  1978. if( imixgroup < 0 )
  1979. {
  1980. nMixGroupId++;
  1981. continue;
  1982. }
  1983. else if ( imixgroup != nMixGroupId ) // only uniquely id'd
  1984. {
  1985. continue;
  1986. }
  1987. if( pmixer->mapMixgroupidToVolume[imixgroup] < 0.0 )
  1988. {
  1989. nMixGroupId++;
  1990. continue;
  1991. }
  1992. int nStrLen = V_strlen( g_grouprules[i].szmixgroup );
  1993. int nDiff = 32 - nStrLen;
  1994. char nTmpStr[32];
  1995. for( int j = 0; j < nDiff; j++ )
  1996. {
  1997. nTmpStr[j] = ' ';
  1998. }
  1999. nTmpStr[nDiff] = NULL;
  2000. DevMsg("%s: %s", g_grouprules[i].szmixgroup, nTmpStr );
  2001. DevMsg("vol: %3.2f ", pmixer->mapMixgroupidToVolume[imixgroup]);
  2002. DevMsg("lvl: %3.2f ", pmixer->mapMixgroupidToLevel[imixgroup]);
  2003. DevMsg("dsp: %3.2f ", pmixer->mapMixgroupidToDsp[imixgroup]);
  2004. DevMsg("solo: %3.2f ", pmixer->mapMixgroupidToSolo[imixgroup]);
  2005. DevMsg("mute: %3.2f\n", pmixer->mapMixgroupidToMute[imixgroup]);
  2006. nMixGroupId++;
  2007. }
  2008. }
  2009. static void MXR_ListMixers( const CCommand &args )
  2010. {
  2011. for (int i = 0; i < g_csoundmixers; i++)
  2012. {
  2013. soundmixer_t *pmixer = &g_soundmixers[i];
  2014. DevMsg("%s:\n", pmixer->szsoundmixer);
  2015. MXR_PrintMixGroups( pmixer );
  2016. }
  2017. }
  2018. static ConCommand snd_list_mixers("snd_soundmixer_list_mixers", MXR_ListMixers, "List all mixers to dev console." );
  2019. static void MXR_ListMixLayers( const CCommand &args )
  2020. {
  2021. for (int i = 0; i < g_cmixlayers; i++)
  2022. {
  2023. soundmixer_t *pmixer = &g_mixlayers[i];
  2024. DevMsg("%s: %f\n", pmixer->szsoundmixer, pmixer->mixAmount );
  2025. MXR_PrintMixGroups( pmixer );
  2026. }
  2027. DevMsg( "g_mastermixlayer:\n" );
  2028. MXR_PrintMixGroups( &g_mastermixlayer );
  2029. }
  2030. static ConCommand snd_list_mix_layers("snd_soundmixer_list_mix_layers", MXR_ListMixLayers, "List all mix layers to dev console.");
  2031. //---------------------------------------------------------------------------
  2032. //
  2033. // list all mix groups and their values
  2034. //
  2035. //---------------------------------------------------------------------------
  2036. static ConCommand snd_list_mix_groups("snd_soundmixer_list_mix_groups", MXR_ListMixGroups, "List all mix groups to dev console.");
  2037. static void MXR_ListMixGroups( const CCommand &args )
  2038. {
  2039. soundmixer_t *pmixer = &g_soundmixers[g_isoundmixer];
  2040. MXR_PrintMixGroups( pmixer );
  2041. }
  2042. void S_GetMixGroupOfCurrentMixer( const char *szgroupname, soundmixer_t *pmixer)
  2043. {
  2044. // iterate over groups
  2045. int imixgroup = 0;
  2046. for (int i = 0; i < g_cgrouprules; i++)
  2047. {
  2048. if(Q_stristr(g_grouprules[i].szmixgroup, szgroupname))
  2049. {
  2050. imixgroup = g_grouprules[i].mixgroupid;
  2051. // float dynVolume = g_mastermixlayer.mapMixgroupidToVolume[imixgroup];
  2052. float volume = pmixer->mapMixgroupidToVolume[imixgroup];
  2053. // float dynLevel = g_mastermixlayer.mapMixgroupidToLevel[imixgroup];
  2054. float level = pmixer->mapMixgroupidToLevel[imixgroup];
  2055. // float dynDsp = g_mastermixlayer.mapMixgroupidToDsp[imixgroup];
  2056. float dsp = pmixer->mapMixgroupidToDsp[imixgroup];
  2057. // float dynMute = g_mastermixlayer.mapMixgroupidToMute[imixgroup];
  2058. float mute = pmixer->mapMixgroupidToMute[imixgroup];
  2059. // float dynSolo = g_mastermixlayer.mapMixgroupidToSolo[imixgroup];
  2060. float solo = pmixer->mapMixgroupidToSolo[imixgroup];
  2061. DevMsg("%s:\n", g_grouprules[i].szmixgroup);
  2062. DevMsg("\tVOL: %f\n\tLVL: %f\n\tDSP: %f\n\tMUTE: %f\n\tSOLO: %f\n\n",
  2063. volume, level, dsp, mute, solo);
  2064. }
  2065. }
  2066. }
  2067. static void MXR_GetSoundMixer( const CCommand &args )
  2068. {
  2069. soundmixer_t *pmixer = NULL;
  2070. if ( args.ArgC() == 2)
  2071. {
  2072. // get current mixer
  2073. if ( g_isoundmixer < 0 )
  2074. return;
  2075. pmixer = &g_soundmixers[g_isoundmixer];
  2076. }
  2077. else
  2078. {
  2079. //DevMsg("Parameters: mix group name, [vol, mute, solo], value");
  2080. return;
  2081. }
  2082. const char *szgroupname = args[1];
  2083. S_GetMixGroupOfCurrentMixer(szgroupname, pmixer);
  2084. }
  2085. static ConCommand snd_getmixer("snd_getmixer", MXR_GetSoundMixer, "Get data related to mix group matching string");
  2086. struct debug_showvols_t
  2087. {
  2088. char *psz; // group name
  2089. int mixgroupid; // groupid
  2090. float vol; // group volume
  2091. float totalvol; // total volume of all sounds playing in this group
  2092. };
  2093. // show the current soundmixer output
  2094. // display routine for MXR_DebugShowMixVolumes
  2095. #define MXR_DEBUG_INCY (1.0/40.0) // vertical text spacing
  2096. #define MXR_DEBUG_GREENSTART 0.3 // start position on screen of bar
  2097. #define MXR_DEBUG_MAXVOL 1.0 // max volume scale
  2098. #define MXR_DEBUG_REDLIMIT 1.0 // volume limit into yellow
  2099. #define MXR_DEBUG_YELLOWLIMIT 0.7 // volume limit into red
  2100. #define MXR_DEBUG_VOLSCALE 48 // length of graph in characters
  2101. #define MXR_DEBUG_CHAR '-' // bar character
  2102. int g_debug_mxr_displaycount = 0;
  2103. void MXR_DebugGraphMixVolumes( debug_showvols_t *groupvols, int cgroups)
  2104. {
  2105. float flXpos, flYpos, flXposBar, duration;
  2106. int r,g,b,a;
  2107. int rb, gb, bb, ab;
  2108. flXpos = 0;
  2109. flYpos = 0;
  2110. char text[128];
  2111. char bartext[MXR_DEBUG_VOLSCALE*3];
  2112. duration = 0.01;
  2113. g_debug_mxr_displaycount++;
  2114. if (!(g_debug_mxr_displaycount % 10))
  2115. return; // only display every 10 frames
  2116. r = 96; g = 86; b = 226; a = 255; ab = 255;
  2117. // show volume, dsp_volume
  2118. /* Q_snprintf( text, 128, "Game Volume: %1.2f", volume.GetFloat());
  2119. CDebugOverlay::AddScreenTextOverlay(flXpos, flYpos, duration, r, g, b,a, text);
  2120. flYpos += MXR_DEBUG_INCY; */
  2121. Q_snprintf( text, 128, "DSP Volume: %1.2f", dsp_volume.GetFloat());
  2122. CDebugOverlay::AddScreenTextOverlay(flXpos, flYpos, duration, r, g, b,a, text);
  2123. flYpos += MXR_DEBUG_INCY;
  2124. for (int i = 0; i < cgroups; i++)
  2125. {
  2126. // r += 64; g += 64; b += 16;
  2127. r = r % 255; g = g % 255; b = b % 255;
  2128. Q_snprintf( text, 128, "%s: %1.2f (%1.2f)", groupvols[i].psz,
  2129. groupvols[i].vol * g_DuckScale, groupvols[i].totalvol * g_DuckScale);
  2130. CDebugOverlay::AddScreenTextOverlay(flXpos, flYpos, duration, r, g, b,a, text);
  2131. // draw volume bar graph
  2132. float vol = (groupvols[i].totalvol * g_DuckScale) / MXR_DEBUG_MAXVOL;
  2133. // draw first 70% green
  2134. float vol1 = 0.0;
  2135. float vol2 = 0.0;
  2136. float vol3 = 0.0;
  2137. int cbars;
  2138. vol1 = clamp(vol, 0.0, 0.7);
  2139. vol2 = clamp(vol, 0.0, 0.95);
  2140. vol3 = vol;
  2141. flXposBar = flXpos + MXR_DEBUG_GREENSTART;
  2142. if (vol1 > 0.0)
  2143. {
  2144. //flXposBar = flXpos + MXR_DEBUG_GREENSTART;
  2145. rb = 0; gb= 255; bb = 0; // green bar
  2146. Q_memset(bartext, 0, sizeof(bartext));
  2147. cbars = (int)((float)vol1 * (float)MXR_DEBUG_VOLSCALE);
  2148. cbars = clamp(cbars, 0, MXR_DEBUG_VOLSCALE*3-1);
  2149. Q_memset(bartext, MXR_DEBUG_CHAR, cbars);
  2150. CDebugOverlay::AddScreenTextOverlay(flXposBar, flYpos, duration, rb, gb, bb,ab, bartext);
  2151. }
  2152. // yellow bar
  2153. if (vol2 > MXR_DEBUG_YELLOWLIMIT)
  2154. {
  2155. rb = 255; gb = 255; bb = 0;
  2156. Q_memset(bartext, 0, sizeof(bartext));
  2157. cbars = (int)((float)vol2 * (float)MXR_DEBUG_VOLSCALE);
  2158. cbars = clamp(cbars, 0, MXR_DEBUG_VOLSCALE*3-1);
  2159. Q_memset(bartext, MXR_DEBUG_CHAR, cbars);
  2160. CDebugOverlay::AddScreenTextOverlay(flXposBar, flYpos, duration, rb, gb, bb,ab, bartext);
  2161. }
  2162. // red bar
  2163. if (vol3 > MXR_DEBUG_REDLIMIT)
  2164. {
  2165. //flXposBar = flXpos + MXR_DEBUG_REDSTART;
  2166. rb = 255; gb = 0; bb = 0;
  2167. Q_memset(bartext, 0, sizeof(bartext));
  2168. cbars = (int)((float)vol3 * (float)MXR_DEBUG_VOLSCALE);
  2169. cbars = clamp(cbars, 0, MXR_DEBUG_VOLSCALE*3-1);
  2170. Q_memset(bartext, MXR_DEBUG_CHAR, cbars);
  2171. CDebugOverlay::AddScreenTextOverlay(flXposBar, flYpos, duration, rb, gb, bb,ab, bartext);
  2172. }
  2173. flYpos += MXR_DEBUG_INCY;
  2174. }
  2175. }
  2176. void MXR_DebugShowMixVolumes( void )
  2177. {
  2178. if (snd_showmixer.GetInt() == 0)
  2179. return;
  2180. // for the current soundmixer:
  2181. // make a totalvolume bucket for each mixgroup type in the soundmixer.
  2182. // for every active channel, add its spatialized volume to
  2183. // totalvolume bucket for that channel's selected mixgroup
  2184. // display all mixgroup/volume/totalvolume values as horizontal bars
  2185. debug_showvols_t groupvols[CMXRGROUPMAX];
  2186. int i;
  2187. int cgroups = 0;
  2188. if (g_isoundmixer < 0)
  2189. {
  2190. DevMsg("No sound mixer selected!");
  2191. return;
  2192. }
  2193. soundmixer_t *pmixer = &g_soundmixers[g_isoundmixer];
  2194. // for every entry in mapMixgroupidToVolume which is not -1,
  2195. // set up groupvols
  2196. for (i = 0; i < CMXRGROUPMAX; i++)
  2197. {
  2198. // currently sound mixers are required and entry per mix group for anything to work!!
  2199. // TODO: change this and make sound mixers operate sparsely, like layers
  2200. if (pmixer->mapMixgroupidToVolume[i] >= 0)
  2201. {
  2202. groupvols[cgroups].mixgroupid = i;
  2203. groupvols[cgroups].psz = MXR_GetGroupnameFromId( i );
  2204. groupvols[cgroups].totalvol = 0.0;
  2205. groupvols[cgroups].vol = pmixer->mapMixgroupidToVolume[i] * g_mastermixlayer.mapMixgroupidToVolume[i];
  2206. cgroups++;
  2207. }
  2208. }
  2209. // for every active channel, get its volume and
  2210. // the selected mixgroupid, add to groupvols totalvol
  2211. CChannelList list;
  2212. int ch_idx;
  2213. channel_t *pchan;
  2214. g_ActiveChannels.GetActiveChannels( list );
  2215. for ( i = 0; i < list.Count(); i++ )
  2216. {
  2217. ch_idx = list.GetChannelIndex(i);
  2218. pchan = &channels[ch_idx];
  2219. if (pchan->last_vol > 0.0)
  2220. {
  2221. // find entry in groupvols
  2222. for (int j = 0; j < CMXRGROUPMAX; j++)
  2223. {
  2224. if (pchan->last_mixgroupid == groupvols[j].mixgroupid)
  2225. {
  2226. groupvols[j].totalvol += pchan->last_vol;
  2227. break;
  2228. }
  2229. }
  2230. }
  2231. }
  2232. // groupvols is now fully initialized - just display it
  2233. MXR_DebugGraphMixVolumes( groupvols, cgroups);
  2234. }
  2235. #ifdef _DEBUG
  2236. // set the named mixgroup volume to vol for the current soundmixer
  2237. void MXR_DebugSetMixGroupVolume( const CCommand &args )
  2238. {
  2239. if ( args.ArgC() != 3 )
  2240. {
  2241. DevMsg("Parameters: mix group name, volume");
  2242. return;
  2243. }
  2244. const char *szgroupname = args[1];
  2245. float vol = atof( args[2] );
  2246. int imixgroup = MXR_GetMixgroupFromName( szgroupname );
  2247. if ( g_isoundmixer < 0 )
  2248. return;
  2249. soundmixer_t *pmixer = &g_soundmixers[g_isoundmixer];
  2250. pmixer->mapMixgroupidToVolume[imixgroup] = vol;
  2251. }
  2252. #endif //_DEBUG