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.

5438 lines
164 KiB

  1. //========= Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Portable code to mix sounds for snd_dma.cpp.
  4. //
  5. //=============================================================================//
  6. #include "audio_pch.h"
  7. #include "mouthinfo.h"
  8. #include "../../cl_main.h"
  9. #include "icliententitylist.h"
  10. #include "icliententity.h"
  11. #include "../../sys_dll.h"
  12. #include "avi/iavi.h"
  13. #include "snd_op_sys/sos_system.h"
  14. #include "tier0/cache_hints.h"
  15. #ifdef GNUC
  16. // we don't suport the ASM in this file right now under GCC, fallback to C libs
  17. #undef id386
  18. #endif
  19. // memdbgon must be the last include file in a .cpp file!!!
  20. #include "tier0/memdbgon.h"
  21. #if defined(_WIN32) && id386
  22. // warning C4731: frame pointer register 'ebp' modified by inline assembly code
  23. #pragma warning(disable : 4731)
  24. #endif
  25. // NOTE: !!!!!! YOU MUST UPDATE SND_MIXA.S IF THIS VALUE IS CHANGED !!!!!
  26. #define SND_SCALE_BITS 7
  27. #define SND_SCALE_SHIFT (8-SND_SCALE_BITS)
  28. #define SND_SCALE_LEVELS (1<<SND_SCALE_BITS)
  29. #define SND_SCALE_BITS16 8
  30. #define SND_SCALE_SHIFT16 (8-SND_SCALE_BITS16)
  31. #define SND_SCALE_LEVELS16 (1<<SND_SCALE_BITS16)
  32. // In debug, we are going to compare the old code and the new code and make sure we get the exact same output
  33. #if _DEBUG
  34. # define CHECK_VALUES_AFTER_REFACTORING 1
  35. # define CULLED_VOLUME 0 // If we check value, we can't cull the volume (otherwise will create false-positive asserts)
  36. portable_samplepair_t * DuplicateSamplePairs(portable_samplepair_t * pInputBuffer, int nSampleCount);
  37. void FreeDuplicatedSamplePairs( portable_samplepair_t * pInputBuffer, int nSampleCount );
  38. #else
  39. # define CHECK_VALUES_AFTER_REFACTORING 0
  40. # define CULLED_VOLUME 1 // Volume of 1 or less will be culled
  41. #endif
  42. ConVar snd_mix_optimization( "snd_mix_optimization", "0", FCVAR_NONE, "Turns optimization on for mixing if set to 1 (default). 0 to turn the optimization off." );
  43. ConVar snd_mix_soundchar_enabled( "snd_mix_soundchar_enabled", "1", FCVAR_NONE, "Turns sound char on for mixing if set to 1 (default). 0 to turn the sound char off and use default behavior (spatial instead of doppler, directional, etc...)." );
  44. ConVar snd_hrtf_volume("snd_hrtf_volume", "0.8", FCVAR_CHEAT, "Controls volume of HRTF sounds");
  45. #define SKIP_MIXING_IF_TOTAL_VOLUME_LESS_OR_EQUAL_THAN 0
  46. void Snd_WriteLinearBlastStereo16(void);
  47. void SND_PaintChannelFrom8( portable_samplepair_t *pOutput, int *volume, byte *pData8, int count );
  48. bool Con_IsVisible( void );
  49. void SND_RecordBuffer( void );
  50. bool DSP_RoomDSPIsOff( void );
  51. bool BChannelLowVolume( channel_t *pch, float vol_min );
  52. void ChannelCopyVolumes( channel_t *pch, float *pvolume_dest, int ivol_start, int cvol );
  53. float ChannelLoudestCurVolume( const channel_t * RESTRICT pch );
  54. extern int64 g_soundtime;
  55. extern float host_frametime;
  56. extern float host_frametime_unbounded;
  57. extern CScratchPad g_scratchpad;
  58. #if !defined( NO_VOICE )
  59. extern int g_SND_VoiceOverdriveInt;
  60. #endif
  61. extern ConVar dsp_room;
  62. extern ConVar dsp_water;
  63. extern ConVar dsp_player;
  64. extern ConVar dsp_facingaway;
  65. extern ConVar snd_showstart;
  66. extern ConVar dsp_automatic;
  67. extern ConVar snd_pitchquality;
  68. extern float DSP_ROOM_MIX;
  69. extern float DSP_NOROOM_MIX;
  70. portable_samplepair_t *g_paintbuffer;
  71. // temp paintbuffer - not included in main list of paintbuffers
  72. // NOTE: this paintbuffer is also used as a copy buffer by interpolating pitch
  73. // shift routines. Decreasing TEMP_COPY_BUFFER_SIZE (or PAINTBUFFER_MEM_SIZE)
  74. // will decrease the maximum pitch level (current 4.0)!
  75. portable_samplepair_t *g_temppaintbuffer = NULL;
  76. paintbuffer_t *g_paintBuffers = NULL;
  77. #define IPAINTBUFFER 0
  78. #define IROOMBUFFER 1
  79. #define IFACINGBUFFER 2
  80. #define IFACINGAWAYBUFFER 3
  81. #define IDRYBUFFER 4
  82. #define ISPEAKERBUFFER 5
  83. // pointer to current paintbuffer (front and rear), used by all mixing, upsampling and dsp routines
  84. portable_samplepair_t *g_curpaintbuffer = NULL;
  85. portable_samplepair_t *g_currearpaintbuffer = NULL;
  86. portable_samplepair_t *g_curcenterpaintbuffer = NULL;
  87. bool g_bdirectionalfx;
  88. bool g_bDspOff;
  89. float g_dsp_volume;
  90. // dsp performance timing
  91. unsigned g_snd_call_time_debug = 0;
  92. unsigned g_snd_time_debug = 0;
  93. unsigned g_snd_count_debug = 0;
  94. unsigned g_snd_samplecount = 0;
  95. unsigned g_snd_frametime = 0;
  96. unsigned g_snd_frametime_total = 0;
  97. int g_snd_profile_type = 0; // type 1 dsp, type 2 mixer, type 3 load sound, type 4 all sound
  98. #define FILTERTYPE_NONE 0
  99. #define FILTERTYPE_LINEAR 1
  100. #define FILTERTYPE_CUBIC 2
  101. // filter memory for upsampling
  102. portable_samplepair_t cubicfilter1[3] = {{0,0},{0,0},{0,0}};
  103. portable_samplepair_t cubicfilter2[3] = {{0,0},{0,0},{0,0}};
  104. portable_samplepair_t linearfilter1[1] = {0,0};
  105. portable_samplepair_t linearfilter2[1] = {0,0};
  106. portable_samplepair_t linearfilter3[1] = {0,0};
  107. portable_samplepair_t linearfilter4[1] = {0,0};
  108. portable_samplepair_t linearfilter5[1] = {0,0};
  109. portable_samplepair_t linearfilter6[1] = {0,0};
  110. portable_samplepair_t linearfilter7[1] = {0,0};
  111. portable_samplepair_t linearfilter8[1] = {0,0};
  112. int snd_scaletable[SND_SCALE_LEVELS][256]; // 32k*4 = 128K
  113. int *snd_p, snd_linear_count, snd_vol;
  114. short *snd_out;
  115. extern CThreadFastMutex g_SoundMapMutex; // From snd_dma.cpp
  116. bool DSP_CheckDspAutoEnabled( void );
  117. int Get_idsp_room ( void );
  118. int dsp_room_GetInt ( void );
  119. void DSP_SetDspAuto( int dsp_preset );
  120. bool DSP_CheckDspAutoEnabled( void );
  121. // Get a pointer to a buffer that summarizes the state of the audio system, for
  122. // ETW or crash dump purposes. It is char* instead of const char* so that we
  123. // can optionally convert line-feeds to tabs in-place for better ETW compatibility.
  124. char* Status_UpdateAudioBuffer()
  125. {
  126. static char buffer[4094];
  127. buffer[0] = 0;
  128. CUtlBuffer buf( buffer, sizeof(buffer), CUtlBuffer::TEXT_BUFFER );
  129. buf.Printf( "Audio: total_channels=%d, Active Channels=%d, g_bdirectionalfx=%d, g_bDspOff=%d\n", total_channels, g_ActiveChannels.GetActiveCount(), g_bdirectionalfx, g_bDspOff );
  130. g_ActiveChannels.DumpChannelInfo( buf );
  131. return buffer;
  132. }
  133. void MIX_ScalePaintBuffer( int bufferIndex, int count, float fgain );
  134. //-----------------------------------------------------------------------------
  135. // Free allocated memory buffers
  136. //-----------------------------------------------------------------------------
  137. void MIX_FreeAllPaintbuffers(void)
  138. {
  139. if ( g_paintBuffers )
  140. {
  141. if ( g_temppaintbuffer )
  142. {
  143. _aligned_free( g_temppaintbuffer );
  144. g_temppaintbuffer = NULL;
  145. }
  146. for ( int i = 0; i < CPAINTBUFFERS; i++ )
  147. {
  148. if ( g_paintBuffers[i].pbuf )
  149. {
  150. _aligned_free( g_paintBuffers[i].pbuf );
  151. }
  152. if ( g_paintBuffers[i].pbufrear )
  153. {
  154. _aligned_free( g_paintBuffers[i].pbufrear );
  155. }
  156. if ( g_paintBuffers[i].pbufcenter )
  157. {
  158. _aligned_free( g_paintBuffers[i].pbufcenter );
  159. }
  160. }
  161. free( g_paintBuffers );
  162. g_paintBuffers = NULL;
  163. }
  164. }
  165. //-----------------------------------------------------------------------------
  166. // Allocate memory buffers
  167. // Initialize paintbuffers array, set current paint buffer to main output buffer IPAINTBUFFER
  168. //-----------------------------------------------------------------------------
  169. bool MIX_InitAllPaintbuffers(void)
  170. {
  171. bool bSurround;
  172. bool bSurroundCenter;
  173. int i;
  174. bSurroundCenter = g_AudioDevice->IsSurroundCenter();
  175. bSurround = g_AudioDevice->IsSurround() || bSurroundCenter;
  176. g_paintBuffers = (paintbuffer_t *)malloc( CPAINTBUFFERS*sizeof( paintbuffer_t ) );
  177. V_memset( g_paintBuffers, 0, CPAINTBUFFERS*sizeof( paintbuffer_t ) );
  178. g_temppaintbuffer = (portable_samplepair_t*)_aligned_malloc( TEMP_COPY_BUFFER_SIZE*sizeof(portable_samplepair_t), 16 );
  179. V_memset( g_temppaintbuffer, 0, TEMP_COPY_BUFFER_SIZE*sizeof(portable_samplepair_t) );
  180. for ( i=0; i<CPAINTBUFFERS; i++ )
  181. {
  182. g_paintBuffers[i].pbuf = (portable_samplepair_t *)_aligned_malloc( PAINTBUFFER_MEM_SIZE*sizeof(portable_samplepair_t), 16 );
  183. V_memset( g_paintBuffers[i].pbuf, 0, PAINTBUFFER_MEM_SIZE*sizeof(portable_samplepair_t) );
  184. if ( bSurround )
  185. {
  186. g_paintBuffers[i].pbufrear = (portable_samplepair_t *)_aligned_malloc( PAINTBUFFER_MEM_SIZE*sizeof(portable_samplepair_t), 16 );
  187. V_memset( g_paintBuffers[i].pbufrear, 0, PAINTBUFFER_MEM_SIZE*sizeof(portable_samplepair_t) );
  188. }
  189. if ( bSurroundCenter )
  190. {
  191. g_paintBuffers[i].pbufcenter = (portable_samplepair_t *)_aligned_malloc( PAINTBUFFER_MEM_SIZE*sizeof(portable_samplepair_t), 16 );
  192. V_memset( g_paintBuffers[i].pbufcenter, 0, PAINTBUFFER_MEM_SIZE*sizeof(portable_samplepair_t) );
  193. }
  194. }
  195. g_paintbuffer = g_paintBuffers[IPAINTBUFFER].pbuf;
  196. // buffer flags
  197. g_paintBuffers[IROOMBUFFER].flags = SOUND_BUSS_ROOM;
  198. g_paintBuffers[IFACINGBUFFER].flags = SOUND_BUSS_FACING;
  199. g_paintBuffers[IFACINGAWAYBUFFER].flags = SOUND_BUSS_FACINGAWAY;
  200. g_paintBuffers[ISPEAKERBUFFER].flags = SOUND_BUSS_SPEAKER;
  201. g_paintBuffers[IDRYBUFFER].flags = SOUND_BUSS_DRY;
  202. // buffer surround sound flag
  203. g_paintBuffers[IPAINTBUFFER].fsurround = bSurround;
  204. g_paintBuffers[IFACINGBUFFER].fsurround = bSurround;
  205. g_paintBuffers[IFACINGAWAYBUFFER].fsurround = bSurround;
  206. g_paintBuffers[IDRYBUFFER].fsurround = bSurround;
  207. // buffer 5 channel surround sound flag
  208. g_paintBuffers[IPAINTBUFFER].fsurround_center = bSurroundCenter;
  209. g_paintBuffers[IFACINGBUFFER].fsurround_center = bSurroundCenter;
  210. g_paintBuffers[IFACINGAWAYBUFFER].fsurround_center = bSurroundCenter;
  211. g_paintBuffers[IDRYBUFFER].fsurround_center = bSurroundCenter;
  212. // room buffer mixes down to mono or stereo, never to 4 or 5 ch
  213. g_paintBuffers[IROOMBUFFER].fsurround = false;
  214. g_paintBuffers[IROOMBUFFER].fsurround_center = false;
  215. // speaker buffer mixes to mono
  216. g_paintBuffers[ISPEAKERBUFFER].fsurround = false;
  217. g_paintBuffers[ISPEAKERBUFFER].fsurround_center = false;
  218. MIX_SetCurrentPaintbuffer( IPAINTBUFFER );
  219. return true;
  220. }
  221. // called before loading samples to mix - cap the mix rate (ie: pitch) so that
  222. // we never overflow the mix copy buffer.
  223. double MIX_GetMaxRate( double rate, int sampleCount )
  224. {
  225. if (rate <= 2.0)
  226. return rate;
  227. // copybuf_bytes = rate_max * samples_max * samplesize_max
  228. // so:
  229. // rate_max = copybuf_bytes / (samples_max * samplesize_max )
  230. double samplesize_max = 4.0; // stereo 16bit samples
  231. double copybuf_bytes = (double)(TEMP_COPY_BUFFER_SIZE * sizeof(portable_samplepair_t));
  232. double samples_max = (double)(PAINTBUFFER_SIZE);
  233. double rate_max = copybuf_bytes / (samples_max * samplesize_max);
  234. // make sure sampleCount is never greater than paintbuffer samples
  235. // (this should have been set up in MIX_PaintChannels)
  236. Assert (sampleCount <= PAINTBUFFER_SIZE);
  237. return fpmin( rate, rate_max );
  238. }
  239. // Transfer (endtime - lpaintedtime) stereo samples in pfront out to hardware
  240. // pfront - pointer to stereo paintbuffer - 32 bit samples, interleaved stereo
  241. // lpaintedtime - total number of 32 bit stereo samples previously output to hardware
  242. // endtime - total number of 32 bit stereo samples currently mixed in paintbuffer
  243. #if USE_AUDIO_DEVICE_V1
  244. void S_TransferStereo16( void *pOutput, const portable_samplepair_t *pfront, int64 lpaintedtime, int64 endtime )
  245. {
  246. int lpos;
  247. if ( IsX360() )
  248. {
  249. // not the right path for 360
  250. Assert( 0 );
  251. return;
  252. }
  253. Assert( pOutput );
  254. snd_vol = S_GetMasterVolume()*256;
  255. snd_p = (int *)pfront;
  256. // get size of output buffer in full samples (LR pairs)
  257. int samplePairCount = g_AudioDevice->DeviceSampleCount() >> 1;
  258. int sampleMask = samplePairCount - 1;
  259. bool bShouldPlaySound = !cl_movieinfo.IsRecording();
  260. while ( lpaintedtime < endtime )
  261. {
  262. // pbuf can hold 16384, 16 bit L/R samplepairs.
  263. // lpaintedtime - where to start painting into dma buffer.
  264. // (modulo size of dma buffer for current position).
  265. // handle recirculating buffer issues
  266. // lpos - samplepair index into dma buffer. First samplepair from paintbuffer to be xfered here.
  267. lpos = lpaintedtime & sampleMask;
  268. // snd_out is L/R sample index into dma buffer. First L sample from paintbuffer goes here.
  269. snd_out = (short *)pOutput + (lpos<<1);
  270. // snd_linear_count is number of samplepairs between end of dma buffer and xfer start index.
  271. snd_linear_count = samplePairCount - lpos;
  272. // clamp snd_linear_count to be only as many samplepairs premixed
  273. if ( snd_linear_count > endtime - lpaintedtime )
  274. {
  275. // endtime - lpaintedtime = number of premixed sample pairs ready for xfer.
  276. snd_linear_count = endtime - lpaintedtime;
  277. }
  278. // snd_linear_count is now number of mono 16 bit samples (L and R) to xfer.
  279. snd_linear_count <<= 1;
  280. // write a linear blast of samples
  281. SND_RecordBuffer();
  282. if ( bShouldPlaySound )
  283. {
  284. // transfer 16bit samples from snd_p into snd_out, multiplying each sample by volume.
  285. Snd_WriteLinearBlastStereo16();
  286. }
  287. // advance paintbuffer pointer
  288. snd_p += snd_linear_count;
  289. // advance lpaintedtime by number of samplepairs just xfered.
  290. lpaintedtime += (snd_linear_count>>1);
  291. }
  292. }
  293. #endif
  294. /*
  295. ===============================================================================
  296. CHANNEL MIXING
  297. ===============================================================================
  298. */
  299. // free channel so that it may be allocated by the
  300. // next request to play a sound. If sound is a
  301. // word in a sentence, release the sentence.
  302. // Works for static, dynamic, sentence and stream sounds
  303. extern ConVar snd_find_channel;
  304. void PrintChannel( const char *pText1, const char *pFileName, channel_t * pChannel, const char *pText2 = NULL );
  305. void S_FreeChannel(channel_t *ch)
  306. {
  307. // Don't reenter in here (can happen inside voice code).
  308. if ( ch->flags.m_bIsFreeingChannel )
  309. return;
  310. ch->flags.m_bIsFreeingChannel = true;
  311. if ( (*snd_find_channel.GetString()) != '\0' )
  312. {
  313. if ( ch->sfx != NULL )
  314. {
  315. char sndname[MAX_PATH];
  316. ch->sfx->GetFileName( sndname, sizeof( sndname ) );
  317. if ( Q_stristr( sndname, snd_find_channel.GetString() ) != 0 )
  318. {
  319. PrintChannel( "FreeChannel", sndname, ch, "from ConVar snd_find_channel." );
  320. }
  321. }
  322. }
  323. SND_CloseMouth(ch);
  324. if ( !IsGameConsole() )
  325. {
  326. char nameBuf[MAX_PATH];
  327. g_pSoundServices->OnSoundStopped( ch->guid, ch->soundsource, ch->entchannel, ch->sfx->getname(nameBuf, sizeof(nameBuf)) );
  328. }
  329. ch->flags.isSentence = false;
  330. // Msg("End sound %s\n", ch->sfx->getname() );
  331. delete ch->pMixer;
  332. ch->pMixer = NULL;
  333. ch->sfx = NULL;
  334. ch->m_nSoundScriptHash = SOUNDEMITTER_INVALID_HASH;
  335. if( ch->m_pStackList )
  336. {
  337. delete ch->m_pStackList;
  338. ch->m_pStackList = NULL;
  339. }
  340. // zero all data in channel
  341. g_ActiveChannels.Remove( ch );
  342. Q_memset(ch, 0, sizeof(channel_t));
  343. }
  344. extern ConVar host_timescale;
  345. ConVar snd_pause_all( "snd_pause_all", "1", FCVAR_CHEAT, "Specifies to pause all sounds and not just voice" );
  346. // Mix all channels into active paintbuffers until paintbuffer is full or 'endtime' is reached.
  347. // endtime: time in 44khz samples to mix
  348. // rate: ignore samples which are not natively at this rate (for multipass mixing/filtering)
  349. // if rate == SOUND_ALL_RATES then mix all samples this pass
  350. // flags: if SOUND_MIX_DRY, then mix only samples with channel flagged as 'dry'
  351. // outputRate: target mix rate for all samples. Note, if outputRate = SOUND_DMA_SPEED, then
  352. // this routine will fill the paintbuffer to endtime. Otherwise, fewer samples are mixed.
  353. // if (endtime - paintedtime) is not aligned on boundaries of 4,
  354. // we'll miss data if outputRate < SOUND_DMA_SPEED!
  355. void MIX_MixChannelsToPaintbuffer( CChannelList &list, int64 endtime, int flags, int rate, int outputRate )
  356. {
  357. VPROF( "MixChannelsToPaintbuffer" );
  358. int i;
  359. int sampleCount;
  360. // mix each channel into paintbuffer
  361. // validate parameters
  362. Assert( outputRate <= SOUND_DMA_SPEED );
  363. Assert( !((endtime - g_paintedtime) & 0x3) || (outputRate == SOUND_DMA_SPEED) ); // make sure we're not discarding data
  364. // 44k: try to mix this many samples at outputRate
  365. sampleCount = ( endtime - g_paintedtime ) / ( SOUND_DMA_SPEED / outputRate );
  366. if ( sampleCount <= 0 )
  367. return;
  368. // Apply host_timescale as a global pitch shift
  369. float flGlobalPitchScale = host_timescale.GetFloat();
  370. extern IVEngineClient *engineClient;
  371. if ( engineClient )
  372. {
  373. flGlobalPitchScale = engineClient->GetTimescale();
  374. }
  375. for ( i = list.Count(); --i >= 0; )
  376. {
  377. channel_t *ch = list.GetChannel( i );
  378. Assert( ch->sfx );
  379. // must never have a 'dry' and 'speaker' set - causes double mixing & double data reading
  380. Assert ( !( ch->flags.bdry && ch->flags.bSpeaker ) );
  381. // if mixing with SOUND_MIX_DRY flag, ignore (don't even load) all channels not flagged as 'dry'
  382. if ( flags == SOUND_MIX_DRY )
  383. {
  384. if ( !ch->flags.bdry )
  385. continue;
  386. }
  387. // if mixing with SOUND_MIX_WET flag, ignore (don't even load) all channels flagged as 'dry' or 'speaker'
  388. if ( flags == SOUND_MIX_WET )
  389. {
  390. if ( ch->flags.bdry || ch->flags.bSpeaker )
  391. continue;
  392. }
  393. // if mixing with SOUND_MIX_SPEAKER flag, ignore (don't even load) all channels not flagged as 'speaker'
  394. if ( flags == SOUND_MIX_SPEAKER )
  395. {
  396. if ( !ch->flags.bSpeaker )
  397. continue;
  398. }
  399. // multipass mixing - only mix samples of specified sample rate
  400. switch ( rate )
  401. {
  402. case SOUND_11k:
  403. case SOUND_22k:
  404. case SOUND_44k:
  405. if ( rate != ch->sfx->pSource->SampleRate() )
  406. continue;
  407. break;
  408. default:
  409. case SOUND_ALL_RATES:
  410. break;
  411. }
  412. // Tracker 20771, if breen is speaking through the monitor, the client doesn't have an entity
  413. // for the "soundsource" but we still need the lipsync to pause if the game is paused. Therefore
  414. // I changed SND_IsMouth to look for any .wav on any channels which has sentence data
  415. bool bIsMouth = ch->flags.m_bHasMouth;
  416. bool bShouldPause = IsGameConsole() ? !ch->sfx->m_bIsUISound : bIsMouth;
  417. if( snd_pause_all.GetInt() )
  418. {
  419. bShouldPause = !ch->sfx->m_bIsUISound;
  420. }
  421. // Tracker 14637: Pausing the game pauses voice sounds, but not other sounds...
  422. if ( bShouldPause && g_pSoundServices->IsGamePaused() )
  423. {
  424. continue;
  425. }
  426. if ( bIsMouth && ch->flags.m_bHasMouth )
  427. {
  428. SND_MoveMouth8(ch, ch->sfx->pSource, sampleCount);
  429. }
  430. // mix channel to all active paintbuffers:
  431. // mix 'dry' sounds only to dry paintbuffer.
  432. // mix 'speaker' sounds only to speaker paintbuffer.
  433. // mix all other sounds between room, facing & facingaway paintbuffers
  434. // NOTE: must be called once per channel only - consecutive calls retrieve additional data.
  435. float flPitch = ch->pitch;
  436. ch->pitch *= flGlobalPitchScale;
  437. if (list.IsQuashed(i))
  438. {
  439. // If the sound has been silenced as a performance heuristic, quash it.
  440. ch->pMixer->SkipSamples( ch, sampleCount, outputRate, 0 );
  441. // DevMsg("Quashed channel %d (%s)\n", i, ch->sfx->GetFileName());
  442. }
  443. else
  444. {
  445. ch->pMixer->MixDataToDevice( ch, sampleCount, outputRate, 0 );
  446. }
  447. // restore to original pitch settings
  448. ch->pitch = flPitch;
  449. if ( !ch->pMixer->ShouldContinueMixing() )
  450. {
  451. // stopping due to file elapsing
  452. if( ch->m_pStackList )
  453. {
  454. ch->m_pStackList->Execute( CSosOperatorStack::SOS_STOP, ch, &g_scratchpad );
  455. }
  456. S_FreeChannel( ch );
  457. list.RemoveChannelFromList(i);
  458. }
  459. }
  460. }
  461. // pass in index -1...count+2, return pointer to source sample in either paintbuffer or delay buffer
  462. inline portable_samplepair_t * S_GetNextpFilter(int i, portable_samplepair_t *pbuffer, portable_samplepair_t *pfiltermem)
  463. {
  464. // The delay buffer is assumed to precede the paintbuffer by 6 duplicated samples
  465. if (i == -1)
  466. return (&(pfiltermem[0]));
  467. if (i == 0)
  468. return (&(pfiltermem[1]));
  469. if (i == 1)
  470. return (&(pfiltermem[2]));
  471. // return from paintbuffer, where samples are doubled.
  472. // even samples are to be replaced with interpolated value.
  473. return (&(pbuffer[(i-2)*2 + 1]));
  474. }
  475. // pass forward over passed in buffer and cubic interpolate all odd samples
  476. // pbuffer: buffer to filter (in place)
  477. // prevfilter: filter memory. NOTE: this must match the filtertype ie: filtercubic[] for FILTERTYPE_CUBIC
  478. // if NULL then perform no filtering. UNDONE: should have a filter memory array type
  479. // count: how many samples to upsample. will become count*2 samples in buffer, in place.
  480. void S_Interpolate2xCubic( portable_samplepair_t *pbuffer, portable_samplepair_t *pfiltermem, int cfltmem, int count )
  481. {
  482. // implement cubic interpolation on 2x upsampled buffer. Effectively delays buffer contents by 2 samples.
  483. // pbuffer: contains samples at 0, 2, 4, 6...
  484. // temppaintbuffer is temp buffer, of same or larger size than a paintbuffer, used to store processed values
  485. // count: number of samples to process in buffer ie: how many samples at 0, 2, 4, 6...
  486. // finpos is the fractional, inpos the integer part.
  487. // finpos = 0.5 for upsampling by 2x
  488. // inpos is the position of the sample
  489. // xm1 = x [inpos - 1];
  490. // x0 = x [inpos + 0];
  491. // x1 = x [inpos + 1];
  492. // x2 = x [inpos + 2];
  493. // a = (3 * (x0-x1) - xm1 + x2) / 2;
  494. // b = 2*x1 + xm1 - (5*x0 + x2) / 2;
  495. // c = (x1 - xm1) / 2;
  496. // y [outpos] = (((a * finpos) + b) * finpos + c) * finpos + x0;
  497. int i, upCount = count << 1;
  498. int a, b, c;
  499. int xm1, x0, x1, x2;
  500. portable_samplepair_t *psamp0;
  501. portable_samplepair_t *psamp1;
  502. portable_samplepair_t *psamp2;
  503. portable_samplepair_t *psamp3;
  504. int outpos = 0;
  505. Assert (upCount <= PAINTBUFFER_SIZE);
  506. // pfiltermem holds 6 samples from previous buffer pass
  507. // process 'count' samples
  508. for ( i = 0; i < count; i++)
  509. {
  510. // get source sample pointer
  511. psamp0 = S_GetNextpFilter(i-1, pbuffer, pfiltermem);
  512. psamp1 = S_GetNextpFilter(i, pbuffer, pfiltermem);
  513. psamp2 = S_GetNextpFilter(i+1, pbuffer, pfiltermem);
  514. psamp3 = S_GetNextpFilter(i+2, pbuffer, pfiltermem);
  515. // write out original sample to interpolation buffer
  516. g_temppaintbuffer[outpos++] = *psamp1;
  517. // get all left samples for interpolation window
  518. xm1 = psamp0->left;
  519. x0 = psamp1->left;
  520. x1 = psamp2->left;
  521. x2 = psamp3->left;
  522. // interpolate
  523. a = (3 * (x0-x1) - xm1 + x2) / 2;
  524. b = 2*x1 + xm1 - (5*x0 + x2) / 2;
  525. c = (x1 - xm1) / 2;
  526. // write out interpolated sample
  527. g_temppaintbuffer[outpos].left = a/8 + b/4 + c/2 + x0;
  528. // get all right samples for window
  529. xm1 = psamp0->right;
  530. x0 = psamp1->right;
  531. x1 = psamp2->right;
  532. x2 = psamp3->right;
  533. // interpolate
  534. a = (3 * (x0-x1) - xm1 + x2) / 2;
  535. b = 2*x1 + xm1 - (5*x0 + x2) / 2;
  536. c = (x1 - xm1) / 2;
  537. // write out interpolated sample, increment output counter
  538. g_temppaintbuffer[outpos++].right = a/8 + b/4 + c/2 + x0;
  539. Assert( outpos <= TEMP_COPY_BUFFER_SIZE );
  540. }
  541. Assert(cfltmem >= 3);
  542. // save last 3 samples from paintbuffer
  543. pfiltermem[0] = pbuffer[upCount - 5];
  544. pfiltermem[1] = pbuffer[upCount - 3];
  545. pfiltermem[2] = pbuffer[upCount - 1];
  546. // copy temppaintbuffer back into paintbuffer
  547. for (i = 0; i < upCount; i++)
  548. pbuffer[i] = g_temppaintbuffer[i];
  549. }
  550. // pass forward over passed in buffer and linearly interpolate all odd samples
  551. // pbuffer: buffer to filter (in place)
  552. // prevfilter: filter memory. NOTE: this must match the filtertype ie: filterlinear[] for FILTERTYPE_LINEAR
  553. // if NULL then perform no filtering.
  554. // count: how many samples to upsample. will become count*2 samples in buffer, in place.
  555. void S_Interpolate2xLinear( portable_samplepair_t *pbuffer, portable_samplepair_t *pfiltermem, int cfltmem, int count )
  556. {
  557. int i, upCount = count<<1;
  558. Assert (upCount <= PAINTBUFFER_SIZE);
  559. Assert (cfltmem >= 1);
  560. // use interpolation value from previous mix
  561. pbuffer[0].left = (pfiltermem->left + pbuffer[0].left) >> 1;
  562. pbuffer[0].right = (pfiltermem->right + pbuffer[0].right) >> 1;
  563. for ( i = 2; i < upCount; i+=2)
  564. {
  565. // use linear interpolation for upsampling
  566. pbuffer[i].left = (pbuffer[i].left + pbuffer[i-1].left) >> 1;
  567. pbuffer[i].right = (pbuffer[i].right + pbuffer[i-1].right) >> 1;
  568. }
  569. // save last value to be played out in buffer
  570. *pfiltermem = pbuffer[upCount - 1];
  571. }
  572. // Optimized routine. 2.27X faster than the above routine
  573. void S_Interpolate2xLinear_2( int count, portable_samplepair_t *pbuffer, portable_samplepair_t *pfiltermem, int cfltmem )
  574. {
  575. Assert (cfltmem >= 1);
  576. int sample = count-1;
  577. int end = (count*2)-1;
  578. portable_samplepair_t *pwrite = &pbuffer[end];
  579. portable_samplepair_t *pread = &pbuffer[sample];
  580. portable_samplepair_t last = pread[0];
  581. pread--;
  582. // PERFORMANCE: Unroll the loop 8 times. This improves speed quite a bit
  583. // Looking at this code, there is a potential to make it SIMD friendly, the logic is simple, don't know if that would save though.
  584. for ( ;sample >= 8; sample -= 8 )
  585. {
  586. pwrite[0] = last;
  587. pwrite[-1].left = (pread[0].left + last.left)>>1;
  588. pwrite[-1].right = (pread[0].right + last.right)>>1;
  589. last = pread[0];
  590. pwrite[-2] = last;
  591. pwrite[-3].left = (pread[-1].left + last.left)>>1;
  592. pwrite[-3].right = (pread[-1].right + last.right)>>1;
  593. last = pread[-1];
  594. pwrite[-4] = last;
  595. pwrite[-5].left = (pread[-2].left + last.left)>>1;
  596. pwrite[-5].right = (pread[-2].right + last.right)>>1;
  597. last = pread[-2];
  598. pwrite[-6] = last;
  599. pwrite[-7].left = (pread[-3].left + last.left)>>1;
  600. pwrite[-7].right = (pread[-3].right + last.right)>>1;
  601. last = pread[-3];
  602. pwrite[-8] = last;
  603. pwrite[-9].left = (pread[-4].left + last.left)>>1;
  604. pwrite[-9].right = (pread[-4].right + last.right)>>1;
  605. last = pread[-4];
  606. pwrite[-10] = last;
  607. pwrite[-11].left = (pread[-5].left + last.left)>>1;
  608. pwrite[-11].right = (pread[-5].right + last.right)>>1;
  609. last = pread[-5];
  610. pwrite[-12] = last;
  611. pwrite[-13].left = (pread[-6].left + last.left)>>1;
  612. pwrite[-13].right = (pread[-6].right + last.right)>>1;
  613. last = pread[-6];
  614. pwrite[-14] = last;
  615. pwrite[-15].left = (pread[-7].left + last.left)>>1;
  616. pwrite[-15].right = (pread[-7].right + last.right)>>1;
  617. last = pread[-7];
  618. pread -= 8;
  619. pwrite -= 16;
  620. }
  621. while ( pread >= pbuffer )
  622. {
  623. pwrite[0] = last;
  624. pwrite[-1].left = (pread[0].left + last.left)>>1;
  625. pwrite[-1].right = (pread[0].right + last.right)>>1;
  626. last = pread[0];
  627. pread--;
  628. pwrite-=2;
  629. }
  630. pbuffer[1] = last;
  631. pbuffer[0].left = (pfiltermem->left + last.left) >> 1;
  632. pbuffer[0].right = (pfiltermem->right + last.right) >> 1;
  633. *pfiltermem = pbuffer[end];
  634. }
  635. FORCEINLINE
  636. void WriteLeftRight( portable_samplepair_t *pWriteBuffer, int nLeft, int nRight )
  637. {
  638. // This should be replaced by one instruction by the compiler on X360 and PS3.
  639. // Unfortunately it does not on X360, 4 instructions on top of the store. So do 2 stores instead like on PC.
  640. //int64 nValue = ( (int64)nLeft << 32L ) | ( (int64)nRight & 0xffffffff );
  641. //*(int64 *)pWriteBuffer = nValue;
  642. pWriteBuffer->left = nLeft;
  643. pWriteBuffer->right = nRight;
  644. }
  645. // Version with reduced LHS for console. (Optimized version ended up being much much slower than the "slow" version).
  646. // This should be as fast or faster on PC too.
  647. // TODO: Add code to compare before and after.
  648. void S_Interpolate2xLinear_3( int count, portable_samplepair_t *pbuffer, portable_samplepair_t *pfiltermem, int cfltmem )
  649. {
  650. Assert (cfltmem >= 1);
  651. int sample = count-1;
  652. int end = (count*2)-1;
  653. portable_samplepair_t *pwrite = &pbuffer[end];
  654. portable_samplepair_t *pread = &pbuffer[sample];
  655. int nLastLeft, nLastRight;
  656. nLastLeft = pread[0].left;
  657. nLastRight = pread[0].right;
  658. pread--;
  659. // PERFORMANCE: Unroll the loop 8 times. This improves speed quite a bit
  660. // Looking at this code, there is a potential to make it SIMD friendly, the logic is simple, don't know if that would save though.
  661. for ( ;sample >= 8; sample -= 8 )
  662. {
  663. WriteLeftRight( pwrite - 0, nLastLeft, nLastRight );
  664. // We also alternate between nLeft0|nRight0 and nLeft1|nRight1 to avoid storing temp values back and forth.
  665. int nLeft0, nRight0, nLeft1, nRight1;
  666. nLeft0 = pread[0].left;
  667. nRight0 = pread[0].right;
  668. WriteLeftRight( pwrite - 1, (nLeft0 + nLastLeft) >> 1, (nRight0 + nLastRight) >> 1 );
  669. WriteLeftRight( pwrite - 2, nLeft0, nRight0 );
  670. nLeft1 = pread[-1].left;
  671. nRight1 = pread[-1].right;
  672. WriteLeftRight( pwrite - 3, (nLeft1 + nLeft0) >> 1, (nRight1 + nRight0) >> 1 );
  673. WriteLeftRight( pwrite - 4, nLeft1, nRight1 );
  674. nLeft0 = pread[-2].left;
  675. nRight0 = pread[-2].right;
  676. WriteLeftRight( pwrite - 5, ( nLeft0 + nLeft1 ) >> 1, ( nRight0 + nRight1 ) >> 1 );
  677. WriteLeftRight( pwrite - 6, nLeft0, nRight0 );
  678. nLeft1 = pread[-3].left;
  679. nRight1 = pread[-3].right;
  680. WriteLeftRight( pwrite - 7, ( nLeft1 + nLeft0 ) >> 1, ( nRight1 + nRight0 ) >> 1 );
  681. WriteLeftRight( pwrite - 8, nLeft1, nRight1 );
  682. nLeft0 = pread[-4].left;
  683. nRight0 = pread[-4].right;
  684. WriteLeftRight( pwrite - 9, ( nLeft0 + nLeft1 ) >> 1, ( nRight0 + nRight1 ) >> 1 );
  685. WriteLeftRight( pwrite - 10, nLeft0, nRight0 );
  686. nLeft1 = pread[-5].left;
  687. nRight1 = pread[-5].right;
  688. WriteLeftRight( pwrite - 11, ( nLeft1 + nLeft0 ) >> 1, ( nRight1 + nRight0 ) >> 1 );
  689. WriteLeftRight( pwrite - 12, nLeft1, nRight1 );
  690. nLeft0 = pread[-6].left;
  691. nRight0 = pread[-6].right;
  692. WriteLeftRight( pwrite - 13, (nLeft0 + nLeft1 ) >> 1, (nRight0 + nRight1 ) >> 1 );
  693. WriteLeftRight( pwrite - 14, nLeft0, nRight0 );
  694. // Use nLastLeft and nLastRight for next iteration or final loop.
  695. nLastLeft = pread[-7].left;
  696. nLastRight = pread[-7].right;
  697. WriteLeftRight( pwrite - 15, (nLastLeft + nLeft0 ) >> 1, ( nLastRight + nRight0 ) >> 1 );
  698. pread -= 8;
  699. pwrite -= 16;
  700. }
  701. while ( pread >= pbuffer )
  702. {
  703. WriteLeftRight( pwrite - 0, nLastLeft, nLastRight );
  704. int nLeft = pread[0].left;
  705. int nRight = pread[0].right;
  706. WriteLeftRight( pwrite - 1, ( nLeft + nLastLeft ) >> 1, ( nRight + nLastRight ) >> 1 );
  707. nLastLeft = nLeft;
  708. nLastRight = nRight;
  709. pread--;
  710. pwrite-=2;
  711. }
  712. WriteLeftRight( pbuffer + 1, nLastLeft, nLastRight );
  713. WriteLeftRight( pbuffer + 0, (pfiltermem->left + nLastLeft) >> 1, (pfiltermem->right + nLastRight) >> 1);
  714. *pfiltermem = pbuffer[end];
  715. }
  716. // upsample by 2x, optionally using interpolation
  717. // count: how many samples to upsample. will become count*2 samples in buffer, in place.
  718. // pbuffer: buffer to upsample into (in place)
  719. // pfiltermem: filter memory. NOTE: this must match the filtertype ie: filterlinear[] for FILTERTYPE_LINEAR
  720. // if NULL then perform no filtering.
  721. // cfltmem: max number of sample pairs filter can use
  722. // filtertype: FILTERTYPE_NONE, _LINEAR, _CUBIC etc. Must match prevfilter.
  723. void S_MixBufferUpsample2x( int count, portable_samplepair_t *pbuffer, portable_samplepair_t *pfiltermem, int cfltmem, int filtertype )
  724. {
  725. // JAY: Optimized this routine. Test then remove old routine.
  726. // NOTE: Has been proven equivalent by comparing output.
  727. if ( filtertype == FILTERTYPE_LINEAR )
  728. {
  729. #if CHECK_VALUES_AFTER_REFACTORING
  730. portable_samplepair_t *pTempBuffer = (portable_samplepair_t *)alloca( 2 * count * sizeof(portable_samplepair_t) );
  731. memcpy( pTempBuffer, pbuffer, count * sizeof(portable_samplepair_t) ); // Copy the source data
  732. portable_samplepair_t oldFiltermem = *pfiltermem;
  733. // Run the older implementation on the temp buffer
  734. S_Interpolate2xLinear_2( count, pTempBuffer, &oldFiltermem, cfltmem );
  735. #endif
  736. // Run the faster implementation
  737. if ( snd_mix_optimization.GetBool() )
  738. {
  739. S_Interpolate2xLinear_3( count, pbuffer, pfiltermem, cfltmem );
  740. }
  741. else
  742. {
  743. S_Interpolate2xLinear_2( count, pbuffer, pfiltermem, cfltmem );
  744. }
  745. #if CHECK_VALUES_AFTER_REFACTORING
  746. bool bIsSame = ( memcmp( pbuffer, pTempBuffer, 2 * count * sizeof(portable_samplepair_t) ) == 0 );
  747. Assert( bIsSame );
  748. Assert( oldFiltermem.left == pfiltermem->left );
  749. Assert( oldFiltermem.right == pfiltermem->right );
  750. #endif
  751. return;
  752. }
  753. int i, j, upCount = count<<1;
  754. // reverse through buffer, duplicating contents for 'count' samples
  755. for (i = upCount - 1, j = count - 1; j >= 0; i-=2, j--)
  756. {
  757. pbuffer[i] = pbuffer[j];
  758. pbuffer[i-1] = pbuffer[j];
  759. }
  760. // pass forward through buffer, interpolate all even slots
  761. switch (filtertype)
  762. {
  763. default:
  764. break;
  765. case FILTERTYPE_LINEAR:
  766. S_Interpolate2xLinear(pbuffer, pfiltermem, cfltmem, count);
  767. break;
  768. case FILTERTYPE_CUBIC:
  769. S_Interpolate2xCubic(pbuffer, pfiltermem, cfltmem, count);
  770. break;
  771. }
  772. }
  773. //===============================================================================
  774. // PAINTBUFFER ROUTINES
  775. //===============================================================================
  776. // Set current paintbuffer to pbuf.
  777. // The set paintbuffer is used by all subsequent mixing, upsampling and dsp routines.
  778. // Also sets the rear paintbuffer if paintbuffer has fsurround true.
  779. // (otherwise, rearpaintbuffer is NULL)
  780. void MIX_SetCurrentPaintbuffer(int ipaintbuffer)
  781. {
  782. // set front and rear paintbuffer
  783. Assert(ipaintbuffer < CPAINTBUFFERS);
  784. g_curpaintbuffer = g_paintBuffers[ipaintbuffer].pbuf;
  785. if ( g_paintBuffers[ipaintbuffer].fsurround )
  786. {
  787. g_currearpaintbuffer = g_paintBuffers[ipaintbuffer].pbufrear;
  788. g_curcenterpaintbuffer = NULL;
  789. if ( g_paintBuffers[ipaintbuffer].fsurround_center )
  790. g_curcenterpaintbuffer = g_paintBuffers[ipaintbuffer].pbufcenter;
  791. }
  792. else
  793. {
  794. g_currearpaintbuffer = NULL;
  795. g_curcenterpaintbuffer = NULL;
  796. }
  797. Assert(g_curpaintbuffer != NULL);
  798. }
  799. // return index to current paintbuffer
  800. int MIX_GetCurrentPaintbufferIndex( void )
  801. {
  802. int i;
  803. for (i = 0; i < CPAINTBUFFERS; i++)
  804. {
  805. if (g_curpaintbuffer == g_paintBuffers[i].pbuf)
  806. return i;
  807. }
  808. return 0;
  809. }
  810. // return pointer to current paintbuffer struct
  811. paintbuffer_t *MIX_GetCurrentPaintbufferPtr( void )
  812. {
  813. int ipaint = MIX_GetCurrentPaintbufferIndex();
  814. Assert(ipaint < CPAINTBUFFERS);
  815. return &g_paintBuffers[ipaint];
  816. }
  817. // return pointer to front paintbuffer pbuf, given index
  818. inline portable_samplepair_t *MIX_GetPFrontFromIPaint(int ipaintbuffer)
  819. {
  820. return g_paintBuffers[ipaintbuffer].pbuf;
  821. }
  822. inline paintbuffer_t *MIX_GetPPaintFromIPaint( int ipaint )
  823. {
  824. Assert(ipaint < CPAINTBUFFERS);
  825. return &g_paintBuffers[ipaint];
  826. }
  827. // return pointer to rear buffer, given index.
  828. // returns null if fsurround is false;
  829. inline portable_samplepair_t *MIX_GetPRearFromIPaint(int ipaintbuffer)
  830. {
  831. if ( g_paintBuffers[ipaintbuffer].fsurround )
  832. return g_paintBuffers[ipaintbuffer].pbufrear;
  833. return NULL;
  834. }
  835. // return pointer to center buffer, given index.
  836. // returns null if fsurround_center is false;
  837. inline portable_samplepair_t *MIX_GetPCenterFromIPaint(int ipaintbuffer)
  838. {
  839. if ( g_paintBuffers[ipaintbuffer].fsurround_center )
  840. return g_paintBuffers[ipaintbuffer].pbufcenter;
  841. return NULL;
  842. }
  843. // return index to paintbuffer, given buffer pointer
  844. inline int MIX_GetIPaintFromPFront( portable_samplepair_t *pbuf )
  845. {
  846. int i;
  847. for (i = 0; i < CPAINTBUFFERS; i++)
  848. {
  849. if (pbuf == g_paintBuffers[i].pbuf)
  850. return i;
  851. }
  852. return 0;
  853. }
  854. // return pointer to paintbuffer struct, given ptr to buffer data
  855. inline paintbuffer_t *MIX_GetPPaintFromPFront( portable_samplepair_t *pbuf )
  856. {
  857. int i;
  858. i = MIX_GetIPaintFromPFront( pbuf );
  859. return &g_paintBuffers[i];
  860. }
  861. // up convert mono buffer to full surround
  862. inline void MIX_ConvertBufferToSurround( int ipaintbuffer )
  863. {
  864. paintbuffer_t *ppaint = &g_paintBuffers[ipaintbuffer];
  865. // duplicate channel data as needed
  866. if ( g_AudioDevice->IsSurround() )
  867. {
  868. // set buffer flags
  869. ppaint->fsurround = g_AudioDevice->IsSurround();
  870. ppaint->fsurround_center = g_AudioDevice->IsSurroundCenter();
  871. portable_samplepair_t *pfront = MIX_GetPFrontFromIPaint( ipaintbuffer );
  872. portable_samplepair_t *prear = MIX_GetPRearFromIPaint( ipaintbuffer );
  873. portable_samplepair_t *pcenter = MIX_GetPCenterFromIPaint( ipaintbuffer );
  874. // copy front to rear
  875. Q_memcpy(prear, pfront, sizeof(portable_samplepair_t) * PAINTBUFFER_SIZE);
  876. // copy front to center
  877. if ( g_AudioDevice->IsSurroundCenter() )
  878. Q_memcpy(pcenter, pfront, sizeof(portable_samplepair_t) * PAINTBUFFER_SIZE);
  879. }
  880. }
  881. // Activate a paintbuffer. All active paintbuffers are mixed in parallel within
  882. // MIX_MixChannelsToPaintbuffer, according to flags
  883. inline void MIX_ActivatePaintbuffer(int ipaintbuffer)
  884. {
  885. Assert(ipaintbuffer < CPAINTBUFFERS);
  886. g_paintBuffers[ipaintbuffer].factive = true;
  887. }
  888. // Don't mix into this paintbuffer
  889. inline void MIX_DeactivatePaintbuffer(int ipaintbuffer)
  890. {
  891. Assert(ipaintbuffer < CPAINTBUFFERS);
  892. g_paintBuffers[ipaintbuffer].factive = false;
  893. }
  894. // Don't mix into any paintbuffers
  895. inline void MIX_DeactivateAllPaintbuffers(void)
  896. {
  897. int i;
  898. for (i = 0; i < CPAINTBUFFERS; i++)
  899. g_paintBuffers[i].factive = false;
  900. }
  901. // set upsampling filter indexes back to 0
  902. inline void MIX_ResetPaintbufferFilterCounters( void )
  903. {
  904. int i;
  905. for (i = 0; i < CPAINTBUFFERS; i++)
  906. g_paintBuffers[i].ifilter = 0;
  907. }
  908. inline void MIX_ResetPaintbufferFilterCounter( int ipaintbuffer )
  909. {
  910. Assert (ipaintbuffer < CPAINTBUFFERS);
  911. g_paintBuffers[ipaintbuffer].ifilter = 0;
  912. }
  913. // Change paintbuffer's flags
  914. inline void MIX_SetPaintbufferFlags(int ipaintbuffer, int flags)
  915. {
  916. Assert(ipaintbuffer < CPAINTBUFFERS);
  917. g_paintBuffers[ipaintbuffer].flags = flags;
  918. }
  919. // zero out all paintbuffers
  920. void ZeroBuffer( void * pBuffer, int nSize )
  921. {
  922. #if IsGameConsole() || IsDebug()
  923. // On console we are going to use prefetch and pre-zero as much as we can...
  924. // We do it on PC debug as well, for debugging purpose.
  925. if ( nSize < 2 * CACHE_LINE_SIZE )
  926. {
  927. // If less than a few cache lines, don't use the complex version. Just use the simple one.
  928. PREFETCH_128( pBuffer, 0 * CACHE_LINE_SIZE );
  929. PREFETCH_128( pBuffer, 1 * CACHE_LINE_SIZE );
  930. PREFETCH_128( pBuffer, 2 * CACHE_LINE_SIZE ); // In some cases, this prefetch could actually prefetch after the buffer we are trying to fill
  931. // TODO: Improve this
  932. Q_memset(pBuffer, 0, nSize );
  933. return;
  934. }
  935. // We have 3 zones. Prefetch the first cache line (then memset it).
  936. // Pre-zero the cache lines in the middle. Then prefetch the last cache line (and memset it).
  937. char * pBufferStartFirstCacheLine = (char *)pBuffer;
  938. char * pBufferEndFirstCacheLine = (char *)ALIGN_VALUE( (intp)pBuffer, CACHE_LINE_SIZE );
  939. int nSizeFirstCacheLine = pBufferEndFirstCacheLine - pBufferStartFirstCacheLine;
  940. if ( nSizeFirstCacheLine != 0 )
  941. {
  942. // It means that the beginning is not aligned, so we have to prefetch / then memset the cache line before
  943. PREFETCH_128( pBufferStartFirstCacheLine, 0 );
  944. }
  945. char * pBufferEndLastCacheLine = (char *)pBuffer + nSize;
  946. char * pBufferStartLastCacheLine = (char *)( (intp)pBufferEndLastCacheLine & ~( CACHE_LINE_SIZE - 1 ) );
  947. int nSizeLastCacheLine = pBufferEndLastCacheLine - pBufferStartLastCacheLine;
  948. if ( nSizeLastCacheLine != 0 )
  949. {
  950. // It means that the end is not aligned, so we have to prefetch / then memset the cache line before
  951. PREFETCH_128( pBufferStartLastCacheLine, 0 );
  952. }
  953. // And then we have to fill everything
  954. int nSizeToZero = pBufferStartLastCacheLine - pBufferEndFirstCacheLine;
  955. Assert( (nSizeToZero % CACHE_LINE_SIZE) == 0 ); // This should be multiple of cache line size
  956. int nNumberOfCacheLinesToZero = nSizeToZero / CACHE_LINE_SIZE;
  957. char * pCurrentCacheLineToZero = pBufferEndFirstCacheLine;
  958. while ( nNumberOfCacheLinesToZero > 0 )
  959. {
  960. PREZERO_128( pCurrentCacheLineToZero, 0 );
  961. pCurrentCacheLineToZero += CACHE_LINE_SIZE;
  962. --nNumberOfCacheLinesToZero;
  963. }
  964. // At that point the initial pre-fetches should be over, we can clear them normally now
  965. // The if tests should be unnecessary - Q_memset() should be a mo-op, still keep them to have more correct profile usage.
  966. if ( nSizeFirstCacheLine != 0)
  967. {
  968. Q_memset( pBufferStartFirstCacheLine, 0, nSizeFirstCacheLine );
  969. }
  970. if ( nSizeLastCacheLine != 0)
  971. {
  972. Q_memset( pBufferStartLastCacheLine, 0, nSizeLastCacheLine );
  973. }
  974. #else
  975. // Slow version here
  976. Q_memset(pBuffer, 0, nSize );
  977. #endif
  978. }
  979. void MIX_ClearAllPaintBuffers( int SampleCount, bool clearFilters )
  980. {
  981. // g_paintBuffers can be NULL with -nosound
  982. if( !g_paintBuffers )
  983. {
  984. return;
  985. }
  986. int i;
  987. int count = MIN(SampleCount, PAINTBUFFER_SIZE);
  988. // zero out all paintbuffer data (ignore sampleCount)
  989. for (i = 0; i < CPAINTBUFFERS; i++)
  990. {
  991. if (g_paintBuffers[i].pbuf != NULL)
  992. ZeroBuffer(g_paintBuffers[i].pbuf, (count+1) * sizeof(portable_samplepair_t));
  993. if (g_paintBuffers[i].pbufrear != NULL)
  994. ZeroBuffer(g_paintBuffers[i].pbufrear, (count+1) * sizeof(portable_samplepair_t));
  995. if (g_paintBuffers[i].pbufcenter != NULL)
  996. ZeroBuffer(g_paintBuffers[i].pbufcenter, (count+1) * sizeof(portable_samplepair_t));
  997. if ( clearFilters )
  998. {
  999. Q_memset( g_paintBuffers[i].fltmem, 0, sizeof(g_paintBuffers[i].fltmem) );
  1000. Q_memset( g_paintBuffers[i].fltmemrear, 0, sizeof(g_paintBuffers[i].fltmemrear) );
  1001. Q_memset( g_paintBuffers[i].fltmemcenter, 0, sizeof(g_paintBuffers[i].fltmemcenter) );
  1002. }
  1003. }
  1004. if ( clearFilters )
  1005. {
  1006. MIX_ResetPaintbufferFilterCounters();
  1007. }
  1008. }
  1009. #define SWAP(a,b,t) {(t) = (a); (a) = (b); (b) = (t);}
  1010. #define AVG(a,b) (((a) + (b)) >> 1 )
  1011. #define AVG4(a,b,c,d) (((a) + (b) + (c) + (d)) >> 2 )
  1012. // Synthesize center channel from left/right values (average).
  1013. // Currently just averages, but could actually remove
  1014. // the center signal from the l/r channels...
  1015. inline int MIX_CenterFromLeftRight( int l, int r )
  1016. {
  1017. int sum = l + r;
  1018. return sum / 2;
  1019. }
  1020. inline int MIX_CenterFromLeftRightRounded( int l, int r )
  1021. {
  1022. int sum = l + r;
  1023. #if IsGameConsole()
  1024. // To match VMX operation (and avoid asserts due to minor differences), we do the rounding.
  1025. // If sum is positive, we add 1. Not for negative sum though. (the X360 documentation only states +1 in all cases but that's incorrect).
  1026. int nSign = sum >> 31; // 0 if sum was positive, 0xffffffff if negative
  1027. sum += nSign + 1;
  1028. #else
  1029. int nSign = sum >> 31; // 0 if sum was positive, 0xffffffff if negative
  1030. sum += nSign;
  1031. #endif
  1032. return sum / 2;
  1033. }
  1034. // mixes pbuf1 + pbuf2 into pbuf3, count samples
  1035. // fgain is output gain 0-1.0
  1036. // NOTE: pbuf3 may equal pbuf1 or pbuf2!
  1037. // mixing algorithms:
  1038. // destination 2ch:
  1039. // pb1 2ch + pb2 2ch -> pb3 2ch
  1040. // pb1 (4ch->2ch) + pb2 2ch -> pb3 2ch
  1041. // pb1 2ch + pb2 (4ch->2ch) -> pb3 2ch
  1042. // pb1 (4ch->2ch) + pb2 (4ch->2ch) -> pb3 2ch
  1043. // destination 4ch:
  1044. // pb1 4ch + pb2 4ch -> pb3 4ch
  1045. // pb1 (2ch->4ch) + pb2 4ch -> pb3 4ch
  1046. // pb1 4ch + pb2 (2ch->4ch) -> pb3 4ch
  1047. // pb1 (2ch->4ch) + pb2 (2ch->4ch) -> pb3 4ch
  1048. // if all buffers are 4 or 5 ch surround, mix rear & center channels into ibuf3 as well.
  1049. // NOTE: for performance, conversion and mixing are done in a single pass instead of
  1050. // a two pass channel convert + mix scheme.
  1051. class CMixData
  1052. {
  1053. public:
  1054. CMixData()
  1055. {
  1056. memset( this, 0, sizeof(*this) );
  1057. }
  1058. int count;
  1059. portable_samplepair_t *pbuf1, *pbuf2, *pbuf3;
  1060. portable_samplepair_t *pbufrear1, *pbufrear2, *pbufrear3;
  1061. portable_samplepair_t *pbufcenter1, *pbufcenter2, *pbufcenter3;
  1062. };
  1063. // Move these intrinsics to ssemath.h (once they are in a better shape).
  1064. // Have some trouble with intx4, define own type and will handle this better at a later point during the refactoring of ssemath.
  1065. #if IsPlatformX360()
  1066. typedef __vector4 samplex4;
  1067. #elif IsPlatformPS3_PPU()
  1068. typedef vector signed int samplex4;
  1069. #else
  1070. // Assume that's intel / SSE
  1071. typedef __m128i samplex4;
  1072. #endif
  1073. FORCEINLINE
  1074. samplex4 AddSignedSIMD( const samplex4 & first, const samplex4 & second )
  1075. {
  1076. #if IsPlatformX360()
  1077. return __vaddsws( first, second );
  1078. #elif IsPlatformPS3_PPU()
  1079. return vec_vaddsws( first, second );
  1080. #else
  1081. // Assume that's intel / SSE
  1082. return _mm_add_epi32( first, second );
  1083. #endif
  1084. }
  1085. FORCEINLINE
  1086. samplex4 AverageSIMD( const samplex4 & first, const samplex4 & second )
  1087. {
  1088. #if IsPlatformX360()
  1089. return __vavgsw( first, second );
  1090. #elif IsPlatformPS3_PPU()
  1091. return vec_vavgsw( first, second );
  1092. #else
  1093. // There is no SSE2 average for 32 bits, do it with 2 operations (the code was not rounding).
  1094. samplex4 sum = _mm_add_epi32( first, second );
  1095. return _mm_srai_epi32( sum, 1 );
  1096. #endif
  1097. }
  1098. FORCEINLINE
  1099. samplex4 AverageLeftAndRightSIMD( const samplex4 & first )
  1100. {
  1101. #if IsPlatformX360()
  1102. // Swap left and right of each sample pair
  1103. samplex4 second = __vpermwi( first, (1 << 6) | (0 << 4) | (3 << 2) | (2 << 0) );
  1104. #elif IsPlatformPS3_PPU()
  1105. samplex4 second = vec_perm( first, first, _VEC_SWIZZLE_YXWZ );
  1106. #else
  1107. // SSE is not as good as VMX in term of converting similar types to one another
  1108. const __m128 & first128 = (const __m128 &)first;
  1109. __m128 result = _mm_shuffle_ps( first128, first128, MM_SHUFFLE_REV( 1, 0, 3, 2 ) );
  1110. samplex4 second = (samplex4&)result;
  1111. #endif
  1112. // Then average them (both pairs should be the same).
  1113. return AverageSIMD( first, second );
  1114. }
  1115. // In these Mix methods, the input buffers and ouput buffer may alias, so we can't really use restrict.
  1116. void Mix255_SIMD( CMixData & data )
  1117. {
  1118. #if CHECK_VALUES_AFTER_REFACTORING
  1119. CMixData backupData( data );
  1120. // Because the values are replaced in place (the first buffer is also the destination buffer, we need to backup first).
  1121. backupData.pbuf1 = DuplicateSamplePairs( data.pbuf1, data.count );
  1122. backupData.pbufrear1 = DuplicateSamplePairs( data.pbufrear1, data.count );
  1123. backupData.pbufcenter1 = DuplicateSamplePairs( data.pbufcenter1, data.count );
  1124. #endif
  1125. int nCount = data.count;
  1126. samplex4 * pDst = ( samplex4 * )data.pbuf3;
  1127. samplex4 * pSrc1 = ( samplex4 * )data.pbuf1;
  1128. samplex4 * pSrc2 = ( samplex4 * )data.pbuf2;
  1129. samplex4 * pRearDst = ( samplex4 * )data.pbufrear3;
  1130. samplex4 * pRearSrc2 = ( samplex4 * )data.pbufrear2;
  1131. samplex4 * pCenterDst = ( samplex4 * )data.pbufcenter3; // Although for center, we only care about left, we are going to do the full calculation anyway
  1132. samplex4 * pCenterSrc2 = ( samplex4 * )data.pbufcenter2; // We can still do 2 lefts at a time
  1133. intp nAddresses = (intp)pDst | (intp)pSrc1 | (intp)pSrc2;
  1134. nAddresses |= (intp)pRearDst | (intp)pRearSrc2;
  1135. nAddresses |= (intp)pCenterDst | (intp)pCenterSrc2;
  1136. if ( ( nAddresses & 0xf ) == 0 )
  1137. {
  1138. // Addresses are 16 bytes aligned, we can VMX it
  1139. // One intx4 vector has LRLR (so 2 samples). Thus we need to do 4 loads / stores per iteration.
  1140. while ( nCount >= 8 )
  1141. {
  1142. samplex4 buf1_0 = pSrc1[0];
  1143. samplex4 buf1_1 = pSrc1[1];
  1144. samplex4 buf1_2 = pSrc1[2];
  1145. samplex4 buf1_3 = pSrc1[3];
  1146. // Use temporary variables so the compiler pipelines better.
  1147. // Otherwise the compiler will do load / add / store / load / add / store (thus creating some stalls)
  1148. // as we can't use restrict due to potential aliasing.
  1149. samplex4 temp0 = AddSignedSIMD( buf1_0, pSrc2[0] );
  1150. samplex4 temp1 = AddSignedSIMD( buf1_1, pSrc2[1] );
  1151. samplex4 temp2 = AddSignedSIMD( buf1_2, pSrc2[2] );
  1152. samplex4 temp3 = AddSignedSIMD( buf1_3, pSrc2[3] );
  1153. pDst[0] = temp0;
  1154. pDst[1] = temp1;
  1155. pDst[2] = temp2;
  1156. pDst[3] = temp3;
  1157. temp0 = AddSignedSIMD( buf1_0, pRearSrc2[0] );
  1158. temp1 = AddSignedSIMD( buf1_1, pRearSrc2[1] );
  1159. temp2 = AddSignedSIMD( buf1_2, pRearSrc2[2] );
  1160. temp3 = AddSignedSIMD( buf1_3, pRearSrc2[3] );
  1161. pRearDst[0] = temp0;
  1162. pRearDst[1] = temp1;
  1163. pRearDst[2] = temp2;
  1164. pRearDst[3] = temp3;
  1165. samplex4 center1_0 = AverageLeftAndRightSIMD( buf1_0 );
  1166. samplex4 center1_1 = AverageLeftAndRightSIMD( buf1_1 );
  1167. samplex4 center1_2 = AverageLeftAndRightSIMD( buf1_2 );
  1168. samplex4 center1_3 = AverageLeftAndRightSIMD( buf1_3 );
  1169. temp0 = AddSignedSIMD( center1_0, pCenterSrc2[0] );
  1170. temp1 = AddSignedSIMD( center1_1, pCenterSrc2[1] );
  1171. temp2 = AddSignedSIMD( center1_2, pCenterSrc2[2] );
  1172. temp3 = AddSignedSIMD( center1_3, pCenterSrc2[3] );
  1173. pCenterDst[0] = temp0;
  1174. pCenterDst[1] = temp1;
  1175. pCenterDst[2] = temp2;
  1176. pCenterDst[3] = temp3;
  1177. pDst += 4;
  1178. pSrc1 += 4;
  1179. pSrc2 += 4;
  1180. pRearDst += 4;
  1181. pRearSrc2 += 4;
  1182. pCenterDst += 4;
  1183. pCenterSrc2 += 4;
  1184. nCount -= 8;
  1185. }
  1186. }
  1187. portable_samplepair_t * pDstSample = (portable_samplepair_t *)pDst;
  1188. portable_samplepair_t * pSrc1Sample = (portable_samplepair_t *)pSrc1;
  1189. portable_samplepair_t * pSrc2Sample = (portable_samplepair_t *)pSrc2;
  1190. portable_samplepair_t * pRearDstSample = (portable_samplepair_t *)pRearDst;
  1191. portable_samplepair_t * pRearSrc2Sample = (portable_samplepair_t *)pRearSrc2;
  1192. portable_samplepair_t * pCenterDstSample = (portable_samplepair_t *)pCenterDst;
  1193. portable_samplepair_t * pCenterSrc2Sample = (portable_samplepair_t *)pCenterSrc2;
  1194. while ( nCount > 0 )
  1195. {
  1196. int l = pSrc1Sample->left;
  1197. int r = pSrc1Sample->right;
  1198. pDstSample->left = l + pSrc2Sample->left;
  1199. pDstSample->right = r + pSrc2Sample->right;
  1200. pRearDstSample->left = l + pRearSrc2Sample->left;
  1201. pRearDstSample->right = r + pRearSrc2Sample->right;
  1202. int c = MIX_CenterFromLeftRightRounded( l, r );
  1203. pCenterDstSample->left = c + pCenterSrc2Sample->left;
  1204. ++pDstSample;
  1205. ++pSrc1Sample;
  1206. ++pSrc2Sample;
  1207. ++pRearDstSample;
  1208. ++pRearSrc2Sample;
  1209. ++pCenterDstSample;
  1210. ++pCenterSrc2Sample;
  1211. --nCount;
  1212. }
  1213. #if CHECK_VALUES_AFTER_REFACTORING
  1214. // Verify that we would get the same result with the old code
  1215. for ( int i = 0; i < data.count ; ++i )
  1216. {
  1217. int l = backupData.pbuf1[i].left;
  1218. int r = backupData.pbuf1[i].right;
  1219. int c = MIX_CenterFromLeftRightRounded( l, r );
  1220. Assert( data.pbuf3[i].left == l + backupData.pbuf2[i].left );
  1221. Assert( data.pbuf3[i].right == r + backupData.pbuf2[i].right );
  1222. Assert( data.pbufrear3[i].left == l + backupData.pbufrear2[i].left );
  1223. Assert( data.pbufrear3[i].right == r + backupData.pbufrear2[i].right );
  1224. Assert( data.pbufcenter3[i].left == c + backupData.pbufcenter2[i].left );
  1225. }
  1226. FreeDuplicatedSamplePairs( backupData.pbuf1, data.count );
  1227. FreeDuplicatedSamplePairs( backupData.pbufrear1, data.count );
  1228. FreeDuplicatedSamplePairs( backupData.pbufcenter1, data.count );
  1229. #endif
  1230. }
  1231. void Mix255( CMixData & data )
  1232. {
  1233. for ( int i = 0; i < data.count; ++i )
  1234. {
  1235. int l = data.pbuf1[i].left;
  1236. int r = data.pbuf1[i].right;
  1237. int c = MIX_CenterFromLeftRight( l, r );
  1238. data.pbuf3[i].left = l + data.pbuf2[i].left;
  1239. data.pbuf3[i].right = r + data.pbuf2[i].right;
  1240. data.pbufrear3[i].left = l + data.pbufrear2[i].left;
  1241. data.pbufrear3[i].right = r + data.pbufrear2[i].right;
  1242. data.pbufcenter3[i].left = c + data.pbufcenter2[i].left;
  1243. }
  1244. }
  1245. void Mix555_SIMD( CMixData & data )
  1246. {
  1247. #if CHECK_VALUES_AFTER_REFACTORING
  1248. CMixData backupData( data );
  1249. // Because the values are replaced in place (the first buffer is also the destination buffer, we need to backup first).
  1250. backupData.pbuf1 = DuplicateSamplePairs( data.pbuf1, data.count );
  1251. backupData.pbufrear1 = DuplicateSamplePairs( data.pbufrear1, data.count );
  1252. backupData.pbufcenter1 = DuplicateSamplePairs( data.pbufcenter1, data.count );
  1253. #endif
  1254. int nCount = data.count;
  1255. samplex4 * pDst = ( samplex4 * )data.pbuf3;
  1256. samplex4 * pSrc1 = ( samplex4 * )data.pbuf1;
  1257. samplex4 * pSrc2 = ( samplex4 * )data.pbuf2;
  1258. samplex4 * pRearDst = ( samplex4 * )data.pbufrear3;
  1259. samplex4 * pRearSrc1 = ( samplex4 * )data.pbufrear1;
  1260. samplex4 * pRearSrc2 = ( samplex4 * )data.pbufrear2;
  1261. samplex4 * pCenterDst = ( samplex4 * )data.pbufcenter3; // Although for center, we only care about left, we are going to do the full calculation anyway
  1262. samplex4 * pCenterSrc1 = ( samplex4 * )data.pbufcenter1; // We can still do 2 lefts at a time
  1263. samplex4 * pCenterSrc2 = ( samplex4 * )data.pbufcenter2;
  1264. intp nAddresses = (intp)pDst | (intp)pSrc1 | (intp)pSrc2;
  1265. nAddresses |= (intp)pRearDst | (intp)pRearSrc1 | (intp)pRearSrc2;
  1266. nAddresses |= (intp)pCenterDst | (intp)pCenterSrc1 | (intp)pCenterSrc2;
  1267. if ( ( nAddresses & 0xf ) == 0 )
  1268. {
  1269. // Addresses are 16 bytes aligned, we can VMX it
  1270. // One intx4 vector has LRLR (so 2 samples). Thus we need to do 4 loads / stores per iteration.
  1271. while ( nCount >= 8 )
  1272. {
  1273. // Use temporary variables so the compiler pipelines better.
  1274. // Otherwise the compiler will do load / add / store / load / add / store (thus creating some stalls)
  1275. // as we can't use restrict due to potential aliasing.
  1276. samplex4 temp0 = AddSignedSIMD( pSrc1[0], pSrc2[0] );
  1277. samplex4 temp1 = AddSignedSIMD( pSrc1[1], pSrc2[1] );
  1278. samplex4 temp2 = AddSignedSIMD( pSrc1[2], pSrc2[2] );
  1279. samplex4 temp3 = AddSignedSIMD( pSrc1[3], pSrc2[3] );
  1280. pDst[0] = temp0;
  1281. pDst[1] = temp1;
  1282. pDst[2] = temp2;
  1283. pDst[3] = temp3;
  1284. temp0 = AddSignedSIMD( pRearSrc1[0], pRearSrc2[0] );
  1285. temp1 = AddSignedSIMD( pRearSrc1[1], pRearSrc2[1] );
  1286. temp2 = AddSignedSIMD( pRearSrc1[2], pRearSrc2[2] );
  1287. temp3 = AddSignedSIMD( pRearSrc1[3], pRearSrc2[3] );
  1288. pRearDst[0] = temp0;
  1289. pRearDst[1] = temp1;
  1290. pRearDst[2] = temp2;
  1291. pRearDst[3] = temp3;
  1292. temp0 = AddSignedSIMD( pCenterSrc1[0], pCenterSrc2[0] );
  1293. temp1 = AddSignedSIMD( pCenterSrc1[1], pCenterSrc2[1] );
  1294. temp2 = AddSignedSIMD( pCenterSrc1[2], pCenterSrc2[2] );
  1295. temp3 = AddSignedSIMD( pCenterSrc1[3], pCenterSrc2[3] );
  1296. pCenterDst[0] = temp0;
  1297. pCenterDst[1] = temp1;
  1298. pCenterDst[2] = temp2;
  1299. pCenterDst[3] = temp3;
  1300. pDst += 4;
  1301. pSrc1 += 4;
  1302. pSrc2 += 4;
  1303. pRearDst += 4;
  1304. pRearSrc1 += 4;
  1305. pRearSrc2 += 4;
  1306. pCenterDst += 4;
  1307. pCenterSrc1 += 4;
  1308. pCenterSrc2 += 4;
  1309. nCount -= 8;
  1310. }
  1311. }
  1312. portable_samplepair_t * pDstSample = (portable_samplepair_t *)pDst;
  1313. portable_samplepair_t * pSrc1Sample = (portable_samplepair_t *)pSrc1;
  1314. portable_samplepair_t * pSrc2Sample = (portable_samplepair_t *)pSrc2;
  1315. portable_samplepair_t * pRearDstSample = (portable_samplepair_t *)pRearDst;
  1316. portable_samplepair_t * pRearSrc1Sample = (portable_samplepair_t *)pRearSrc1;
  1317. portable_samplepair_t * pRearSrc2Sample = (portable_samplepair_t *)pRearSrc2;
  1318. portable_samplepair_t * pCenterDstSample = (portable_samplepair_t *)pCenterDst;
  1319. portable_samplepair_t * pCenterSrc1Sample = (portable_samplepair_t *)pCenterSrc1;
  1320. portable_samplepair_t * pCenterSrc2Sample = (portable_samplepair_t *)pCenterSrc2;
  1321. while ( nCount > 0 )
  1322. {
  1323. pDstSample->left = pSrc1Sample->left + pSrc2Sample->left;
  1324. pDstSample->right = pSrc1Sample->right + pSrc2Sample->right;
  1325. pRearDstSample->left = pRearSrc1Sample->left + pRearSrc2Sample->left;
  1326. pRearDstSample->right = pRearSrc1Sample->right + pRearSrc2Sample->right;
  1327. pCenterDstSample->left = pCenterSrc1Sample->left + pCenterSrc2Sample->left;
  1328. ++pDstSample;
  1329. ++pSrc1Sample;
  1330. ++pSrc2Sample;
  1331. ++pRearDstSample;
  1332. ++pRearSrc1Sample;
  1333. ++pRearSrc2Sample;
  1334. ++pCenterDstSample;
  1335. ++pCenterSrc1Sample;
  1336. ++pCenterSrc2Sample;
  1337. --nCount;
  1338. }
  1339. #if CHECK_VALUES_AFTER_REFACTORING
  1340. // Verify that we would get the same result with the old code
  1341. for ( int i = 0; i < data.count; ++i )
  1342. {
  1343. Assert( data.pbuf3[i].left == backupData.pbuf1[i].left + backupData.pbuf2[i].left );
  1344. Assert( data.pbuf3[i].right == backupData.pbuf1[i].right + backupData.pbuf2[i].right );
  1345. Assert( data.pbufrear3[i].left == backupData.pbufrear1[i].left + backupData.pbufrear2[i].left );
  1346. Assert( data.pbufrear3[i].right == backupData.pbufrear1[i].right + backupData.pbufrear2[i].right );
  1347. Assert( data.pbufcenter3[i].left == backupData.pbufcenter1[i].left + backupData.pbufcenter2[i].left );
  1348. }
  1349. FreeDuplicatedSamplePairs( backupData.pbuf1, data.count );
  1350. FreeDuplicatedSamplePairs( backupData.pbufrear1, data.count );
  1351. FreeDuplicatedSamplePairs( backupData.pbufcenter1, data.count );
  1352. #endif
  1353. }
  1354. void Mix555( CMixData & data )
  1355. {
  1356. for ( int i = 0; i < data.count; ++i )
  1357. {
  1358. data.pbuf3[i].left = data.pbuf1[i].left + data.pbuf2[i].left;
  1359. data.pbuf3[i].right = data.pbuf1[i].right + data.pbuf2[i].right;
  1360. data.pbufrear3[i].left = data.pbufrear1[i].left + data.pbufrear2[i].left;
  1361. data.pbufrear3[i].right = data.pbufrear1[i].right + data.pbufrear2[i].right;
  1362. data.pbufcenter3[i].left = data.pbufcenter1[i].left + data.pbufcenter2[i].left;
  1363. }
  1364. }
  1365. void MIX_MixPaintbuffers(int ibuf1, int ibuf2, int ibuf3, int count, float fgain_out)
  1366. {
  1367. VPROF("Mixpaintbuffers");
  1368. int i;
  1369. portable_samplepair_t *pbuf1, *pbuf2, *pbuf3, *pbuft;
  1370. portable_samplepair_t *pbufrear1, *pbufrear2, *pbufrear3, *pbufreart;
  1371. portable_samplepair_t *pbufcenter1, *pbufcenter2, *pbufcenter3, *pbufcentert;
  1372. int cchan1, cchan2, cchan3, cchant;
  1373. int xl,xr;
  1374. int l,r,l2,r2,c, c2;
  1375. int gain_out;
  1376. gain_out = 256 * fgain_out;
  1377. Assert (count <= PAINTBUFFER_SIZE);
  1378. Assert (ibuf1 < CPAINTBUFFERS);
  1379. Assert (ibuf2 < CPAINTBUFFERS);
  1380. Assert (ibuf3 < CPAINTBUFFERS);
  1381. pbuf1 = g_paintBuffers[ibuf1].pbuf;
  1382. pbuf2 = g_paintBuffers[ibuf2].pbuf;
  1383. pbuf3 = g_paintBuffers[ibuf3].pbuf;
  1384. pbufrear1 = g_paintBuffers[ibuf1].pbufrear;
  1385. pbufrear2 = g_paintBuffers[ibuf2].pbufrear;
  1386. pbufrear3 = g_paintBuffers[ibuf3].pbufrear;
  1387. pbufcenter1 = g_paintBuffers[ibuf1].pbufcenter;
  1388. pbufcenter2 = g_paintBuffers[ibuf2].pbufcenter;
  1389. pbufcenter3 = g_paintBuffers[ibuf3].pbufcenter;
  1390. cchan1 = 2 + (g_paintBuffers[ibuf1].fsurround ? 2 : 0) + (g_paintBuffers[ibuf1].fsurround_center ? 1 : 0);
  1391. cchan2 = 2 + (g_paintBuffers[ibuf2].fsurround ? 2 : 0) + (g_paintBuffers[ibuf2].fsurround_center ? 1 : 0);
  1392. cchan3 = 2 + (g_paintBuffers[ibuf3].fsurround ? 2 : 0) + (g_paintBuffers[ibuf3].fsurround_center ? 1 : 0);
  1393. // make sure pbuf1 always has fewer or equal channels than pbuf2
  1394. // NOTE: pbuf3 may equal pbuf1 or pbuf2!
  1395. if ( cchan2 < cchan1 )
  1396. {
  1397. SWAP( cchan1, cchan2, cchant );
  1398. SWAP( pbuf1, pbuf2, pbuft );
  1399. SWAP( pbufrear1, pbufrear2, pbufreart );
  1400. SWAP( pbufcenter1, pbufcenter2, pbufcentert);
  1401. }
  1402. CMixData data;
  1403. data.count = count;
  1404. data.pbuf1 = pbuf1;
  1405. data.pbuf2 = pbuf2;
  1406. data.pbuf3 = pbuf3;
  1407. data.pbufcenter1 = pbufcenter1;
  1408. data.pbufcenter2 = pbufcenter2;
  1409. data.pbufcenter3 = pbufcenter3;
  1410. data.pbufrear1 = pbufrear1;
  1411. data.pbufrear2 = pbufrear2;
  1412. data.pbufrear3 = pbufrear3;
  1413. // UNDONE: implement fast mixing routines for each of the following sections
  1414. // destination buffer stereo - average n chans down to stereo
  1415. if ( cchan3 == 2 )
  1416. {
  1417. // destination 2ch:
  1418. // pb1 2ch + pb2 2ch -> pb3 2ch
  1419. // pb1 2ch + pb2 (4ch->2ch) -> pb3 2ch
  1420. // pb1 (4ch->2ch) + pb2 (4ch->2ch) -> pb3 2ch
  1421. if ( cchan1 == 2 && cchan2 == 2 )
  1422. {
  1423. // mix front channels
  1424. for (i = 0; i < count; i++)
  1425. {
  1426. pbuf3[i].left = pbuf1[i].left + pbuf2[i].left;
  1427. pbuf3[i].right = pbuf1[i].right + pbuf2[i].right;
  1428. }
  1429. goto gain2ch;
  1430. }
  1431. if ( cchan1 == 2 && cchan2 == 4 )
  1432. {
  1433. // avg rear chan l/r
  1434. for (i = 0; i < count; i++)
  1435. {
  1436. pbuf3[i].left = pbuf1[i].left + AVG( pbuf2[i].left, pbufrear2[i].left );
  1437. pbuf3[i].right = pbuf1[i].right + AVG( pbuf2[i].right, pbufrear2[i].right );
  1438. }
  1439. goto gain2ch;
  1440. }
  1441. if ( cchan1 == 4 && cchan2 == 4 )
  1442. {
  1443. // avg rear chan l/r
  1444. for (i = 0; i < count; i++)
  1445. {
  1446. pbuf3[i].left = AVG( pbuf1[i].left, pbufrear1[i].left) + AVG( pbuf2[i].left, pbufrear2[i].left );
  1447. pbuf3[i].right = AVG( pbuf1[i].right, pbufrear1[i].right) + AVG( pbuf2[i].right, pbufrear2[i].right );
  1448. }
  1449. goto gain2ch;
  1450. }
  1451. if ( cchan1 == 2 && cchan2 == 5 )
  1452. {
  1453. // avg rear chan l/r + center split into left/right
  1454. for (i = 0; i < count; i++)
  1455. {
  1456. l = pbuf2[i].left + ((pbufcenter2[i].left) >> 1);
  1457. r = pbuf2[i].right + ((pbufcenter2[i].left) >> 1);
  1458. pbuf3[i].left = pbuf1[i].left + AVG( l, pbufrear2[i].left );
  1459. pbuf3[i].right = pbuf1[i].right + AVG( r, pbufrear2[i].right );
  1460. }
  1461. goto gain2ch;
  1462. }
  1463. if ( cchan1 == 4 && cchan2 == 5)
  1464. {
  1465. for (i = 0; i < count; i++)
  1466. {
  1467. l = pbuf2[i].left + ((pbufcenter2[i].left) >> 1);
  1468. r = pbuf2[i].right + ((pbufcenter2[i].left) >> 1);
  1469. pbuf3[i].left = AVG( pbuf1[i].left, pbufrear1[i].left) + AVG( l, pbufrear2[i].left );
  1470. pbuf3[i].right = AVG( pbuf1[i].right, pbufrear1[i].right) + AVG( r, pbufrear2[i].right );
  1471. }
  1472. goto gain2ch;
  1473. }
  1474. if ( cchan1 == 5 && cchan2 == 5)
  1475. {
  1476. for (i = 0; i < count; i++)
  1477. {
  1478. l = pbuf1[i].left + ((pbufcenter1[i].left) >> 1);
  1479. r = pbuf1[i].right + ((pbufcenter1[i].left) >> 1);
  1480. l2 = pbuf2[i].left + ((pbufcenter2[i].left) >> 1);
  1481. r2 = pbuf2[i].right + ((pbufcenter2[i].left) >> 1);
  1482. pbuf3[i].left = AVG( l, pbufrear1[i].left) + AVG( l2, pbufrear2[i].left );
  1483. pbuf3[i].right = AVG( r, pbufrear1[i].right) + AVG( r2, pbufrear2[i].right );
  1484. } goto gain2ch;
  1485. }
  1486. }
  1487. // destination buffer quad - duplicate n chans up to quad
  1488. if ( cchan3 == 4 )
  1489. {
  1490. // pb1 4ch + pb2 4ch -> pb3 4ch
  1491. // pb1 (2ch->4ch) + pb2 4ch -> pb3 4ch
  1492. // pb1 (2ch->4ch) + pb2 (2ch->4ch) -> pb3 4ch
  1493. if ( cchan1 == 4 && cchan2 == 4)
  1494. {
  1495. // mix front -> front, rear -> rear
  1496. for (i = 0; i < count; i++)
  1497. {
  1498. pbuf3[i].left = pbuf1[i].left + pbuf2[i].left;
  1499. pbuf3[i].right = pbuf1[i].right + pbuf2[i].right;
  1500. pbufrear3[i].left = pbufrear1[i].left + pbufrear2[i].left;
  1501. pbufrear3[i].right = pbufrear1[i].right + pbufrear2[i].right;
  1502. }
  1503. goto gain4ch;
  1504. }
  1505. if ( cchan1 == 2 && cchan2 == 4)
  1506. {
  1507. for (i = 0; i < count; i++)
  1508. {
  1509. // split 2 ch left -> front left, rear left
  1510. // split 2 ch right -> front right, rear right
  1511. xl = pbuf1[i].left;
  1512. xr = pbuf1[i].right;
  1513. pbuf3[i].left = xl + pbuf2[i].left;
  1514. pbuf3[i].right = xr + pbuf2[i].right;
  1515. pbufrear3[i].left = xl + pbufrear2[i].left;
  1516. pbufrear3[i].right = xr + pbufrear2[i].right;
  1517. }
  1518. goto gain4ch;
  1519. }
  1520. if ( cchan1 == 2 && cchan2 == 2)
  1521. {
  1522. // mix l,r, split into front l, front r
  1523. for (i = 0; i < count; i++)
  1524. {
  1525. xl = pbuf1[i].left + pbuf2[i].left;
  1526. xr = pbuf1[i].right + pbuf2[i].right;
  1527. pbufrear3[i].left = pbuf3[i].left = xl;
  1528. pbufrear3[i].right = pbuf3[i].right = xr;
  1529. }
  1530. goto gain4ch;
  1531. }
  1532. if ( cchan1 == 2 && cchan2 == 5 )
  1533. {
  1534. for (i = 0; i < count; i++)
  1535. {
  1536. // split center of chan2 into left/right
  1537. l2 = pbuf2[i].left + ((pbufcenter2[i].left) >> 1);
  1538. r2 = pbuf2[i].right + ((pbufcenter2[i].left) >> 1);
  1539. xl = pbuf1[i].left;
  1540. xr = pbuf1[i].right;
  1541. pbuf3[i].left = xl + l2;
  1542. pbuf3[i].right = xr + r2;
  1543. pbufrear3[i].left = xl + pbufrear2[i].left;
  1544. pbufrear3[i].right = xr + pbufrear2[i].right;
  1545. }
  1546. goto gain4ch;
  1547. }
  1548. if ( cchan1 == 4 && cchan2 == 5)
  1549. {
  1550. for (i = 0; i < count; i++)
  1551. {
  1552. l2 = pbuf2[i].left + ((pbufcenter2[i].left) >> 1);
  1553. r2 = pbuf2[i].right + ((pbufcenter2[i].left) >> 1);
  1554. pbuf3[i].left = pbuf1[i].left + l2;
  1555. pbuf3[i].right = pbuf1[i].right + r2;
  1556. pbufrear3[i].left = pbufrear1[i].left + pbufrear2[i].left;
  1557. pbufrear3[i].right = pbufrear1[i].right + pbufrear2[i].right;
  1558. }
  1559. goto gain4ch;
  1560. }
  1561. if ( cchan1 == 5 && cchan2 == 5 )
  1562. {
  1563. for (i = 0; i < count; i++)
  1564. {
  1565. l = pbuf1[i].left + ((pbufcenter1[i].left) >> 1);
  1566. r = pbuf1[i].right + ((pbufcenter1[i].left) >> 1);
  1567. l2 = pbuf2[i].left + ((pbufcenter2[i].left) >> 1);
  1568. r2 = pbuf2[i].right + ((pbufcenter2[i].left) >> 1);
  1569. pbuf3[i].left = l + l2;
  1570. pbuf3[i].right = r + r2;
  1571. pbufrear3[i].left = pbufrear1[i].left + pbufrear2[i].left;
  1572. pbufrear3[i].right = pbufrear1[i].right + pbufrear2[i].right;
  1573. }
  1574. goto gain4ch;
  1575. }
  1576. }
  1577. // 5 channel destination
  1578. if (cchan3 == 5)
  1579. {
  1580. // up convert from 2 or 4 ch buffer to 5 ch buffer:
  1581. // center channel is synthesized from front left, front right
  1582. if (cchan1 == 2 && cchan2 == 2)
  1583. {
  1584. for (i = 0; i < count; i++)
  1585. {
  1586. // split 2 ch left -> front left, center, rear left
  1587. // split 2 ch right -> front right, center, rear right
  1588. l = pbuf1[i].left;
  1589. r = pbuf1[i].right;
  1590. c = MIX_CenterFromLeftRight( l, r );
  1591. l2 = pbuf2[i].left;
  1592. r2 = pbuf2[i].right;
  1593. c2 = MIX_CenterFromLeftRight( l2, r2 );
  1594. pbuf3[i].left = l + l2;
  1595. pbuf3[i].right = r + r2;
  1596. pbufrear3[i].left = pbuf1[i].left + pbuf2[i].left;
  1597. pbufrear3[i].right = pbuf1[i].right + pbuf2[i].right;
  1598. pbufcenter3[i].left = c + c2;
  1599. }
  1600. goto gain5ch;
  1601. }
  1602. if (cchan1 == 2 && cchan2 == 4)
  1603. {
  1604. for (i = 0; i < count; i++)
  1605. {
  1606. l = pbuf1[i].left;
  1607. r = pbuf1[i].right;
  1608. c = MIX_CenterFromLeftRight( l, r );
  1609. l2 = pbuf2[i].left;
  1610. r2 = pbuf2[i].right;
  1611. c2 = MIX_CenterFromLeftRight( l2, r2 );
  1612. pbuf3[i].left = l + l2;
  1613. pbuf3[i].right = r + r2;
  1614. pbufrear3[i].left = pbuf1[i].left + pbufrear2[i].left;
  1615. pbufrear3[i].right = pbuf1[i].right + pbufrear2[i].right;
  1616. pbufcenter3[i].left = c + c2;
  1617. }
  1618. goto gain5ch;
  1619. }
  1620. if (cchan1 == 2 && cchan2 == 5)
  1621. {
  1622. if ( snd_mix_optimization.GetBool() )
  1623. {
  1624. Mix255_SIMD( data );
  1625. }
  1626. else
  1627. {
  1628. Mix255( data );
  1629. }
  1630. goto gain5ch;
  1631. }
  1632. if (cchan1 == 4 && cchan2 == 4)
  1633. {
  1634. for (i = 0; i < count; i++)
  1635. {
  1636. l = pbuf1[i].left;
  1637. r = pbuf1[i].right;
  1638. c = MIX_CenterFromLeftRight( l, r );
  1639. l2 = pbuf2[i].left;
  1640. r2 = pbuf2[i].right;
  1641. c2 = MIX_CenterFromLeftRight( l2, r2 );
  1642. pbuf3[i].left = l + l2;
  1643. pbuf3[i].right = r + r2;
  1644. pbufrear3[i].left = pbufrear1[i].left + pbufrear2[i].left;
  1645. pbufrear3[i].right = pbufrear1[i].right + pbufrear2[i].right;
  1646. pbufcenter3[i].left = c + c2;
  1647. }
  1648. goto gain5ch;
  1649. }
  1650. if (cchan1 == 4 && cchan2 == 5)
  1651. {
  1652. for (i = 0; i < count; i++)
  1653. {
  1654. l = pbuf1[i].left;
  1655. r = pbuf1[i].right;
  1656. c = MIX_CenterFromLeftRight( l, r );
  1657. pbuf3[i].left = l + pbuf2[i].left;
  1658. pbuf3[i].right = r + pbuf2[i].right;
  1659. pbufrear3[i].left = pbufrear1[i].left + pbufrear2[i].left;
  1660. pbufrear3[i].right = pbufrear1[i].right + pbufrear2[i].right;
  1661. pbufcenter3[i].left = c + pbufcenter2[i].left;
  1662. }
  1663. goto gain5ch;
  1664. }
  1665. if ( cchan2 == 5 && cchan1 == 5 )
  1666. {
  1667. if ( snd_mix_optimization.GetBool() )
  1668. {
  1669. Mix555_SIMD( data );
  1670. }
  1671. else
  1672. {
  1673. Mix555( data );
  1674. }
  1675. goto gain5ch;
  1676. }
  1677. }
  1678. gain2ch:
  1679. if ( gain_out == 256) // KDB: perf
  1680. return;
  1681. for (i = 0; i < count; i++)
  1682. {
  1683. pbuf3[i].left = (pbuf3[i].left * gain_out) >> 8;
  1684. pbuf3[i].right = (pbuf3[i].right * gain_out) >> 8;
  1685. }
  1686. return;
  1687. gain4ch:
  1688. if ( gain_out == 256) // KDB: perf
  1689. return;
  1690. for (i = 0; i < count; i++)
  1691. {
  1692. pbuf3[i].left = (pbuf3[i].left * gain_out) >> 8;
  1693. pbuf3[i].right = (pbuf3[i].right * gain_out) >> 8;
  1694. pbufrear3[i].left = (pbufrear3[i].left * gain_out) >> 8;
  1695. pbufrear3[i].right = (pbufrear3[i].right * gain_out) >> 8;
  1696. }
  1697. return;
  1698. gain5ch:
  1699. if ( gain_out == 256) // KDB: perf
  1700. return;
  1701. for (i = 0; i < count; i++)
  1702. {
  1703. pbuf3[i].left = (pbuf3[i].left * gain_out) >> 8;
  1704. pbuf3[i].right = (pbuf3[i].right * gain_out) >> 8;
  1705. pbufrear3[i].left = (pbufrear3[i].left * gain_out) >> 8;
  1706. pbufrear3[i].right = (pbufrear3[i].right * gain_out) >> 8;
  1707. pbufcenter3[i].left = (pbufcenter3[i].left * gain_out) >> 8;
  1708. }
  1709. return;
  1710. }
  1711. // multiply all values in paintbuffer by fgain
  1712. void MIX_ScalePaintBuffer( int bufferIndex, int count, float fgain )
  1713. {
  1714. portable_samplepair_t *pbuf = g_paintBuffers[bufferIndex].pbuf;
  1715. portable_samplepair_t *pbufrear = g_paintBuffers[bufferIndex].pbufrear;
  1716. portable_samplepair_t *pbufcenter = g_paintBuffers[bufferIndex].pbufcenter;
  1717. int gain = 256 * fgain;
  1718. int i;
  1719. if (gain == 256)
  1720. return;
  1721. if ( !g_paintBuffers[bufferIndex].fsurround )
  1722. {
  1723. for (i = 0; i < count; i++)
  1724. {
  1725. pbuf[i].left = (pbuf[i].left * gain) >> 8;
  1726. pbuf[i].right = (pbuf[i].right * gain) >> 8;
  1727. }
  1728. }
  1729. else
  1730. {
  1731. for (i = 0; i < count; i++)
  1732. {
  1733. pbuf[i].left = (pbuf[i].left * gain) >> 8;
  1734. pbuf[i].right = (pbuf[i].right * gain) >> 8;
  1735. pbufrear[i].left = (pbufrear[i].left * gain) >> 8;
  1736. pbufrear[i].right = (pbufrear[i].right * gain) >> 8;
  1737. }
  1738. if (g_paintBuffers[bufferIndex].fsurround_center)
  1739. {
  1740. for (i = 0; i < count; i++)
  1741. {
  1742. pbufcenter[i].left = (pbufcenter[i].left * gain) >> 8;
  1743. // pbufcenter[i].right = (pbufcenter[i].right * gain) >> 8; mono center channel
  1744. }
  1745. }
  1746. }
  1747. }
  1748. // DEBUG peak detection values
  1749. #define _SDEBUG 1
  1750. #ifdef _SDEBUG
  1751. float sdebug_avg_in = 0.0;
  1752. float sdebug_in_count = 0.0;
  1753. float sdebug_avg_out = 0.0;
  1754. float sdebug_out_count = 0.0;
  1755. #define SDEBUG_TOTAL_COUNT (3*44100)
  1756. #endif // DEBUG
  1757. // DEBUG code - get and show peak value of specified paintbuffer
  1758. // DEBUG code - ibuf is buffer index, count is # samples to test, pppeakprev stores peak
  1759. void SDEBUG_GetAvgValue( int ibuf, int count, float *pav )
  1760. {
  1761. #ifdef _SDEBUG
  1762. if (snd_showstart.GetInt() != 4 )
  1763. return;
  1764. float av = 0.0;
  1765. for (int i = 0; i < count; i++)
  1766. av += (float)(abs(g_paintBuffers[ibuf].pbuf->left) + abs(g_paintBuffers[ibuf].pbuf->right))/2.0;
  1767. *pav = av / count;
  1768. #endif // DEBUG
  1769. }
  1770. void SDEBUG_GetAvgIn( int ibuf, int count)
  1771. {
  1772. float av = 0.0;
  1773. SDEBUG_GetAvgValue( ibuf, count, &av );
  1774. sdebug_avg_in = ((av * count ) + (sdebug_avg_in * sdebug_in_count)) / (count + sdebug_in_count);
  1775. sdebug_in_count += count;
  1776. }
  1777. void SDEBUG_GetAvgOut( int ibuf, int count)
  1778. {
  1779. float av = 0.0;
  1780. SDEBUG_GetAvgValue( ibuf, count, &av );
  1781. sdebug_avg_out = ((av * count ) + (sdebug_avg_out * sdebug_out_count)) / (count + sdebug_out_count);
  1782. sdebug_out_count += count;
  1783. }
  1784. void SDEBUG_ShowAvgValue()
  1785. {
  1786. #ifdef _SDEBUG
  1787. if (sdebug_in_count > SDEBUG_TOTAL_COUNT)
  1788. {
  1789. if ((int)sdebug_avg_in > 20.0 && (int)sdebug_avg_out > 20.0)
  1790. DevMsg("dsp avg gain:%1.2f in:%1.2f out:%1.2f 1/gain:%1.2f\n", sdebug_avg_out/sdebug_avg_in, sdebug_avg_in, sdebug_avg_out, sdebug_avg_in/sdebug_avg_out);
  1791. sdebug_avg_in = 0.0;
  1792. sdebug_avg_out = 0.0;
  1793. sdebug_in_count = 0.0;
  1794. sdebug_out_count = 0.0;
  1795. }
  1796. #endif // DEBUG
  1797. }
  1798. void ClipStereo( portable_samplepair_t * pBuffer, int nCount )
  1799. {
  1800. while ( nCount >= 4 )
  1801. {
  1802. pBuffer[0].left = iclip( pBuffer[0].left );
  1803. pBuffer[0].right = iclip( pBuffer[0].right );
  1804. pBuffer[1].left = iclip( pBuffer[1].left );
  1805. pBuffer[1].right = iclip( pBuffer[1].right );
  1806. pBuffer[2].left = iclip( pBuffer[2].left );
  1807. pBuffer[2].right = iclip( pBuffer[2].right );
  1808. pBuffer[3].left = iclip( pBuffer[3].left );
  1809. pBuffer[3].right = iclip( pBuffer[3].right );
  1810. nCount -= 4;
  1811. pBuffer += 4;
  1812. }
  1813. while ( nCount > 0 )
  1814. {
  1815. pBuffer->left = iclip( pBuffer->left );
  1816. pBuffer->right = iclip(pBuffer->right );
  1817. --nCount;
  1818. ++pBuffer;
  1819. }
  1820. }
  1821. void ClipLeft( portable_samplepair_t * pBuffer, int nCount )
  1822. {
  1823. while ( nCount >= 8 )
  1824. {
  1825. pBuffer[0].left = iclip( pBuffer[0].left );
  1826. pBuffer[1].left = iclip( pBuffer[1].left );
  1827. pBuffer[2].left = iclip( pBuffer[2].left );
  1828. pBuffer[3].left = iclip( pBuffer[3].left );
  1829. pBuffer[4].left = iclip( pBuffer[4].left );
  1830. pBuffer[5].left = iclip( pBuffer[5].left );
  1831. pBuffer[6].left = iclip( pBuffer[6].left );
  1832. pBuffer[7].left = iclip( pBuffer[7].left );
  1833. nCount -= 8;
  1834. pBuffer += 8;
  1835. }
  1836. while ( nCount > 0 )
  1837. {
  1838. pBuffer->left = iclip( pBuffer->left );
  1839. --nCount;
  1840. ++pBuffer;
  1841. }
  1842. }
  1843. // clip all values in paintbuffer to 16bit.
  1844. // if fsurround is set for paintbuffer, also process rear buffer samples
  1845. void MIX_CompressPaintbuffer(int ipaint, int count)
  1846. {
  1847. VPROF("CompressPaintbuffer");
  1848. paintbuffer_t *ppaint = MIX_GetPPaintFromIPaint(ipaint);
  1849. portable_samplepair_t *pbf;
  1850. portable_samplepair_t *pbr;
  1851. portable_samplepair_t *pbc;
  1852. pbf = ppaint->pbuf;
  1853. pbr = ppaint->pbufrear;
  1854. pbc = ppaint->pbufcenter;
  1855. ClipStereo( pbf, count );
  1856. if ( ppaint->fsurround )
  1857. {
  1858. Assert (pbr);
  1859. ClipStereo( pbr, count );
  1860. }
  1861. if ( ppaint->fsurround_center )
  1862. {
  1863. Assert (pbc);
  1864. // mono - left channel
  1865. ClipLeft( pbc, count );
  1866. }
  1867. }
  1868. // mix and upsample channels to 44khz 'ipaintbuffer'
  1869. // mix channels matching 'flags' (SOUND_MIX_DRY, SOUND_MIX_WET, SOUND_MIX_SPEAKER) into specified paintbuffer
  1870. // upsamples 11khz, 22khz channels to 44khz.
  1871. // NOTE: only call this on channels that will be mixed into only 1 paintbuffer
  1872. // and that will not be mixed until the next mix pass! otherwise, MIX_MixChannelsToPaintbuffer
  1873. // will advance any internal pointers on mixed channels; subsequent calls will be at
  1874. // incorrect offset.
  1875. void MIX_MixUpsampleBuffer( CChannelList &list, int ipaintbuffer, int64 end, int count, int flags )
  1876. {
  1877. VPROF("MixUpsampleBuffer");
  1878. int ipaintcur = MIX_GetCurrentPaintbufferIndex(); // save current paintbuffer
  1879. // reset paintbuffer upsampling filter index
  1880. MIX_ResetPaintbufferFilterCounter( ipaintbuffer );
  1881. // prevent other paintbuffers from being mixed
  1882. MIX_DeactivateAllPaintbuffers();
  1883. MIX_ActivatePaintbuffer( ipaintbuffer ); // operates on MIX_MixChannelsToPaintbuffer
  1884. MIX_SetCurrentPaintbuffer( ipaintbuffer ); // operates on MixUpSample
  1885. // mix 11khz channels to buffer
  1886. if ( list.m_has11kChannels )
  1887. {
  1888. MIX_MixChannelsToPaintbuffer( list, end, flags, SOUND_11k, SOUND_11k );
  1889. // upsample 11khz buffer by 2x
  1890. Device_MixUpsample( count / (SOUND_DMA_SPEED / SOUND_11k), FILTERTYPE_LINEAR );
  1891. }
  1892. if ( list.m_has22kChannels || list.m_has11kChannels )
  1893. {
  1894. // mix 22khz channels to buffer
  1895. MIX_MixChannelsToPaintbuffer( list, end, flags, SOUND_22k, SOUND_22k );
  1896. #if (SOUND_DMA_SPEED > SOUND_22k)
  1897. // upsample 22khz buffer by 2x
  1898. Device_MixUpsample( count / (SOUND_DMA_SPEED / SOUND_22k), FILTERTYPE_LINEAR );
  1899. #endif
  1900. }
  1901. // mix 44khz channels to buffer
  1902. MIX_MixChannelsToPaintbuffer( list, end, flags, SOUND_44k, SOUND_DMA_SPEED);
  1903. MIX_DeactivateAllPaintbuffers();
  1904. // restore previous paintbuffer
  1905. MIX_SetCurrentPaintbuffer( ipaintcur );
  1906. }
  1907. // upsample and mix sounds into final 44khz versions of the following paintbuffers:
  1908. // IROOMBUFFER, IFACINGBUFFER, IFACINGAWAY, IDRYBUFFER, ISPEAKERBUFFER
  1909. // dsp fx are then applied to these buffers by the caller.
  1910. // caller also remixes all into final IPAINTBUFFER output.
  1911. void MIX_UpsampleAllPaintbuffers( CChannelList &list, int64 end, int count )
  1912. {
  1913. VPROF( "MixUpsampleAll" );
  1914. // 'dry' and 'speaker' channel sounds mix 100% into their corresponding buffers
  1915. // mix and upsample all 'dry' sounds (channels) to 44khz IDRYBUFFER paintbuffer
  1916. if ( list.m_hasDryChannels )
  1917. MIX_MixUpsampleBuffer( list, IDRYBUFFER, end, count, SOUND_MIX_DRY );
  1918. // mix and upsample all 'speaker' sounds (channels) to 44khz ISPEAKERBUFFER paintbuffer
  1919. if ( list.m_hasSpeakerChannels )
  1920. MIX_MixUpsampleBuffer( list, ISPEAKERBUFFER, end, count, SOUND_MIX_SPEAKER );
  1921. // 'room', 'facing' 'facingaway' sounds are mixed into up to 3 buffers:
  1922. // 11khz sounds are mixed into 3 buffers based on distance from listener, and facing direction
  1923. // These buffers are room, facing, facingaway
  1924. // These 3 mixed buffers are then each upsampled to 22khz.
  1925. // 22khz sounds are mixed into the 3 buffers based on distance from listener, and facing direction
  1926. // These 3 mixed buffers are then each upsampled to 44khz.
  1927. // 44khz sounds are mixed into the 3 buffers based on distance from listener, and facing direction
  1928. MIX_DeactivateAllPaintbuffers();
  1929. // set paintbuffer upsample filter indices to 0
  1930. MIX_ResetPaintbufferFilterCounters();
  1931. if ( !g_bDspOff )
  1932. {
  1933. // only mix to roombuffer if dsp fx are on KDB: perf
  1934. MIX_ActivatePaintbuffer(IROOMBUFFER); // operates on MIX_MixChannelsToPaintbuffer
  1935. }
  1936. MIX_ActivatePaintbuffer(IFACINGBUFFER);
  1937. if ( g_bdirectionalfx )
  1938. {
  1939. // mix to facing away buffer only if directional presets are set
  1940. MIX_ActivatePaintbuffer(IFACINGAWAYBUFFER);
  1941. }
  1942. // mix 11khz sounds:
  1943. // pan sounds between 3 busses: facing, facingaway and room buffers
  1944. MIX_MixChannelsToPaintbuffer( list, end, SOUND_MIX_WET, SOUND_11k, SOUND_11k);
  1945. // upsample all 11khz buffers by 2x
  1946. if ( !g_bDspOff )
  1947. {
  1948. // only upsample roombuffer if dsp fx are on KDB: perf
  1949. MIX_SetCurrentPaintbuffer(IROOMBUFFER); // operates on MixUpSample
  1950. Device_MixUpsample( count / (SOUND_DMA_SPEED / SOUND_11k), FILTERTYPE_LINEAR );
  1951. }
  1952. MIX_SetCurrentPaintbuffer(IFACINGBUFFER);
  1953. Device_MixUpsample( count / (SOUND_DMA_SPEED / SOUND_11k), FILTERTYPE_LINEAR );
  1954. if ( g_bdirectionalfx )
  1955. {
  1956. MIX_SetCurrentPaintbuffer(IFACINGAWAYBUFFER);
  1957. Device_MixUpsample( count / (SOUND_DMA_SPEED / SOUND_11k), FILTERTYPE_LINEAR );
  1958. }
  1959. // mix 22khz sounds:
  1960. // pan sounds between 3 busses: facing, facingaway and room buffers
  1961. MIX_MixChannelsToPaintbuffer( list, end, SOUND_MIX_WET, SOUND_22k, SOUND_22k);
  1962. // upsample all 22khz buffers by 2x
  1963. #if ( SOUND_DMA_SPEED > SOUND_22k )
  1964. if ( !g_bDspOff )
  1965. {
  1966. // only upsample roombuffer if dsp fx are on KDB: perf
  1967. MIX_SetCurrentPaintbuffer(IROOMBUFFER);
  1968. Device_MixUpsample( count / (SOUND_DMA_SPEED / SOUND_22k), FILTERTYPE_LINEAR );
  1969. }
  1970. MIX_SetCurrentPaintbuffer(IFACINGBUFFER);
  1971. Device_MixUpsample( count / (SOUND_DMA_SPEED / SOUND_22k), FILTERTYPE_LINEAR );
  1972. if ( g_bdirectionalfx )
  1973. {
  1974. MIX_SetCurrentPaintbuffer(IFACINGAWAYBUFFER);
  1975. Device_MixUpsample( count / (SOUND_DMA_SPEED / SOUND_22k), FILTERTYPE_LINEAR );
  1976. }
  1977. #endif
  1978. // mix all 44khz sounds to all active paintbuffers
  1979. MIX_MixChannelsToPaintbuffer( list, end, SOUND_MIX_WET, SOUND_44k, SOUND_DMA_SPEED);
  1980. MIX_DeactivateAllPaintbuffers();
  1981. MIX_SetCurrentPaintbuffer(IPAINTBUFFER);
  1982. }
  1983. ConVar snd_cull_duplicates("snd_cull_duplicates","0",FCVAR_NONE,"If nonzero, aggressively cull duplicate sounds during mixing. The number specifies the number of duplicates allowed to be played.");
  1984. // Helper class for determining whether a given channel number should be culled from
  1985. // mixing, if snd_cull_duplicates is enabled (psychoacoustic quashing).
  1986. class CChannelCullList
  1987. {
  1988. public:
  1989. // default constructor
  1990. CChannelCullList() : m_numChans(0) {};
  1991. // call if you plan on culling channels - and not otherwise, it's a little expensive
  1992. // (that's why it's not in the constructor)
  1993. void Initialize( CChannelList &list );
  1994. // returns true if a given channel number has been marked for culling
  1995. inline bool ShouldCull( int channelNum )
  1996. {
  1997. return (m_numChans > channelNum) ? m_bShouldCull[channelNum] : false;
  1998. }
  1999. // an array of sound names and their volumes
  2000. // TODO: there may be a way to do this faster on 360 (eg, pad to 128bit, use SIMD)
  2001. struct sChannelVolData
  2002. {
  2003. int m_channelNum;
  2004. int m_vol; // max volume of sound. -1 means "do not cull, ever, do not even do the math"
  2005. uintp m_nameHash; // a unique id for a sound file
  2006. };
  2007. protected:
  2008. sChannelVolData m_channelInfo[MAX_CHANNELS];
  2009. bool m_bShouldCull[MAX_CHANNELS]; // in ChannelList order, not sorted order
  2010. int m_numChans;
  2011. };
  2012. // comparator for qsort as used below (eg a lambda)
  2013. // returns < 0 if a should come before b, > 0 if a should come after, 0 otherwise
  2014. static int __cdecl ChannelVolComparator ( const void * a, const void * b )
  2015. {
  2016. // greater numbers come first.
  2017. return static_cast<const CChannelCullList::sChannelVolData *>(b)->m_vol - static_cast<const CChannelCullList::sChannelVolData *>(a)->m_vol;
  2018. }
  2019. void CChannelCullList::Initialize( CChannelList &list )
  2020. {
  2021. VPROF("CChannelCullList::Initialize");
  2022. // First, build a sorted list of channels by decreasing volume, and by a hash of their wavname.
  2023. m_numChans = list.Count();
  2024. for ( int i = m_numChans - 1 ; i >= 0 ; --i )
  2025. {
  2026. channel_t *ch = list.GetChannel(i);
  2027. m_channelInfo[i].m_channelNum = i;
  2028. if ( ch && ch->pMixer->IsReadyToMix() )
  2029. {
  2030. m_channelInfo[i].m_vol = ChannelLoudestCurVolume(ch);
  2031. AssertMsg(m_channelInfo[i].m_vol >= 0, "Sound channel has a negative volume?");
  2032. m_channelInfo[i].m_nameHash = (uintp) ch->sfx;
  2033. }
  2034. else
  2035. {
  2036. m_channelInfo[i].m_vol = -1;
  2037. m_channelInfo[i].m_nameHash = (uintp) 0; // doesn't matter
  2038. }
  2039. }
  2040. // set the unused channels to invalid data
  2041. for ( int i = m_numChans ; i < MAX_CHANNELS ; ++i )
  2042. {
  2043. m_channelInfo[i].m_channelNum = -1;
  2044. m_channelInfo[i].m_vol = -1;
  2045. }
  2046. // Sort the list.
  2047. qsort( m_channelInfo, MAX_CHANNELS, sizeof(sChannelVolData), ChannelVolComparator );
  2048. // Then, determine if the given sound is less than the nth loudest of its hash. If so, mark its flag
  2049. // for removal.
  2050. // TODO: use an actual algorithm rather than this bogus quadratic technique.
  2051. // (I'm using it for now because we don't have convenient/fast hash table
  2052. // classes, which would be the linear-time way to deal with this).
  2053. const int cutoff = snd_cull_duplicates.GetInt();
  2054. for ( int i = 0 ; i < m_numChans ; ++i ) // i is index in original channel list
  2055. {
  2056. channel_t *ch = list.GetChannel(i);
  2057. // for each sound, determine where it ranks in loudness
  2058. int howManyLouder = 0;
  2059. for ( int j = 0 ;
  2060. m_channelInfo[j].m_channelNum != i && m_channelInfo[j].m_vol >= 0 && j < MAX_CHANNELS ;
  2061. ++j )
  2062. {
  2063. // j steps through the sorted list until we find ourselves:
  2064. if (m_channelInfo[j].m_nameHash == (uintp)(ch->sfx))
  2065. {
  2066. // that's another channel playing this sound but louder than me
  2067. ++howManyLouder;
  2068. }
  2069. }
  2070. if (howManyLouder >= cutoff)
  2071. {
  2072. // this sound should be culled
  2073. m_bShouldCull[i] = true;
  2074. }
  2075. else
  2076. {
  2077. // this sound should not be culled
  2078. m_bShouldCull[i] = false;
  2079. }
  2080. }
  2081. }
  2082. // build a list of channels that will actually do mixing in this update
  2083. // remove all active channels that won't mix for some reason
  2084. void MIX_BuildChannelList( CChannelList &list )
  2085. {
  2086. VPROF("MIX_BuildChannelList");
  2087. g_ActiveChannels.GetActiveChannels( list );
  2088. list.m_hasDryChannels = false;
  2089. list.m_hasSpeakerChannels = false;
  2090. list.m_has11kChannels = false;
  2091. list.m_has22kChannels = false;
  2092. list.m_has44kChannels = false;
  2093. bool delayStartServer = false;
  2094. bool delayStartClient = false;
  2095. bool bPaused = g_pSoundServices->IsGamePaused();
  2096. CChannelCullList cullList;
  2097. if (snd_cull_duplicates.GetInt() > 0)
  2098. {
  2099. cullList.Initialize(list);
  2100. }
  2101. AUTO_LOCK( g_SoundMapMutex );
  2102. // int numQuashed = 0;
  2103. for ( int i = list.Count(); --i >= 0; )
  2104. {
  2105. channel_t *ch = list.GetChannel(i);
  2106. bool bRemove = false;
  2107. // Certain async loaded sounds lazily load into memory in the background, use this to determine
  2108. // if the sound is ready for mixing
  2109. CAudioSource *pSource = NULL;
  2110. if ( ch->pMixer->IsReadyToMix() )
  2111. {
  2112. SoundError soundError;
  2113. pSource = S_LoadSound( ch->sfx, ch, soundError );
  2114. // Don't mix sound data for sounds with 'zero' volume. If it's a non-looping sound,
  2115. // just remove the sound when its volume goes to zero. If it's a 'dry' channel sound (ie: music)
  2116. // then assume bZeroVolume is fade in - don't restart
  2117. // To be 'zero' volume, all target volume and current volume values must all be less than 5
  2118. bool bZeroVolume = BChannelLowVolume( ch, 0 );
  2119. if ( !pSource || ( bZeroVolume && !pSource->IsLooped() && !ch->flags.bdry ) )
  2120. {
  2121. // NOTE: Since we've loaded the sound, check to see if it's a sentence. Play them at zero anyway
  2122. // to keep the character's lips moving and the captions happening.
  2123. if ( !pSource || pSource->GetSentence() == NULL )
  2124. {
  2125. S_FreeChannel( ch );
  2126. bRemove = true;
  2127. }
  2128. }
  2129. else if ( bZeroVolume )
  2130. {
  2131. list.m_quashed[i] = true;
  2132. }
  2133. // If the sound wants to stop when the game pauses, do so
  2134. if ( bPaused && SND_ShouldPause(ch) )
  2135. {
  2136. bRemove = true;
  2137. }
  2138. // On lowend, aggressively cull duplicate sounds.
  2139. if ( !bRemove && snd_cull_duplicates.GetInt() > 0 )
  2140. {
  2141. // We can't simply remove them, because then sounds will pile up waiting to finish later.
  2142. // We need to flag them for not mixing.
  2143. list.m_quashed[i] = cullList.ShouldCull(i);
  2144. /*
  2145. if (list.m_quashed[i])
  2146. {
  2147. numQuashed++;
  2148. // Msg("removed %i\n", i);
  2149. }
  2150. */
  2151. }
  2152. else
  2153. {
  2154. list.m_quashed[i] = false;
  2155. }
  2156. }
  2157. else
  2158. {
  2159. if ( ch->pMixer->GetSource()->GetCacheStatus() == CAudioSource::AUDIO_ERROR_LOADING )
  2160. {
  2161. S_FreeChannel( ch );
  2162. }
  2163. bRemove = true;
  2164. }
  2165. if ( bRemove )
  2166. {
  2167. list.RemoveChannelFromList(i);
  2168. continue;
  2169. }
  2170. if ( ch->flags.bSpeaker )
  2171. {
  2172. list.m_hasSpeakerChannels = true;
  2173. }
  2174. if ( ch->flags.bdry )
  2175. {
  2176. list.m_hasDryChannels = true;
  2177. }
  2178. int rate = pSource->SampleRate();
  2179. if ( rate == SOUND_11k )
  2180. {
  2181. list.m_has11kChannels = true;
  2182. }
  2183. else if ( rate == SOUND_22k )
  2184. {
  2185. list.m_has22kChannels = true;
  2186. }
  2187. else if ( rate == SOUND_44k )
  2188. {
  2189. list.m_has44kChannels = true;
  2190. }
  2191. if ( ch->flags.delayed_start && !ch->flags.m_bHasMouth )
  2192. {
  2193. if ( ch->flags.fromserver )
  2194. {
  2195. delayStartServer = true;
  2196. }
  2197. else
  2198. {
  2199. delayStartClient = true;
  2200. }
  2201. }
  2202. // get playback pitch
  2203. ch->pitch = ch->pMixer->ModifyPitch( ch->basePitch * 0.01f );
  2204. }
  2205. // DevMsg( "%d channels quashed.\n", numQuashed );
  2206. // This code will resync the delay calculation clock really often
  2207. // any time there are no scheduled waves or the game is paused
  2208. // we go ahead and reset the clock
  2209. // That way the clock is only used for short periods of time
  2210. // and we need no solution for drift
  2211. if ( bPaused || (host_frametime_unbounded > host_frametime) )
  2212. {
  2213. delayStartClient = false;
  2214. delayStartServer = false;
  2215. }
  2216. if (!delayStartServer)
  2217. {
  2218. S_SyncClockAdjust(CLOCK_SYNC_SERVER);
  2219. }
  2220. if (!delayStartClient)
  2221. {
  2222. S_SyncClockAdjust(CLOCK_SYNC_CLIENT);
  2223. }
  2224. }
  2225. // main mixing rountine - mix up to 'endtime' samples.
  2226. // All channels are mixed in a paintbuffer and then sent to
  2227. // hardware.
  2228. // A mix pass is performed, resulting in mixed sounds in IROOMBUFFER, IFACINGBUFFER, IFACINGAWAYBUFFER, IDRYBUFFER, ISPEAKERBUFFER:
  2229. // directional sounds are panned and mixed between IFACINGBUFFER and IFACINGAWAYBUFFER
  2230. // omnidirectional sounds are panned 100% into IFACINGBUFFER
  2231. // sound sources far from player (ie: near back of room ) are mixed in proportion to this distance
  2232. // into IROOMBUFFER
  2233. // sounds with ch->bSpeaker set are mixed in mono into ISPEAKERBUFFER
  2234. // dsp_facingaway fx (2 or 4ch filtering) are then applied to the IFACINGAWAYBUFFER
  2235. // dsp_speaker fx (1ch) are then applied to the ISPEAKERBUFFER
  2236. // dsp_room fx (1ch reverb) are then applied to the IROOMBUFFER
  2237. // All buffers are recombined into the IPAINTBUFFER
  2238. // The dsp_water and dsp_player fx are applied in series to the IPAINTBUFFER
  2239. // Finally, the IDRYBUFFER buffer is mixed into the IPAINTBUFFER
  2240. extern ConVar dsp_off;
  2241. extern ConVar snd_profile;
  2242. extern void DEBUG_StartSoundMeasure(int type, int samplecount );
  2243. extern void DEBUG_StopSoundMeasure(int type, int samplecount );
  2244. extern ConVar dsp_enhance_stereo;
  2245. extern ConVar dsp_volume;
  2246. extern ConVar dsp_vol_5ch;
  2247. extern ConVar dsp_vol_4ch;
  2248. extern ConVar dsp_vol_2ch;
  2249. extern void MXR_SetCurrentSoundMixer( const char *szsoundmixer );
  2250. extern ConVar snd_soundmixer;
  2251. ConVar snd_mix_dry_volume("snd_mix_dry_volume", "1.0", FCVAR_NONE );
  2252. ConVar snd_mix_test1( "snd_mix_test1", "1.0", FCVAR_NONE );
  2253. ConVar snd_mix_test2( "snd_mix_test2", "1.0", FCVAR_NONE );
  2254. void MIX_PaintChannels( int64 endtime, bool bIsUnderwater )
  2255. {
  2256. VPROF("MIX_PaintChannels");
  2257. #if !defined( USE_AUDIO_DEVICE_V1 ) && defined( USE_SDL )
  2258. //Our path for make snd_mute_losefocus work on Linux/Mac.
  2259. extern IVEngineClient *engineClient;
  2260. if ( engineClient && g_AudioDevice )
  2261. {
  2262. g_AudioDevice->UpdateFocus( engineClient->IsActiveApp() );
  2263. }
  2264. #endif
  2265. int64 end;
  2266. int count;
  2267. #ifdef CSTRIKE15
  2268. bool b_spatial_delays = false;
  2269. #else
  2270. bool b_spatial_delays = dsp_enhance_stereo.GetBool();
  2271. #endif
  2272. bool room_fsurround_sav;
  2273. bool room_fsurround_center_sav;
  2274. paintbuffer_t *proom = MIX_GetPPaintFromIPaint(IROOMBUFFER);
  2275. CheckNewDspPresets();
  2276. MXR_SetCurrentSoundMixer( snd_soundmixer.GetString() );
  2277. // dsp performance tuning
  2278. g_snd_profile_type = snd_profile.GetInt();
  2279. // dsp_off is true if no dsp processing is to run
  2280. // directional dsp processing is enabled if dsp_facingaway is non-zero
  2281. g_bDspOff = dsp_off.GetInt() ? 1 : 0;
  2282. CChannelList list;
  2283. MIX_BuildChannelList(list);
  2284. // get master dsp volume
  2285. g_dsp_volume = dsp_volume.GetFloat();
  2286. // attenuate master dsp volume by 2,4 or 5 ch settings
  2287. if ( g_AudioDevice->IsSurround() )
  2288. {
  2289. g_dsp_volume *= ( g_AudioDevice->IsSurroundCenter() ? dsp_vol_5ch.GetFloat() : dsp_vol_4ch.GetFloat() );
  2290. }
  2291. else
  2292. {
  2293. g_dsp_volume *= dsp_vol_2ch.GetFloat();
  2294. }
  2295. if ( !g_bDspOff )
  2296. {
  2297. g_bdirectionalfx = dsp_facingaway.GetInt() ? 1 : 0;
  2298. }
  2299. else
  2300. {
  2301. g_bdirectionalfx = 0;
  2302. }
  2303. // get dsp preset gain values, update gain crossfaders, used when mixing dsp processed buffers into paintbuffer
  2304. SDEBUG_ShowAvgValue();
  2305. while ( g_paintedtime < endtime )
  2306. {
  2307. VPROF("MIX_PaintChannels inner loop");
  2308. // mix a full 'paintbuffer' of sound
  2309. // clamp at paintbuffer size
  2310. end = endtime;
  2311. if (endtime - g_paintedtime > PAINTBUFFER_SIZE)
  2312. {
  2313. end = g_paintedtime + PAINTBUFFER_SIZE;
  2314. }
  2315. // number of 44khz samples to mix into paintbuffer, up to paintbuffer size
  2316. count = end - g_paintedtime;
  2317. // clear all mix buffers
  2318. MIX_ClearAllPaintBuffers( count, false );
  2319. // upsample all mix buffers.
  2320. // results in 44khz versions of:
  2321. // IROOMBUFFER, IFACINGBUFFER, IFACINGAWAYBUFFER, IDRYBUFFER, ISPEAKERBUFFER
  2322. MIX_UpsampleAllPaintbuffers( list, end, count );
  2323. // apply appropriate dsp fx to each buffer, remix buffers into single quad output buffer
  2324. // apply 2 or 4ch filtering to IFACINGAWAY buffer
  2325. if ( g_bdirectionalfx )
  2326. {
  2327. Device_ApplyDSPEffects( idsp_facingaway, MIX_GetPFrontFromIPaint(IFACINGAWAYBUFFER), MIX_GetPRearFromIPaint(IFACINGAWAYBUFFER), MIX_GetPCenterFromIPaint(IFACINGAWAYBUFFER), count );
  2328. }
  2329. if ( !g_bDspOff && list.m_hasSpeakerChannels )
  2330. {
  2331. // apply 1ch filtering to ISPEAKERBUFFER
  2332. Device_ApplyDSPEffects( idsp_speaker, MIX_GetPFrontFromIPaint(ISPEAKERBUFFER), MIX_GetPRearFromIPaint(ISPEAKERBUFFER), MIX_GetPCenterFromIPaint(ISPEAKERBUFFER), count );
  2333. // mix ISPEAKERBUFFER with IROOMBUFFER and IFACINGBUFFER
  2334. MIX_ScalePaintBuffer( ISPEAKERBUFFER, count, 0.7 );
  2335. MIX_MixPaintbuffers( ISPEAKERBUFFER, IFACINGBUFFER, IFACINGBUFFER, count, 1.0 ); // +70% dry speaker
  2336. MIX_ScalePaintBuffer( ISPEAKERBUFFER, count, 0.43 );
  2337. MIX_MixPaintbuffers( ISPEAKERBUFFER, IROOMBUFFER, IROOMBUFFER, count, 1.0 ); // +30% wet speaker
  2338. }
  2339. // apply dsp_room effects to room buffer
  2340. Device_ApplyDSPEffects( Get_idsp_room(), MIX_GetPFrontFromIPaint(IROOMBUFFER), MIX_GetPRearFromIPaint(IROOMBUFFER), MIX_GetPCenterFromIPaint(IROOMBUFFER), count );
  2341. // save room buffer surround status, in case we upconvert it
  2342. room_fsurround_sav = proom->fsurround;
  2343. room_fsurround_center_sav = proom->fsurround_center;
  2344. // apply left/center/right/lrear/rrear spatial delays to room buffer
  2345. if ( b_spatial_delays && !g_bDspOff && !DSP_RoomDSPIsOff() )
  2346. {
  2347. // upgrade mono room buffer to surround status so we can apply spatial delays to all channels
  2348. MIX_ConvertBufferToSurround( IROOMBUFFER );
  2349. Device_ApplyDSPEffects( idsp_spatial, MIX_GetPFrontFromIPaint(IROOMBUFFER), MIX_GetPRearFromIPaint(IROOMBUFFER), MIX_GetPCenterFromIPaint(IROOMBUFFER), count );
  2350. }
  2351. if ( g_bdirectionalfx ) // KDB: perf
  2352. {
  2353. // Recombine IFACING and IFACINGAWAY buffers into IPAINTBUFFER
  2354. MIX_MixPaintbuffers( IFACINGBUFFER, IFACINGAWAYBUFFER, IPAINTBUFFER, count, DSP_NOROOM_MIX );
  2355. // Add in dsp room fx to paintbuffer, mix at 75%
  2356. MIX_MixPaintbuffers( IROOMBUFFER, IPAINTBUFFER, IPAINTBUFFER, count, DSP_ROOM_MIX );
  2357. }
  2358. else
  2359. {
  2360. // Mix IFACING buffer with IROOMBUFFER
  2361. // (IFACINGAWAYBUFFER contains no data, IFACINGBBUFFER has full dry mix based on distance from listener)
  2362. // if dsp disabled, mix 100% facingbuffer, otherwise, mix 75% facingbuffer + roombuffer
  2363. /*MIX_ScalePaintBuffer( IROOMBUFFER, count, snd_mix_test1.GetFloat() );*/
  2364. float flDryVolume = snd_mix_dry_volume.GetFloat();
  2365. if( flDryVolume < 1.0 )
  2366. {
  2367. MIX_ScalePaintBuffer( IFACINGBUFFER, count, flDryVolume );
  2368. }
  2369. float mix = g_bDspOff ? 1.0 : DSP_ROOM_MIX;
  2370. MIX_MixPaintbuffers( IROOMBUFFER, IFACINGBUFFER, IPAINTBUFFER, count, mix );
  2371. }
  2372. // restore room buffer surround status, in case we upconverted it
  2373. proom->fsurround = room_fsurround_sav;
  2374. proom->fsurround_center = room_fsurround_center_sav;
  2375. // Apply underwater fx dsp_water (serial in-line)
  2376. if ( bIsUnderwater )
  2377. {
  2378. // BUG: if out of water, previous delays will be heard. must clear dly buffers.
  2379. Device_ApplyDSPEffects( idsp_water, MIX_GetPFrontFromIPaint(IPAINTBUFFER), MIX_GetPRearFromIPaint(IPAINTBUFFER), MIX_GetPCenterFromIPaint(IPAINTBUFFER), count );
  2380. }
  2381. // find dsp gain
  2382. SDEBUG_GetAvgIn(IPAINTBUFFER, count);
  2383. // Apply player fx dsp_player (serial in-line) - does nothing if dsp fx are disabled
  2384. Device_ApplyDSPEffects( idsp_player, MIX_GetPFrontFromIPaint(IPAINTBUFFER), MIX_GetPRearFromIPaint(IPAINTBUFFER), MIX_GetPCenterFromIPaint(IPAINTBUFFER), count );
  2385. // display dsp gain
  2386. SDEBUG_GetAvgOut(IPAINTBUFFER, count);
  2387. /*
  2388. // apply left/center/right/lrear/rrear spatial delays to paint buffer
  2389. if ( b_spatial_delays )
  2390. Device_ApplyDSPEffects( idsp_spatial, MIX_GetPFrontFromIPaint(IPAINTBUFFER), MIX_GetPRearFromIPaint(IPAINTBUFFER), MIX_GetPCenterFromIPaint(IPAINTBUFFER), count );
  2391. */
  2392. // Add dry buffer, set output gain to water * player dsp gain (both 1.0 if not active)
  2393. MIX_MixPaintbuffers( IPAINTBUFFER, IDRYBUFFER, IPAINTBUFFER, count, 1.0);
  2394. // clip all values > 16 bit down to 16 bit
  2395. // NOTE: This is required - the hardware buffer transfer routines no longer perform clipping.
  2396. MIX_CompressPaintbuffer( IPAINTBUFFER, count );
  2397. // transfer IPAINTBUFFER paintbuffer out to DMA buffer
  2398. MIX_SetCurrentPaintbuffer( IPAINTBUFFER );
  2399. g_AudioDevice->TransferSamples( end );
  2400. g_paintedtime = end;
  2401. }
  2402. }
  2403. // Applies volume scaling (evenly) to all fl,fr,rl,rr volumes
  2404. // used for voice ducking and panning between various mix busses
  2405. // Ensures if mixing to speaker buffer, only speaker sounds pass through
  2406. // Called just before mixing wav data to current paintbuffer.
  2407. // a) if another player in a multiplayer game is speaking, scale all volumes down.
  2408. // b) if mixing to IROOMBUFFER, scale all volumes by ch.dspmix and dsp_room gain
  2409. // c) if mixing to IFACINGAWAYBUFFER, scale all volumes by ch.dspface and dsp_facingaway gain
  2410. // d) If SURROUND_ON, but buffer is not surround, recombined front/rear volumes
  2411. // returns false if channel is to be entirely skipped.
  2412. bool MIX_ScaleChannelVolume( paintbuffer_t *ppaint, channel_t *pChannel, float volume[CCHANVOLUMES], int mixchans )
  2413. {
  2414. int i;
  2415. int mixflag = ppaint->flags;
  2416. float scale;
  2417. char wavtype = pChannel->wavtype;
  2418. float dspmix;
  2419. // copy current channel volumes into output array
  2420. ChannelCopyVolumes( pChannel, volume, 0, CCHANVOLUMES );
  2421. dspmix = pChannel->dspmix;
  2422. dspmix *= 256.0; // Pre-multiply the dspmix by 256 so we can do integer arithmetic
  2423. // It will reduce LHS on game console.
  2424. // if dsp is off, or room dsp is off, mix 0% to mono room buffer, 100% to facing buffer
  2425. if ( g_bDspOff || DSP_RoomDSPIsOff() )
  2426. dspmix = 0.0;
  2427. // duck all sound volumes except speaker's voice
  2428. #if !defined( NO_VOICE )
  2429. int duckScale = MIN(g_DuckScaleInt256, g_SND_VoiceOverdriveInt); // g_SND_VoiceOverdriveInt is already multipled by 256
  2430. #else
  2431. int duckScale = g_DuckScaleInt256;
  2432. #endif
  2433. if( duckScale < 256 )
  2434. {
  2435. if( pChannel->pMixer )
  2436. {
  2437. CAudioSource *pSource = pChannel->pMixer->GetSource();
  2438. if( !pSource->IsVoiceSource() )
  2439. {
  2440. // Apply voice overdrive..
  2441. for (i = 0; i < CCHANVOLUMES; i++)
  2442. volume[i] = (volume[i] * duckScale) / 256.0;
  2443. }
  2444. }
  2445. }
  2446. // If mixing to the room buss, adjust volume based on channel's dspmix setting.
  2447. // dspmix is DSP_MIX_MAX (~0.78) if sound is far from player, DSP_MIX_MIN (~0.24) if sound is near player
  2448. if ( mixflag & SOUND_BUSS_ROOM )
  2449. {
  2450. // set dsp mix volume, scaled by global dsp_volume
  2451. // Values are pre-multiplied by 256
  2452. int dspmixvol = imin( (int)(dspmix * g_dsp_volume), 256 ); // LHS
  2453. // if dspmix is 1.0, 100% of sound goes to IROOMBUFFER and 0% to IFACINGBUFFER
  2454. for (i = 0; i < CCHANVOLUMES; i++)
  2455. volume[i] = ( volume[i] * dspmixvol ) / 256.0f;
  2456. }
  2457. // If global dsp volume is less than 1, reduce dspmix (ie: increase dry volume)
  2458. // If gloabl dsp volume is greater than 1, do not reduce dspmix
  2459. if (g_dsp_volume < 1.0)
  2460. dspmix *= g_dsp_volume;
  2461. // If mixing to facing/facingaway buss, adjust volume based on sound entity's facing direction.
  2462. // If sound directly faces player, ch->dspface = 1.0. If facing directly away, ch->dspface = -1.0.
  2463. // mix to lowpass buffer if facing away, to allpass if facing
  2464. // scale 1.0 - facing player, scale 0, facing away
  2465. scale = (pChannel->dspface + 1.0) / 2.0;
  2466. // UNDONE: get front cone % from channel to set this.
  2467. // bias scale such that 1.0 to 'cone' is considered facing. Facing cone narrows as cone -> 1.0
  2468. // and 'cone' -> 0.0 becomes 1.0 -> 0.0
  2469. float cone = 0.6f;
  2470. scale = scale * (1/cone);
  2471. scale = clamp( scale, 0.0f, 1.0f );
  2472. // pan between facing and facing away buffers
  2473. // if ( !g_bdirectionalfx || wavtype == CHAR_DOPPLER || wavtype == CHAR_OMNI || (wavtype == CHAR_DIRECTIONAL && mixchans == 2) )
  2474. if ( !g_bdirectionalfx || wavtype != CHAR_DIRECTIONAL )
  2475. {
  2476. // if no directional fx mix 0% to facingaway buffer
  2477. // if wavtype is DOPPLER, mix 0% to facingaway buffer - DOPPLER wavs have a custom mixer
  2478. // if wavtype is OMNI, mix 0% to facingaway buffer - OMNI wavs have no directionality
  2479. // if wavtype is DIRECTIONAL and stereo encoded, mix 0% to facingaway buffer - DIRECTIONAL STEREO wavs have a custom mixer
  2480. scale = 1.0;
  2481. }
  2482. if ( mixflag & SOUND_BUSS_FACING )
  2483. {
  2484. // facing player
  2485. // if dspface is 1.0, 100% of sound goes to IFACINGBUFFER
  2486. float fMultiplier = scale * ( 256.0f - dspmix ); // dspmix is pre-multiplied by 256
  2487. int nMultiplier = (int)fMultiplier; // LHS
  2488. for (i = 0; i < CCHANVOLUMES; i++)
  2489. volume[i] = ( volume[i] * nMultiplier ) / 256.0f;
  2490. }
  2491. else if ( mixflag & SOUND_BUSS_FACINGAWAY )
  2492. {
  2493. // facing away from player
  2494. // if dspface is 0.0, 100% of sound goes to IFACINGAWAYBUFFER
  2495. float fMultiplier = ( 1.0f - scale ) * ( 256.0f - dspmix ); // dspmix is pre-multiplied by 256
  2496. int nMultiplier = (int)fMultiplier; // LHS
  2497. for (i = 0; i < CCHANVOLUMES; i++)
  2498. volume[i] = ( volume[i] * nMultiplier ) / 256.0f;
  2499. }
  2500. // NOTE: this must occur last in this routine:
  2501. if ( g_AudioDevice->IsSurround() && !ppaint->fsurround )
  2502. {
  2503. // if 4ch or 5ch spatialization on, but current mix buffer is 2ch,
  2504. // recombine front + rear volumes (revert to 2ch spatialization)
  2505. // Use temp variables to reduce LHS
  2506. int nFrontRight = volume[IFRONT_RIGHT];
  2507. int nFrontLeft = volume[IFRONT_LEFT];
  2508. int nFrontRightD = volume[IFRONT_RIGHTD];
  2509. int nFrontLeftD = volume[IFRONT_LEFTD];
  2510. nFrontRight += volume[IREAR_RIGHT];
  2511. nFrontLeft += volume[IREAR_LEFT];
  2512. nFrontRightD += volume[IREAR_RIGHTD];
  2513. nFrontLeftD += volume[IREAR_LEFTD];
  2514. // if 5 ch, recombine center channel vol
  2515. if ( g_AudioDevice->IsSurroundCenter() )
  2516. {
  2517. nFrontRight += volume[IFRONT_CENTER] / 2;
  2518. nFrontLeft += volume[IFRONT_CENTER] / 2;
  2519. nFrontRightD += volume[IFRONT_CENTERD] / 2;
  2520. nFrontLeftD += volume[IFRONT_CENTERD] / 2;
  2521. }
  2522. volume[IFRONT_RIGHT] = nFrontRight;
  2523. volume[IFRONT_LEFT] = nFrontLeft;
  2524. volume[IFRONT_RIGHTD] = nFrontRightD;
  2525. volume[IFRONT_LEFTD] = nFrontLeftD;
  2526. // clear rear & center volumes
  2527. volume[IREAR_RIGHT] = 0;
  2528. volume[IREAR_LEFT] = 0;
  2529. volume[IFRONT_CENTER] = 0;
  2530. volume[IREAR_RIGHTD] = 0;
  2531. volume[IREAR_LEFTD] = 0;
  2532. volume[IFRONT_CENTERD] = 0;
  2533. // Note that we pay another set of LHS with iclamp below, we could embed the iclamp above (and have a simpler fzerovolume test).
  2534. }
  2535. bool fzerovolume = true;
  2536. for (i = 0; i < CCHANVOLUMES; i++)
  2537. {
  2538. volume[i] = iclamp(volume[i], 0, 255);
  2539. if (volume[i])
  2540. fzerovolume = false;
  2541. }
  2542. if ( fzerovolume )
  2543. {
  2544. // DevMsg ("Skipping mix of 0 volume sound! \n");
  2545. return false;
  2546. }
  2547. return true;
  2548. }
  2549. //===============================================================================
  2550. // Low level mixing routines
  2551. //===============================================================================
  2552. void Snd_WriteLinearBlastStereo16( void )
  2553. {
  2554. #if !id386
  2555. int i;
  2556. int val;
  2557. for ( i=0; i<snd_linear_count; i+=2 )
  2558. {
  2559. // scale and clamp left 16bit signed: [0x8000, 0x7FFF]
  2560. val = ( snd_p[i] * snd_vol )>>8;
  2561. if ( val > 32767 )
  2562. snd_out[i] = 32767;
  2563. else if ( val < -32768 )
  2564. snd_out[i] = -32768;
  2565. else
  2566. snd_out[i] = val;
  2567. // scale and clamp right 16bit signed: [0x8000, 0x7FFF]
  2568. val = ( snd_p[i+1] * snd_vol )>>8;
  2569. if ( val > 32767 )
  2570. snd_out[i+1] = 32767;
  2571. else if ( val < -32768 )
  2572. snd_out[i+1] = -32768;
  2573. else
  2574. snd_out[i+1] = val;
  2575. }
  2576. #else
  2577. __asm
  2578. {
  2579. // input data
  2580. mov ebx,snd_p
  2581. // output data
  2582. mov edi,snd_out
  2583. // iterate from end to beginning
  2584. mov ecx,snd_linear_count
  2585. // scale table
  2586. mov esi,snd_vol
  2587. // scale and clamp 16bit signed lsw: [0x8000, 0x7FFF]
  2588. WLBS16_LoopTop:
  2589. mov eax,[ebx+ecx*4-8]
  2590. imul eax,esi
  2591. sar eax,0x08
  2592. cmp eax,0x7FFF
  2593. jg WLBS16_ClampHigh
  2594. cmp eax,0xFFFF8000
  2595. jnl WLBS16_ClampDone
  2596. mov eax,0xFFFF8000
  2597. jmp WLBS16_ClampDone
  2598. WLBS16_ClampHigh:
  2599. mov eax,0x7FFF
  2600. WLBS16_ClampDone:
  2601. // scale and clamp 16bit signed msw: [0x8000, 0x7FFF]
  2602. mov edx,[ebx+ecx*4-4]
  2603. imul edx,esi
  2604. sar edx,0x08
  2605. cmp edx,0x7FFF
  2606. jg WLBS16_ClampHigh2
  2607. cmp edx,0xFFFF8000
  2608. jnl WLBS16_ClampDone2
  2609. mov edx,0xFFFF8000
  2610. jmp WLBS16_ClampDone2
  2611. WLBS16_ClampHigh2:
  2612. mov edx,0x7FFF
  2613. WLBS16_ClampDone2:
  2614. shl edx,0x10
  2615. and eax,0xFFFF
  2616. or edx,eax
  2617. mov [edi+ecx*2-4],edx
  2618. // two shorts per iteration
  2619. sub ecx,0x02
  2620. jnz WLBS16_LoopTop
  2621. }
  2622. #endif
  2623. }
  2624. void SND_InitScaletable (void)
  2625. {
  2626. int i, j;
  2627. for (i=0 ; i<SND_SCALE_LEVELS; i++)
  2628. for (j=0 ; j<256 ; j++)
  2629. snd_scaletable[i][j] = ((signed char)j) * i * (1<<SND_SCALE_SHIFT);
  2630. }
  2631. void SND_PaintChannelFrom8(portable_samplepair_t *pOutput, float *volume, byte *pData8, int count)
  2632. {
  2633. #if 1
  2634. int data;
  2635. int *lscale, *rscale;
  2636. int i;
  2637. lscale = snd_scaletable[int(volume[0]) >> SND_SCALE_SHIFT];
  2638. rscale = snd_scaletable[int(volume[1]) >> SND_SCALE_SHIFT];
  2639. for (i=0 ; i<count ; i++)
  2640. {
  2641. data = pData8[i];
  2642. pOutput[i].left += lscale[data];
  2643. pOutput[i].right += rscale[data];
  2644. }
  2645. #else
  2646. // portable_samplepair_t structure
  2647. #define psp_left 0
  2648. #define psp_right 4
  2649. #define psp_size 8
  2650. static int tempStore;
  2651. __asm
  2652. {
  2653. // prologue
  2654. push ebp
  2655. // esp = pOutput
  2656. mov eax, pOutput
  2657. mov tempStore, eax
  2658. xchg esp,tempStore
  2659. // ebx = volume
  2660. mov ebx,volume
  2661. // esi = pData8
  2662. mov esi,pData8
  2663. // ecx = count
  2664. mov ecx,count
  2665. // These values depend on the setting of SND_SCALE_BITS
  2666. // The mask must mask off all the lower bits you aren't using in the multiply
  2667. // so for 7 bits, the mask is 0xFE, 6 bits 0xFC, etc.
  2668. // The shift must multiply by the table size. There are 256 4-byte values in the table at each level.
  2669. // So each index must be shifted left by 10, but since the bits we use are in the MSB rather than LSB
  2670. // they must be shifted right by 8 - SND_SCALE_BITS. e.g., for a 7 bit number the left shift is:
  2671. // 10 - (8-7) = 9. For a 5 bit number it's 10 - (8-5) = 7.
  2672. mov eax,[ebx]
  2673. mov edx,[ebx + 4]
  2674. and eax,0xFE
  2675. and edx,0xFE
  2676. // shift up by 10 to index table, down by 1 to make the 7 MSB of the bytes an index
  2677. // eax = lscale
  2678. // edx = rscale
  2679. shl eax,0x09
  2680. shl edx,0x09
  2681. add eax,OFFSET snd_scaletable
  2682. add edx,OFFSET snd_scaletable
  2683. // ebx = data byte
  2684. sub ebx,ebx
  2685. mov bl,[esi+ecx-1]
  2686. // odd or even number of L/R samples
  2687. test ecx,0x01
  2688. jz PCF8_Loop
  2689. // process odd L/R sample
  2690. mov edi,[eax+ebx*4]
  2691. mov ebp,[edx+ebx*4]
  2692. add edi,[esp+ecx*psp_size-psp_size+psp_left]
  2693. add ebp,[esp+ecx*psp_size-psp_size+psp_right]
  2694. mov [esp+ecx*psp_size-psp_size+psp_left],edi
  2695. mov [esp+ecx*psp_size-psp_size+psp_right],ebp
  2696. mov bl,[esi+ecx-1-1]
  2697. dec ecx
  2698. jz PCF8_Done
  2699. PCF8_Loop:
  2700. // process L/R sample N
  2701. mov edi,[eax+ebx*4]
  2702. mov ebp,[edx+ebx*4]
  2703. add edi,[esp+ecx*psp_size-psp_size+psp_left]
  2704. add ebp,[esp+ecx*psp_size-psp_size+psp_right]
  2705. mov [esp+ecx*psp_size-psp_size+psp_left],edi
  2706. mov [esp+ecx*psp_size-psp_size+psp_right],ebp
  2707. mov bl,[esi+ecx-1-1]
  2708. // process L/R sample N-1
  2709. mov edi,[eax+ebx*4]
  2710. mov ebp,[edx+ebx*4]
  2711. add edi,[esp+ecx*psp_size-psp_size*2+psp_left]
  2712. add ebp,[esp+ecx*psp_size-psp_size*2+psp_right]
  2713. mov [esp+ecx*psp_size-psp_size*2+psp_left],edi
  2714. mov [esp+ecx*psp_size-psp_size*2+psp_right],ebp
  2715. mov bl,[esi+ecx-1-2]
  2716. // two L/R samples per iteration
  2717. sub ecx,0x02
  2718. jnz PCF8_Loop
  2719. PCF8_Done:
  2720. // epilogue
  2721. xchg esp,tempStore
  2722. pop ebp
  2723. }
  2724. #endif
  2725. }
  2726. //===============================================================================
  2727. // SOFTWARE MIXING ROUTINES
  2728. //===============================================================================
  2729. // UNDONE: optimize these
  2730. // grab samples from left source channel only and mix as if mono.
  2731. // volume array contains appropriate spatialization volumes for doppler left (incoming sound)
  2732. void SW_Mix8StereoDopplerLeft( portable_samplepair_t *pOutput, float *volume, byte *pData, int inputOffset, fixedint rateScaleFix, int outCount )
  2733. {
  2734. int sampleIndex = 0;
  2735. fixedint sampleFrac = inputOffset;
  2736. int *lscale, *rscale;
  2737. lscale = snd_scaletable[int(volume[0]) >> SND_SCALE_SHIFT];
  2738. rscale = snd_scaletable[int(volume[1]) >> SND_SCALE_SHIFT];
  2739. for ( int i = 0; i < outCount; i++ )
  2740. {
  2741. pOutput[i].left += lscale[pData[sampleIndex]];
  2742. pOutput[i].right += rscale[pData[sampleIndex]];
  2743. sampleFrac += rateScaleFix;
  2744. sampleIndex += FIX_INTPART(sampleFrac)<<1;
  2745. sampleFrac = FIX_FRACPART(sampleFrac);
  2746. }
  2747. }
  2748. // grab samples from right source channel only and mix as if mono.
  2749. // volume array contains appropriate spatialization volumes for doppler right (outgoing sound)
  2750. void SW_Mix8StereoDopplerRight( portable_samplepair_t *pOutput, float *volume, byte *pData, int inputOffset, fixedint rateScaleFix, int outCount )
  2751. {
  2752. int sampleIndex = 0;
  2753. fixedint sampleFrac = inputOffset;
  2754. int *lscale, *rscale;
  2755. lscale = snd_scaletable[int(volume[0]) >> SND_SCALE_SHIFT];
  2756. rscale = snd_scaletable[int(volume[1]) >> SND_SCALE_SHIFT];
  2757. for ( int i = 0; i < outCount; i++ )
  2758. {
  2759. pOutput[i].left += lscale[pData[sampleIndex+1]];
  2760. pOutput[i].right += rscale[pData[sampleIndex+1]];
  2761. sampleFrac += rateScaleFix;
  2762. sampleIndex += FIX_INTPART(sampleFrac)<<1;
  2763. sampleFrac = FIX_FRACPART(sampleFrac);
  2764. }
  2765. }
  2766. // grab samples from left source channel only and mix as if mono.
  2767. // volume array contains appropriate spatialization volumes for doppler left (incoming sound)
  2768. void SW_Mix16StereoDopplerLeft( portable_samplepair_t *pOutput, float *volume, short *pData, int inputOffset, fixedint rateScaleFix, int outCount )
  2769. {
  2770. int sampleIndex = 0;
  2771. fixedint sampleFrac = inputOffset;
  2772. for ( int i = 0; i < outCount; i++ )
  2773. {
  2774. pOutput[i].left += int((volume[0] * (int)(pData[sampleIndex]))/256.0f);
  2775. pOutput[i].right += int((volume[1] * (int)(pData[sampleIndex]))/256.0f);
  2776. sampleFrac += rateScaleFix;
  2777. sampleIndex += FIX_INTPART(sampleFrac)<<1;
  2778. sampleFrac = FIX_FRACPART(sampleFrac);
  2779. }
  2780. }
  2781. void SW_Mix16StereoDopplerLeft_Interp( portable_samplepair_t *pOutput, float *volume, short *pData, int inputOffset, fixedint rateScaleFix, int outCount )
  2782. {
  2783. int sampleIndex = 0;
  2784. fixedint rateScaleFix14 = FIX_28TO14(rateScaleFix); // convert 28 bit fixed point to 14 bit fixed point
  2785. fixedint sampleFrac14 = FIX_28TO14(inputOffset);
  2786. for ( int i = 0; i < outCount; i++ )
  2787. {
  2788. int first = (int)(pData[sampleIndex]);
  2789. int second = (int)(pData[sampleIndex + 2]);
  2790. int interpl = first + (((second - first) * (int)sampleFrac14) >> 14);
  2791. pOutput[i].left += int((volume[0] * interpl) / 256.0f);
  2792. pOutput[i].right += int((volume[1] * interpl) / 256.0f);
  2793. sampleFrac14 += rateScaleFix14;
  2794. sampleIndex += FIX_INTPART14(sampleFrac14) << 1;
  2795. sampleFrac14 = FIX_FRACPART14(sampleFrac14);
  2796. }
  2797. }
  2798. // grab samples from right source channel only and mix as if mono.
  2799. // volume array contains appropriate spatialization volumes for doppler right (outgoing sound)
  2800. void SW_Mix16StereoDopplerRight( portable_samplepair_t *pOutput, float *volume, short *pData, int inputOffset, fixedint rateScaleFix, int outCount )
  2801. {
  2802. int sampleIndex = 0;
  2803. fixedint sampleFrac = inputOffset;
  2804. for ( int i = 0; i < outCount; i++ )
  2805. {
  2806. pOutput[i].left += int((volume[0] * (int)(pData[sampleIndex+1])) / 256.0f);
  2807. pOutput[i].right += int((volume[1] * (int)(pData[sampleIndex+1])) / 256.0f);
  2808. sampleFrac += rateScaleFix;
  2809. sampleIndex += FIX_INTPART(sampleFrac)<<1;
  2810. sampleFrac = FIX_FRACPART(sampleFrac);
  2811. }
  2812. }
  2813. void SW_Mix16StereoDopplerRight_Interp( portable_samplepair_t *pOutput, float *volume, short *pData, int inputOffset, fixedint rateScaleFix, int outCount )
  2814. {
  2815. SW_Mix16StereoDopplerLeft_Interp( pOutput, volume, pData + 1, inputOffset, rateScaleFix, outCount );
  2816. }
  2817. // mix left wav (front facing) with right wav (rear facing) based on soundfacing direction
  2818. void SW_Mix8StereoDirectional( float soundfacing, portable_samplepair_t *pOutput, float *volume, byte *pData, int inputOffset, fixedint rateScaleFix, int outCount )
  2819. {
  2820. int sampleIndex = 0;
  2821. fixedint sampleFrac = inputOffset;
  2822. int x;
  2823. int l,r;
  2824. signed char lb,rb;
  2825. int *lscale, *rscale;
  2826. lscale = snd_scaletable[int(volume[0]) >> SND_SCALE_SHIFT];
  2827. rscale = snd_scaletable[int(volume[1]) >> SND_SCALE_SHIFT];
  2828. // if soundfacing -1.0, sound source is facing away from player
  2829. // if soundfacing 0.0, sound source is perpendicular to player
  2830. // if soundfacing 1.0, sound source is facing player
  2831. int frontmix = (int)(256.0f * ((1.f + soundfacing) / 2.f)); // 0 -> 256
  2832. for ( int i = 0; i < outCount; i++ )
  2833. {
  2834. lb = (pData[sampleIndex]); // get left byte
  2835. rb = (pData[sampleIndex+1]); // get right byte
  2836. l = ((int)lb);
  2837. r = ((int)rb);
  2838. x = ( r + ((( l - r ) * frontmix) >> 8) );
  2839. pOutput[i].left += lscale[x & 0xFF]; // multiply by volume and convert to 16 bit
  2840. pOutput[i].right += rscale[x & 0xFF];
  2841. sampleFrac += rateScaleFix;
  2842. sampleIndex += FIX_INTPART(sampleFrac)<<1;
  2843. sampleFrac = FIX_FRACPART(sampleFrac);
  2844. }
  2845. }
  2846. // mix left wav (front facing) with right wav (rear facing) based on soundfacing direction
  2847. // interpolating pitch shifter - sample(s) from preceding buffer are preloaded in
  2848. // pData buffer, ensuring we can always provide 'outCount' samples.
  2849. void SW_Mix8StereoDirectional_Interp( float soundfacing, portable_samplepair_t *pOutput, float *volume, byte *pData, int inputOffset, fixedint rateScaleFix, int outCount )
  2850. {
  2851. fixedint sampleIndex = 0;
  2852. fixedint rateScaleFix14 = FIX_28TO14(rateScaleFix); // convert 28 bit fixed point to 14 bit fixed point
  2853. fixedint sampleFrac14 = FIX_28TO14(inputOffset);
  2854. int first, second, interpl, interpr;
  2855. int *lscale, *rscale;
  2856. lscale = snd_scaletable[int(volume[0]) >> SND_SCALE_SHIFT];
  2857. rscale = snd_scaletable[int(volume[1]) >> SND_SCALE_SHIFT];
  2858. int x;
  2859. // if soundfacing -1.0, sound source is facing away from player
  2860. // if soundfacing 0.0, sound source is perpendicular to player
  2861. // if soundfacing 1.0, sound source is facing player
  2862. int frontmix = (int)(256.0f * ((1.f + soundfacing) / 2.f)); // 0 -> 256
  2863. for ( int i = 0; i < outCount; i++ )
  2864. {
  2865. // interpolate between first & second sample (the samples bordering sampleFrac12 fraction)
  2866. first = (int)((signed char)(pData[sampleIndex])); // left byte
  2867. second = (int)((signed char)(pData[sampleIndex+2]));
  2868. interpl = first + ( ((second - first) * (int)sampleFrac14) >> 14 );
  2869. first = (int)((signed char)(pData[sampleIndex+1])); // right byte
  2870. second = (int)((signed char)(pData[sampleIndex+3]));
  2871. interpr = first + ( ((second - first) * (int)sampleFrac14) >> 14 );
  2872. // crossfade between right/left based on directional mix
  2873. x = ( interpr + ((( interpl - interpr ) * frontmix) >> 8) );
  2874. pOutput[i].left += lscale[x & 0xFF]; // scale and convert to 16 bit
  2875. pOutput[i].right += rscale[x & 0xFF];
  2876. sampleFrac14 += rateScaleFix14;
  2877. sampleIndex += FIX_INTPART14(sampleFrac14)<<1;
  2878. sampleFrac14 = FIX_FRACPART14(sampleFrac14);
  2879. }
  2880. }
  2881. // mix left wav (front facing) with right wav (rear facing) based on soundfacing direction
  2882. void SW_Mix16StereoDirectional( float soundfacing, portable_samplepair_t *pOutput, float *volume, short *pData, int inputOffset, fixedint rateScaleFix, int outCount )
  2883. {
  2884. fixedint sampleIndex = 0;
  2885. fixedint sampleFrac = inputOffset;
  2886. int x;
  2887. int l, r;
  2888. // if soundfacing -1.0, sound source is facing away from player
  2889. // if soundfacing 0.0, sound source is perpendicular to player
  2890. // if soundfacing 1.0, sound source is facing player
  2891. int frontmix = (int)(256.0f * ((1.f + soundfacing) / 2.f)); // 0 -> 256
  2892. for ( int i = 0; i < outCount; i++ )
  2893. {
  2894. // get left, right samples
  2895. l = (int)(pData[sampleIndex]);
  2896. r = (int)(pData[sampleIndex+1]);
  2897. // crossfade between left & right based on front/rear facing
  2898. x = ( r + ((( l - r ) * frontmix) >> 8) );
  2899. pOutput[i].left += int((volume[0] * x) / 256.0f);
  2900. pOutput[i].right += int((volume[1] * x) / 256.0f);
  2901. sampleFrac += rateScaleFix;
  2902. sampleIndex += FIX_INTPART(sampleFrac)<<1;
  2903. sampleFrac = FIX_FRACPART(sampleFrac);
  2904. }
  2905. }
  2906. // mix left wav (front facing) with right wav (rear facing) based on soundfacing direction
  2907. // interpolating pitch shifter - sample(s) from preceding buffer are preloaded in
  2908. // pData buffer, ensuring we can always provide 'outCount' samples.
  2909. void SW_Mix16StereoDirectional_Interp( float soundfacing, portable_samplepair_t *pOutput, float *volume, short *pData, int inputOffset, fixedint rateScaleFix, int outCount )
  2910. {
  2911. fixedint sampleIndex = 0;
  2912. fixedint rateScaleFix14 = FIX_28TO14(rateScaleFix); // convert 28 bit fixed point to 14 bit fixed point
  2913. fixedint sampleFrac14 = FIX_28TO14(inputOffset);
  2914. int x;
  2915. int first, second, interpl, interpr;
  2916. // if soundfacing -1.0, sound source is facing away from player
  2917. // if soundfacing 0.0, sound source is perpendicular to player
  2918. // if soundfacing 1.0, sound source is facing player
  2919. int frontmix = (int)(256.0f * ((1.f + soundfacing) / 2.f)); // 0 -> 256
  2920. for ( int i = 0; i < outCount; i++ )
  2921. {
  2922. // get interpolated left, right samples
  2923. first = (int)(pData[sampleIndex]);
  2924. second = (int)(pData[sampleIndex+2]);
  2925. interpl = first + (((second - first) * (int)sampleFrac14) >> 14);
  2926. first = (int)(pData[sampleIndex+1]);
  2927. second = (int)(pData[sampleIndex+3]);
  2928. interpr = first + (((second - first) * (int)sampleFrac14) >> 14);
  2929. // crossfade between left & right based on front/rear facing
  2930. x = ( interpr + ((( interpl - interpr ) * frontmix) >> 8) );
  2931. pOutput[i].left += int((volume[0] * x) / 256.0f);
  2932. pOutput[i].right += int((volume[1] * x) / 256.0f);
  2933. sampleFrac14 += rateScaleFix14;
  2934. sampleIndex += FIX_INTPART14(sampleFrac14)<<1;
  2935. sampleFrac14 = FIX_FRACPART14(sampleFrac14);
  2936. }
  2937. }
  2938. // distance variant wav (left is close, right is far)
  2939. void SW_Mix8StereoDistVar( float distmix, portable_samplepair_t *pOutput, float *volume, byte *pData, int inputOffset, fixedint rateScaleFix, int outCount )
  2940. {
  2941. int sampleIndex = 0;
  2942. fixedint sampleFrac = inputOffset;
  2943. int x;
  2944. int l,r;
  2945. signed char lb, rb;
  2946. int *lscale, *rscale;
  2947. lscale = snd_scaletable[int(volume[0]) >> SND_SCALE_SHIFT];
  2948. rscale = snd_scaletable[int(volume[1]) >> SND_SCALE_SHIFT];
  2949. // distmix 0 - sound is near player (100% wav left)
  2950. // distmix 1.0 - sound is far from player (100% wav right)
  2951. int nearmix = (int)(256.0f * (1.0f - distmix));
  2952. int farmix = (int)(256.0f * distmix);
  2953. // if mixing at max or min range, skip crossfade (KDB: perf)
  2954. if (!nearmix)
  2955. {
  2956. for ( int i = 0; i < outCount; i++ )
  2957. {
  2958. rb = (pData[sampleIndex+1]); // get right byte
  2959. x = (int) rb;
  2960. pOutput[i].left += lscale[x & 0xFF]; // multiply by volume and convert to 16 bit
  2961. pOutput[i].right += rscale[x & 0xFF];
  2962. sampleFrac += rateScaleFix;
  2963. sampleIndex += FIX_INTPART(sampleFrac)<<1;
  2964. sampleFrac = FIX_FRACPART(sampleFrac);
  2965. }
  2966. return;
  2967. }
  2968. if (!farmix)
  2969. {
  2970. for ( int i = 0; i < outCount; i++ )
  2971. {
  2972. lb = (pData[sampleIndex]); // get left byte
  2973. x = (int) lb;
  2974. pOutput[i].left += lscale[x & 0xFF]; // multiply by volume and convert to 16 bit
  2975. pOutput[i].right += rscale[x & 0xFF];
  2976. sampleFrac += rateScaleFix;
  2977. sampleIndex += FIX_INTPART(sampleFrac)<<1;
  2978. sampleFrac = FIX_FRACPART(sampleFrac);
  2979. }
  2980. return;
  2981. }
  2982. // crossfade left/right
  2983. for ( int i = 0; i < outCount; i++ )
  2984. {
  2985. lb = (pData[sampleIndex]); // get left byte
  2986. rb = (pData[sampleIndex+1]); // get right byte
  2987. l = (int)lb;
  2988. r = (int)rb;
  2989. x = ( l + (((r - l) * farmix ) >> 8) );
  2990. pOutput[i].left += lscale[x & 0xFF]; // multiply by volume and convert to 16 bit
  2991. pOutput[i].right += rscale[x & 0xFF];
  2992. sampleFrac += rateScaleFix;
  2993. sampleIndex += FIX_INTPART(sampleFrac)<<1;
  2994. sampleFrac = FIX_FRACPART(sampleFrac);
  2995. }
  2996. }
  2997. // distance variant wav (left is close, right is far)
  2998. // interpolating pitch shifter - sample(s) from preceding buffer are preloaded in
  2999. // pData buffer, ensuring we can always provide 'outCount' samples.
  3000. void SW_Mix8StereoDistVar_Interp( float distmix, portable_samplepair_t *pOutput, float *volume, byte *pData, int inputOffset, fixedint rateScaleFix, int outCount )
  3001. {
  3002. int x;
  3003. // distmix 0 - sound is near player (100% wav left)
  3004. // distmix 1.0 - sound is far from player (100% wav right)
  3005. int nearmix = (int)(256.0f * (1.0f - distmix));
  3006. int farmix = (int)(256.0f * distmix);
  3007. fixedint sampleIndex = 0;
  3008. fixedint rateScaleFix14 = FIX_28TO14(rateScaleFix); // convert 28 bit fixed point to 14 bit fixed point
  3009. fixedint sampleFrac14 = FIX_28TO14(inputOffset);
  3010. int first, second, interpl, interpr;
  3011. int *lscale, *rscale;
  3012. lscale = snd_scaletable[int(volume[0]) >> SND_SCALE_SHIFT];
  3013. rscale = snd_scaletable[int(volume[1]) >> SND_SCALE_SHIFT];
  3014. // if mixing at max or min range, skip crossfade (KDB: perf)
  3015. if (!nearmix)
  3016. {
  3017. for ( int i = 0; i < outCount; i++ )
  3018. {
  3019. first = (int)((signed char)(pData[sampleIndex+1])); // right sample
  3020. second = (int)((signed char)(pData[sampleIndex+3]));
  3021. interpr = first + ( ((second - first) * (int)sampleFrac14) >> 14 );
  3022. pOutput[i].left += lscale[interpr & 0xFF]; // scale and convert to 16 bit
  3023. pOutput[i].right += rscale[interpr & 0xFF];
  3024. sampleFrac14 += rateScaleFix14;
  3025. sampleIndex += FIX_INTPART14(sampleFrac14)<<1;
  3026. sampleFrac14 = FIX_FRACPART14(sampleFrac14);
  3027. }
  3028. return;
  3029. }
  3030. if (!farmix)
  3031. {
  3032. for ( int i = 0; i < outCount; i++ )
  3033. {
  3034. first = (int)((signed char)(pData[sampleIndex])); // left sample
  3035. second = (int)((signed char)(pData[sampleIndex+2]));
  3036. interpl = first + ( ((second - first) * (int)sampleFrac14) >> 14 );
  3037. pOutput[i].left += lscale[interpl & 0xFF]; // scale and convert to 16 bit
  3038. pOutput[i].right += rscale[interpl & 0xFF];
  3039. sampleFrac14 += rateScaleFix14;
  3040. sampleIndex += FIX_INTPART14(sampleFrac14)<<1;
  3041. sampleFrac14 = FIX_FRACPART14(sampleFrac14);
  3042. }
  3043. return;
  3044. }
  3045. // crossfade left/right
  3046. for ( int i = 0; i < outCount; i++ )
  3047. {
  3048. // interpolate between first & second sample (the samples bordering sampleFrac14 fraction)
  3049. first = (int)((signed char)(pData[sampleIndex]));
  3050. second = (int)((signed char)(pData[sampleIndex+2]));
  3051. interpl = first + ( ((second - first) * (int)sampleFrac14) >> 14 );
  3052. first = (int)((signed char)(pData[sampleIndex+1]));
  3053. second = (int)((signed char)(pData[sampleIndex+3]));
  3054. interpr = first + ( ((second - first) * (int)sampleFrac14) >> 14 );
  3055. // crossfade between left and right based on distance mix
  3056. x = ( interpl + (((interpr - interpl) * farmix ) >> 8) );
  3057. pOutput[i].left += lscale[x & 0xFF]; // scale and convert to 16 bit
  3058. pOutput[i].right += rscale[x & 0xFF];
  3059. sampleFrac14 += rateScaleFix14;
  3060. sampleIndex += FIX_INTPART14(sampleFrac14)<<1;
  3061. sampleFrac14 = FIX_FRACPART14(sampleFrac14);
  3062. }
  3063. }
  3064. // distance variant wav (left is close, right is far)
  3065. void SW_Mix16StereoDistVar( float distmix, portable_samplepair_t *pOutput, float *volume, short *pData, int inputOffset, fixedint rateScaleFix, int outCount )
  3066. {
  3067. int sampleIndex = 0;
  3068. fixedint sampleFrac = inputOffset;
  3069. int x;
  3070. int l,r;
  3071. // distmix 0 - sound is near player (100% wav left)
  3072. // distmix 1.0 - sound is far from player (100% wav right)
  3073. int nearmix = Float2Int(256.0f * (1.f - distmix));
  3074. int farmix = Float2Int(256.0f * distmix);
  3075. // if mixing at max or min range, skip crossfade (KDB: perf)
  3076. if (!nearmix)
  3077. {
  3078. for ( int i = 0; i < outCount; i++ )
  3079. {
  3080. x = pData[sampleIndex+1]; // right sample
  3081. pOutput[i].left += int((volume[0] * x) / 256.0f);
  3082. pOutput[i].right += int((volume[1] * x) / 256.0f);
  3083. sampleFrac += rateScaleFix;
  3084. sampleIndex += FIX_INTPART(sampleFrac)<<1;
  3085. sampleFrac = FIX_FRACPART(sampleFrac);
  3086. }
  3087. return;
  3088. }
  3089. if (!farmix)
  3090. {
  3091. for ( int i = 0; i < outCount; i++ )
  3092. {
  3093. x = pData[sampleIndex]; // left sample
  3094. pOutput[i].left += int((volume[0] * x)/256.0f);
  3095. pOutput[i].right += int((volume[1] * x)/256.0f);
  3096. sampleFrac += rateScaleFix;
  3097. sampleIndex += FIX_INTPART(sampleFrac)<<1;
  3098. sampleFrac = FIX_FRACPART(sampleFrac);
  3099. }
  3100. return;
  3101. }
  3102. // crossfade left/right
  3103. for ( int i = 0; i < outCount; i++ )
  3104. {
  3105. l = pData[sampleIndex];
  3106. r = pData[sampleIndex+1];
  3107. x = ( l + (((r - l) * farmix) >> 8) );
  3108. pOutput[i].left += int((volume[0] * x)/256.0f);
  3109. pOutput[i].right += int((volume[1] * x)/256.0f);
  3110. sampleFrac += rateScaleFix;
  3111. sampleIndex += FIX_INTPART(sampleFrac)<<1;
  3112. sampleFrac = FIX_FRACPART(sampleFrac);
  3113. }
  3114. }
  3115. // distance variant wav (left is close, right is far)
  3116. // interpolating pitch shifter - sample(s) from preceding buffer are preloaded in
  3117. // pData buffer, ensuring we can always provide 'outCount' samples.
  3118. void SW_Mix16StereoDistVar_Interp( float distmix, portable_samplepair_t *pOutput, float *volume, short *pData, int inputOffset, fixedint rateScaleFix, int outCount )
  3119. {
  3120. int x;
  3121. fixedint sampleIndex = 0;
  3122. fixedint rateScaleFix14 = FIX_28TO14(rateScaleFix); // convert 28 bit fixed point to 14 bit fixed point
  3123. fixedint sampleFrac14 = FIX_28TO14(inputOffset);
  3124. int first, second, interpl, interpr;
  3125. // distmix 0 - sound is near player (100% wav left)
  3126. // distmix 1.0 - sound is far from player (100% wav right)
  3127. int nearmix = Float2Int(256.0f * (1.f - distmix));
  3128. int farmix = Float2Int(256.0f * distmix);
  3129. // if mixing at max or min range, skip crossfade (KDB: perf)
  3130. if (!nearmix)
  3131. {
  3132. for ( int i = 0; i < outCount; i++ )
  3133. {
  3134. first = (int)(pData[sampleIndex+1]); // right sample
  3135. second = (int)(pData[sampleIndex+3]);
  3136. interpr = first + (((second - first) * (int)sampleFrac14) >> 14);
  3137. pOutput[i].left += int((volume[0] * interpr)/256.0f);
  3138. pOutput[i].right += int((volume[1] * interpr)/256.0f);
  3139. sampleFrac14 += rateScaleFix14;
  3140. sampleIndex += FIX_INTPART14(sampleFrac14)<<1;
  3141. sampleFrac14 = FIX_FRACPART14(sampleFrac14);
  3142. }
  3143. return;
  3144. }
  3145. if (!farmix)
  3146. {
  3147. for ( int i = 0; i < outCount; i++ )
  3148. {
  3149. first = (int)(pData[sampleIndex]); // left sample
  3150. second = (int)(pData[sampleIndex+2]);
  3151. interpl = first + (((second - first) * (int)sampleFrac14) >> 14);
  3152. pOutput[i].left += int((volume[0] * interpl)/256.0f);
  3153. pOutput[i].right += int((volume[1] * interpl)/256.0f);
  3154. sampleFrac14 += rateScaleFix14;
  3155. sampleIndex += FIX_INTPART14(sampleFrac14)<<1;
  3156. sampleFrac14 = FIX_FRACPART14(sampleFrac14);
  3157. }
  3158. return;
  3159. }
  3160. // crossfade left/right
  3161. for ( int i = 0; i < outCount; i++ )
  3162. {
  3163. first = (int)(pData[sampleIndex]);
  3164. second = (int)(pData[sampleIndex+2]);
  3165. interpl = first + (((second - first) * (int)sampleFrac14) >> 14);
  3166. first = (int)(pData[sampleIndex+1]);
  3167. second = (int)(pData[sampleIndex+3]);
  3168. interpr = first + (((second - first) * (int)sampleFrac14) >> 14);
  3169. // crossfade between left & right samples
  3170. x = ( interpl + (((interpr - interpl) * farmix) >> 8) );
  3171. pOutput[i].left += int((volume[0] * x)/256.0f);
  3172. pOutput[i].right += int((volume[1] * x)/256.0f);
  3173. sampleFrac14 += rateScaleFix14;
  3174. sampleIndex += FIX_INTPART14(sampleFrac14)<<1;
  3175. sampleFrac14 = FIX_FRACPART14(sampleFrac14);
  3176. }
  3177. }
  3178. void SW_Mix8Mono( portable_samplepair_t *pOutput, float *volume, byte *pData, int inputOffset, fixedint rateScaleFix, int outCount )
  3179. {
  3180. // Not using pitch shift?
  3181. if ( rateScaleFix == FIX(1) )
  3182. {
  3183. // native code
  3184. SND_PaintChannelFrom8( pOutput, volume, (byte *)pData, outCount );
  3185. return;
  3186. }
  3187. int sampleIndex = 0;
  3188. fixedint sampleFrac = inputOffset;
  3189. int *lscale, *rscale;
  3190. lscale = snd_scaletable[int(volume[0]) >> SND_SCALE_SHIFT];
  3191. rscale = snd_scaletable[int(volume[1]) >> SND_SCALE_SHIFT];
  3192. for ( int i = 0; i < outCount; i++ )
  3193. {
  3194. pOutput[i].left += lscale[pData[sampleIndex]];
  3195. pOutput[i].right += rscale[pData[sampleIndex]];
  3196. sampleFrac += rateScaleFix;
  3197. sampleIndex += FIX_INTPART(sampleFrac);
  3198. sampleFrac = FIX_FRACPART(sampleFrac);
  3199. }
  3200. }
  3201. // interpolating pitch shifter - sample(s) from preceding buffer are preloaded in
  3202. // pData buffer, ensuring we can always provide 'outCount' samples.
  3203. void SW_Mix8Mono_Interp( portable_samplepair_t *pOutput, float *volume, byte *pData, int inputOffset, fixedint rateScaleFix, int outCount)
  3204. {
  3205. fixedint sampleIndex = 0;
  3206. fixedint rateScaleFix14 = FIX_28TO14(rateScaleFix); // convert 28 bit fixed point to 14 bit fixed point
  3207. fixedint sampleFrac14 = FIX_28TO14(inputOffset);
  3208. int first, second, interp;
  3209. int *lscale, *rscale;
  3210. lscale = snd_scaletable[int(volume[0]) >> SND_SCALE_SHIFT];
  3211. rscale = snd_scaletable[int(volume[1]) >> SND_SCALE_SHIFT];
  3212. // iterate 0th sample to outCount-1 sample
  3213. for (int i = 0; i < outCount; i++ )
  3214. {
  3215. // interpolate between first & second sample (the samples bordering sampleFrac12 fraction)
  3216. first = (int)((signed char)(pData[sampleIndex]));
  3217. second = (int)((signed char)(pData[sampleIndex+1]));
  3218. interp = first + ( ((second - first) * (int)sampleFrac14) >> 14 );
  3219. pOutput[i].left += lscale[interp & 0xFF]; // multiply by volume and convert to 16 bit
  3220. pOutput[i].right += rscale[interp & 0xFF];
  3221. sampleFrac14 += rateScaleFix14;
  3222. sampleIndex += FIX_INTPART14(sampleFrac14);
  3223. sampleFrac14 = FIX_FRACPART14(sampleFrac14);
  3224. }
  3225. }
  3226. void SW_Mix8Stereo( portable_samplepair_t *pOutput, float *volume, byte *pData, int inputOffset, fixedint rateScaleFix, int outCount )
  3227. {
  3228. int sampleIndex = 0;
  3229. fixedint sampleFrac = inputOffset;
  3230. int *lscale, *rscale;
  3231. lscale = snd_scaletable[int(volume[0]) >> SND_SCALE_SHIFT];
  3232. rscale = snd_scaletable[int(volume[1]) >> SND_SCALE_SHIFT];
  3233. for ( int i = 0; i < outCount; i++ )
  3234. {
  3235. pOutput[i].left += lscale[pData[sampleIndex]];
  3236. pOutput[i].right += rscale[pData[sampleIndex+1]];
  3237. sampleFrac += rateScaleFix;
  3238. sampleIndex += FIX_INTPART(sampleFrac)<<1;
  3239. sampleFrac = FIX_FRACPART(sampleFrac);
  3240. }
  3241. }
  3242. // interpolating pitch shifter - sample(s) from preceding buffer are preloaded in
  3243. // pData buffer, ensuring we can always provide 'outCount' samples.
  3244. void SW_Mix8Stereo_Interp( portable_samplepair_t *pOutput, float *volume, byte *pData, int inputOffset, fixedint rateScaleFix, int outCount)
  3245. {
  3246. fixedint sampleIndex = 0;
  3247. fixedint rateScaleFix14 = FIX_28TO14(rateScaleFix); // convert 28 bit fixed point to 14 bit fixed point
  3248. fixedint sampleFrac14 = FIX_28TO14(inputOffset);
  3249. int first, second, interpl, interpr;
  3250. int *lscale, *rscale;
  3251. lscale = snd_scaletable[int(volume[0]) >> SND_SCALE_SHIFT];
  3252. rscale = snd_scaletable[int(volume[1]) >> SND_SCALE_SHIFT];
  3253. // iterate 0th sample to outCount-1 sample
  3254. for (int i = 0; i < outCount; i++ )
  3255. {
  3256. // interpolate between first & second sample (the samples bordering sampleFrac12 fraction)
  3257. first = (int)((signed char)(pData[sampleIndex])); // left
  3258. second = (int)((signed char)(pData[sampleIndex+2]));
  3259. interpl = first + ( ((second - first) * (int)sampleFrac14) >> 14 );
  3260. first = (int)((signed char)(pData[sampleIndex+1])); // right
  3261. second = (int)((signed char)(pData[sampleIndex+3]));
  3262. interpr = first + ( ((second - first) * (int)sampleFrac14) >> 14 );
  3263. pOutput[i].left += lscale[interpl & 0xFF]; // multiply by volume and convert to 16 bit
  3264. pOutput[i].right += rscale[interpr & 0xFF];
  3265. sampleFrac14 += rateScaleFix14;
  3266. sampleIndex += FIX_INTPART14(sampleFrac14)<<1;
  3267. sampleFrac14 = FIX_FRACPART14(sampleFrac14);
  3268. }
  3269. }
  3270. void SW_Mix16Mono_Shift( portable_samplepair_t *pOutput, float *volume, short *pData, int inputOffset, fixedint rateScaleFix, int outCount )
  3271. {
  3272. float vol0 = volume[0];
  3273. float vol1 = volume[1];
  3274. #if 1
  3275. int sampleIndex = 0;
  3276. fixedint sampleFrac = inputOffset;
  3277. for ( int i = 0; i < outCount; i++ )
  3278. {
  3279. pOutput[i].left += int((vol0 * (int)(pData[sampleIndex]))/256.0f);
  3280. pOutput[i].right += int((vol1 * (int)(pData[sampleIndex]))/256.0f);
  3281. sampleFrac += rateScaleFix;
  3282. sampleIndex += FIX_INTPART(sampleFrac);
  3283. sampleFrac = FIX_FRACPART(sampleFrac);
  3284. }
  3285. #else
  3286. // in assembly, you can make this 32.32 instead of 4.28 and use the carry flag instead of masking
  3287. int rateScaleInt = FIX_INTPART(rateScaleFix);
  3288. unsigned int rateScaleFrac = FIX_FRACPART(rateScaleFix) << (32-FIX_BITS);
  3289. __asm
  3290. {
  3291. mov eax, volume ;
  3292. movq mm0, DWORD PTR [eax] ; vol1, vol0 (32-bits each)
  3293. packssdw mm0, mm0 ; pack and replicate... vol1, vol0, vol1, vol0 (16-bits each)
  3294. //pxor mm7, mm7 ; mm7 is my zero register...
  3295. xor esi, esi
  3296. mov eax, DWORD PTR [pOutput] ; store initial output ptr
  3297. mov edx, DWORD PTR [pData] ; store initial input ptr
  3298. mov ebx, inputOffset;
  3299. mov ecx, outCount;
  3300. BEGINLOAD:
  3301. movd mm2, WORD PTR [edx+2*esi] ; load first piece of data from pData
  3302. punpcklwd mm2, mm2 ; 0, 0, pData_1st, pData_1st
  3303. add ebx, rateScaleFrac ; do the crazy fixed integer math
  3304. adc esi, rateScaleInt
  3305. movd mm3, WORD PTR [edx+2*esi] ; load second piece of data from pData
  3306. punpcklwd mm3, mm3 ; 0, 0, pData_2nd, pData_2nd
  3307. punpckldq mm2, mm3 ; pData_2nd, pData_2nd, pData_2nd, pData_2nd
  3308. add ebx, rateScaleFrac ; do the crazy fixed integer math
  3309. adc esi, rateScaleInt
  3310. movq mm3, mm2 ; copy the goods
  3311. pmullw mm2, mm0 ; pData_2nd*vol1, pData_2nd*vol0, pData_1st*vol1, pData_1st*vol0 (bits 0-15)
  3312. pmulhw mm3, mm0 ; pData_2nd*vol1, pData_2nd*vol0, pData_1st*vol1, pData_1st*vol0 (bits 16-31)
  3313. movq mm4, mm2 ; copy
  3314. movq mm5, mm3 ; copy
  3315. punpcklwd mm2, mm3 ; pData_1st*vol1, pData_1st*vol0 (bits 0-31)
  3316. punpckhwd mm4, mm5 ; pData_2nd*vol1, pData_2nd*vol0 (bits 0-31)
  3317. psrad mm2, 8 ; shift right by 8
  3318. psrad mm4, 8 ; shift right by 8
  3319. add ecx, -2 ; decrement i-value
  3320. paddd mm2, QWORD PTR [eax] ; add to existing vals
  3321. paddd mm4, QWORD PTR [eax+8] ;
  3322. movq QWORD PTR [eax], mm2 ; store back
  3323. movq QWORD PTR [eax+8], mm4 ;
  3324. add eax, 10h ;
  3325. cmp ecx, 01h ; see if we can quit
  3326. jg BEGINLOAD ; Kipp Owens is a doof...
  3327. jl END ; Nick Shaffner is killing me...
  3328. movsx edi, WORD PTR [edx+2*esi] ; load first 16 bit val and zero-extend
  3329. imul edi, vol0 ; multiply pData[sampleIndex] by volume[0]
  3330. sar edi, 08h ; divide by 256
  3331. add DWORD PTR [eax], edi ; add to pOutput[i].left
  3332. movsx edi, WORD PTR [edx+2*esi] ; load same 16 bit val and zero-extend (cuz I thrashed the reg)
  3333. imul edi, vol1 ; multiply pData[sampleIndex] by volume[1]
  3334. sar edi, 08h ; divide by 256
  3335. add DWORD PTR [eax+04h], edi ; add to pOutput[i].right
  3336. END:
  3337. emms;
  3338. }
  3339. #endif
  3340. }
  3341. void SW_Mix16Mono_NoShift( portable_samplepair_t *pOutput, float *volume, short *pData, int outCount )
  3342. {
  3343. float vol0 = volume[0];
  3344. float vol1 = volume[1];
  3345. #if 1
  3346. for ( int i = 0; i < outCount; i++ )
  3347. {
  3348. int x = *pData++;
  3349. pOutput[i].left += int((x * vol0) / 256.0f);
  3350. pOutput[i].right += int((x * vol1) / 256.0f);
  3351. }
  3352. #else
  3353. __asm
  3354. {
  3355. mov eax, volume ;
  3356. movq mm0, DWORD PTR [eax] ; vol1, vol0 (32-bits each)
  3357. packssdw mm0, mm0 ; pack and replicate... vol1, vol0, vol1, vol0 (16-bits each)
  3358. //pxor mm7, mm7 ; mm7 is my zero register...
  3359. mov eax, DWORD PTR [pOutput] ; store initial output ptr
  3360. mov edx, DWORD PTR [pData] ; store initial input ptr
  3361. mov ecx, outCount;
  3362. BEGINLOAD:
  3363. movd mm2, WORD PTR [edx] ; load first piece o data from pData
  3364. punpcklwd mm2, mm2 ; 0, 0, pData_1st, pData_1st
  3365. add edx,2 ; move to the next sample
  3366. movd mm3, WORD PTR [edx] ; load second piece o data from pData
  3367. punpcklwd mm3, mm3 ; 0, 0, pData_2nd, pData_2nd
  3368. punpckldq mm2, mm3 ; pData_2nd, pData_2nd, pData_2nd, pData_2nd
  3369. add edx,2 ; move to the next sample
  3370. movq mm3, mm2 ; copy the goods
  3371. pmullw mm2, mm0 ; pData_2nd*vol1, pData_2nd*vol0, pData_1st*vol1, pData_1st*vol0 (bits 0-15)
  3372. pmulhw mm3, mm0 ; pData_2nd*vol1, pData_2nd*vol0, pData_1st*vol1, pData_1st*vol0 (bits 16-31)
  3373. movq mm4, mm2 ; copy
  3374. movq mm5, mm3 ; copy
  3375. punpcklwd mm2, mm3 ; pData_1st*vol1, pData_1st*vol0 (bits 0-31)
  3376. punpckhwd mm4, mm5 ; pData_2nd*vol1, pData_2nd*vol0 (bits 0-31)
  3377. psrad mm2, 8 ; shift right by 8
  3378. psrad mm4, 8 ; shift right by 8
  3379. add ecx, -2 ; decrement i-value
  3380. paddd mm2, QWORD PTR [eax] ; add to existing vals
  3381. paddd mm4, QWORD PTR [eax+8] ;
  3382. movq QWORD PTR [eax], mm2 ; store back
  3383. movq QWORD PTR [eax+8], mm4 ;
  3384. add eax, 10h ;
  3385. cmp ecx, 01h ; see if we can quit
  3386. jg BEGINLOAD ; I can cut and paste code!
  3387. jl END ;
  3388. movsx edi, WORD PTR [edx] ; load first 16 bit val and zero-extend
  3389. mov esi,edi ; save a copy for the other channel
  3390. imul edi, vol0 ; multiply pData[sampleIndex] by volume[0]
  3391. sar edi, 08h ; divide by 256
  3392. add DWORD PTR [eax], edi ; add to pOutput[i].left
  3393. ; esi has a copy, use it now
  3394. imul esi, vol1 ; multiply pData[sampleIndex] by volume[1]
  3395. sar esi, 08h ; divide by 256
  3396. add DWORD PTR [eax+04h], esi ; add to pOutput[i].right
  3397. END:
  3398. emms;
  3399. }
  3400. #endif
  3401. }
  3402. enum SW_FillMode
  3403. {
  3404. FM_SAME_VOL,
  3405. FM_LEFT_ZERO,
  3406. FM_RIGHT_ZERO,
  3407. FM_NORMAL,
  3408. };
  3409. // Try to keep the number of parameters to 4 to make sure the optimizer is not doing something too stupid.
  3410. // Pass the volume by pointer instead of left and right values. It seems that the compiler has harder time optimizing with one more variable.
  3411. template <SW_FillMode MODE>
  3412. void FillMonoOutput( int nValue, portable_samplepair_t * RESTRICT pOutput, float * RESTRICT pVolume );
  3413. template <>
  3414. FORCEINLINE
  3415. void FillMonoOutput<FM_SAME_VOL>( int nValue, portable_samplepair_t * RESTRICT pOutput, float * RESTRICT pVolume )
  3416. {
  3417. nValue = int(( pVolume[0] * nValue ) /256.0f);
  3418. pOutput->left += nValue;
  3419. pOutput->right += nValue;
  3420. }
  3421. template <>
  3422. FORCEINLINE
  3423. void FillMonoOutput<FM_LEFT_ZERO>( int nValue, portable_samplepair_t * RESTRICT pOutput, float * RESTRICT pVolume )
  3424. {
  3425. pOutput->right += int(( pVolume[1] * nValue ) / 256.0f);
  3426. }
  3427. template <>
  3428. FORCEINLINE
  3429. void FillMonoOutput<FM_RIGHT_ZERO>( int nValue, portable_samplepair_t * RESTRICT pOutput, float * RESTRICT pVolume )
  3430. {
  3431. pOutput->left += int(( pVolume[0] * nValue ) / 256.0f);
  3432. }
  3433. template <>
  3434. FORCEINLINE
  3435. void FillMonoOutput<FM_NORMAL>( int nValue, portable_samplepair_t * RESTRICT pOutput, float * RESTRICT pVolume )
  3436. {
  3437. pOutput->left += int(( pVolume[0] * nValue ) /256.0f);
  3438. pOutput->right += int(( pVolume[1] * nValue ) /256.0f);
  3439. }
  3440. template <SW_FillMode MODE>
  3441. void SW_Mix16Mono_Shift_OptMeta( portable_samplepair_t * RESTRICT pOutput, float * RESTRICT pVolume, short * RESTRICT pData, int nInputOffset, fixedint nRateScaleFix, int nOutCount )
  3442. {
  3443. fixedint nSampleFrac = nInputOffset;
  3444. while ( nOutCount >= 4 )
  3445. {
  3446. FillMonoOutput<MODE>( *pData, pOutput, pVolume );
  3447. nSampleFrac += nRateScaleFix;
  3448. pData += FIX_INTPART(nSampleFrac);
  3449. nSampleFrac = FIX_FRACPART(nSampleFrac);
  3450. FillMonoOutput<MODE>( *pData, pOutput + 1, pVolume );
  3451. nSampleFrac += nRateScaleFix;
  3452. pData += FIX_INTPART(nSampleFrac);
  3453. nSampleFrac = FIX_FRACPART(nSampleFrac);
  3454. FillMonoOutput<MODE>( *pData, pOutput + 2, pVolume );
  3455. nSampleFrac += nRateScaleFix;
  3456. pData += FIX_INTPART(nSampleFrac);
  3457. nSampleFrac = FIX_FRACPART(nSampleFrac);
  3458. FillMonoOutput<MODE>( *pData, pOutput + 3, pVolume );
  3459. nSampleFrac += nRateScaleFix;
  3460. pData += FIX_INTPART(nSampleFrac);
  3461. nSampleFrac = FIX_FRACPART(nSampleFrac);
  3462. pOutput += 4;
  3463. nOutCount -= 4;
  3464. }
  3465. while ( nOutCount > 0 )
  3466. {
  3467. FillMonoOutput<MODE>( *pData, pOutput, pVolume );
  3468. nSampleFrac += nRateScaleFix;
  3469. pData += FIX_INTPART(nSampleFrac);
  3470. nSampleFrac = FIX_FRACPART(nSampleFrac);
  3471. ++pOutput;
  3472. --nOutCount;
  3473. }
  3474. }
  3475. void SW_Mix16Mono_Shift_Opt( portable_samplepair_t * RESTRICT pOutput, float * RESTRICT pVolume, short * RESTRICT pData, int nInputOffset, fixedint nRateScaleFix, int nOutCount )
  3476. {
  3477. int nVolumeLeft = pVolume[0];
  3478. int nVolumeRight = pVolume[1];
  3479. if ( nVolumeLeft == nVolumeRight )
  3480. {
  3481. SW_Mix16Mono_Shift_OptMeta<FM_SAME_VOL>( pOutput, pVolume, pData, nInputOffset, nRateScaleFix, nOutCount );
  3482. }
  3483. else
  3484. {
  3485. if ( nVolumeLeft <= CULLED_VOLUME )
  3486. {
  3487. SW_Mix16Mono_Shift_OptMeta<FM_LEFT_ZERO>( pOutput, pVolume, pData, nInputOffset, nRateScaleFix, nOutCount );
  3488. }
  3489. else if ( nVolumeRight <= CULLED_VOLUME )
  3490. {
  3491. SW_Mix16Mono_Shift_OptMeta<FM_RIGHT_ZERO>( pOutput, pVolume, pData, nInputOffset, nRateScaleFix, nOutCount );
  3492. }
  3493. else
  3494. {
  3495. SW_Mix16Mono_Shift_OptMeta<FM_NORMAL>( pOutput, pVolume, pData, nInputOffset, nRateScaleFix, nOutCount );
  3496. }
  3497. }
  3498. }
  3499. template <SW_FillMode MODE>
  3500. void SW_Mix16Mono_NoShift_OptMeta( portable_samplepair_t * RESTRICT pOutput, float * RESTRICT pVolume, short * RESTRICT pData, int nOutCount )
  3501. {
  3502. // This code is relatively lightweight, and usually 255 to 1020 samples are passed. So 8 at a time.
  3503. while ( nOutCount >= 4 )
  3504. {
  3505. FillMonoOutput<MODE>( pData[0], pOutput, pVolume );
  3506. FillMonoOutput<MODE>( pData[1], pOutput + 1, pVolume );
  3507. FillMonoOutput<MODE>( pData[2], pOutput + 2, pVolume );
  3508. FillMonoOutput<MODE>( pData[3], pOutput + 3, pVolume );
  3509. pData += 4;
  3510. pOutput += 4;
  3511. nOutCount -= 4;
  3512. }
  3513. while ( nOutCount > 0 )
  3514. {
  3515. FillMonoOutput<MODE>( pData[0], pOutput, pVolume );
  3516. ++pData;
  3517. ++pOutput;
  3518. --nOutCount;
  3519. }
  3520. }
  3521. void SW_Mix16Mono_NoShift_Opt( portable_samplepair_t * RESTRICT pOutput, float * RESTRICT pVolume, short * RESTRICT pData, int nOutCount )
  3522. {
  3523. int nVolumeLeft = pVolume[0];
  3524. int nVolumeRight = pVolume[1];
  3525. if ( nVolumeLeft == nVolumeRight )
  3526. {
  3527. SW_Mix16Mono_NoShift_OptMeta<FM_SAME_VOL>( pOutput, pVolume, pData, nOutCount );
  3528. }
  3529. else
  3530. {
  3531. if ( nVolumeLeft <= CULLED_VOLUME )
  3532. {
  3533. SW_Mix16Mono_NoShift_OptMeta<FM_LEFT_ZERO>( pOutput, pVolume, pData, nOutCount );
  3534. }
  3535. else if ( nVolumeRight <= CULLED_VOLUME )
  3536. {
  3537. SW_Mix16Mono_NoShift_OptMeta<FM_RIGHT_ZERO>( pOutput, pVolume, pData, nOutCount );
  3538. }
  3539. else
  3540. {
  3541. SW_Mix16Mono_NoShift_OptMeta<FM_NORMAL>( pOutput, pVolume, pData, nOutCount );
  3542. }
  3543. }
  3544. }
  3545. void SW_Mix16Mono( portable_samplepair_t * RESTRICT pOutput, float * RESTRICT volume, short * RESTRICT pData, int inputOffset, fixedint rateScaleFix, int outCount )
  3546. {
  3547. if ( rateScaleFix == FIX(1) )
  3548. {
  3549. SW_Mix16Mono_NoShift( pOutput, volume, pData, outCount );
  3550. }
  3551. else
  3552. {
  3553. SW_Mix16Mono_Shift( pOutput, volume, pData, inputOffset, rateScaleFix, outCount );
  3554. }
  3555. }
  3556. void SW_Mix16Mono_Opt(portable_samplepair_t * RESTRICT pOutput, float * RESTRICT volume, short * RESTRICT pData, int inputOffset, fixedint rateScaleFix, int outCount)
  3557. {
  3558. if ( rateScaleFix == FIX(1) )
  3559. {
  3560. SW_Mix16Mono_NoShift_Opt( pOutput, volume, pData, outCount );
  3561. }
  3562. else
  3563. {
  3564. SW_Mix16Mono_Shift_Opt( pOutput, volume, pData, inputOffset, rateScaleFix, outCount );
  3565. }
  3566. }
  3567. // interpolating pitch shifter - sample(s) from preceding buffer are preloaded in
  3568. // pData buffer, ensuring we can always provide 'outCount' samples.
  3569. void SW_Mix16Mono_Interp(portable_samplepair_t * RESTRICT pOutput, float * RESTRICT volume, short * RESTRICT pData, int inputOffset, fixedint rateScaleFix, int outCount)
  3570. {
  3571. fixedint sampleIndex = 0;
  3572. fixedint rateScaleFix14 = FIX_28TO14(rateScaleFix); // convert 28 bit fixed point to 14 bit fixed point
  3573. fixedint sampleFrac14 = FIX_28TO14(inputOffset);
  3574. int first, second, interp;
  3575. for ( int i = 0; i < outCount; i++ )
  3576. {
  3577. first = (int)(pData[sampleIndex]);
  3578. second = (int)(pData[sampleIndex+1]);
  3579. interp = first + (((second - first) * (int)sampleFrac14) >> 14);
  3580. pOutput[i].left += int( (volume[0] * interp) / 256.0f);
  3581. pOutput[i].right += int( (volume[1] * interp) / 256.0f);
  3582. sampleFrac14 += rateScaleFix14;
  3583. sampleIndex += FIX_INTPART14(sampleFrac14);
  3584. sampleFrac14 = FIX_FRACPART14(sampleFrac14);
  3585. }
  3586. }
  3587. template <SW_FillMode MODE>
  3588. void SW_Mix16Mono_Interp_OptMeta( portable_samplepair_t * RESTRICT pOutput, float * RESTRICT pVolume, short * RESTRICT pData, int nInputOffset, fixedint nRateScaleFix, int nOutCount )
  3589. {
  3590. fixedint rateScaleFix14 = FIX_28TO14(nRateScaleFix); // convert 28 bit fixed point to 14 bit fixed point
  3591. fixedint sampleFrac14 = FIX_28TO14(nInputOffset);
  3592. int first, second, interp;
  3593. while ( nOutCount >= 4 )
  3594. {
  3595. first = (int)(pData[0]);
  3596. second = (int)(pData[1]);
  3597. interp = first + (((second - first) * (int)sampleFrac14) >> 14);
  3598. FillMonoOutput<MODE>( interp, pOutput, pVolume );
  3599. sampleFrac14 += rateScaleFix14;
  3600. pData += FIX_INTPART14(sampleFrac14);
  3601. sampleFrac14 = FIX_FRACPART14(sampleFrac14);
  3602. first = (int)(pData[0]);
  3603. second = (int)(pData[1]);
  3604. interp = first + (((second - first) * (int)sampleFrac14) >> 14);
  3605. FillMonoOutput<MODE>( interp, pOutput + 1, pVolume );
  3606. sampleFrac14 += rateScaleFix14;
  3607. pData += FIX_INTPART14(sampleFrac14);
  3608. sampleFrac14 = FIX_FRACPART14(sampleFrac14);
  3609. first = (int)(pData[0]);
  3610. second = (int)(pData[1]);
  3611. interp = first + (((second - first) * (int)sampleFrac14) >> 14);
  3612. FillMonoOutput<MODE>( interp, pOutput + 2, pVolume );
  3613. sampleFrac14 += rateScaleFix14;
  3614. pData += FIX_INTPART14(sampleFrac14);
  3615. sampleFrac14 = FIX_FRACPART14(sampleFrac14);
  3616. first = (int)(pData[0]);
  3617. second = (int)(pData[1]);
  3618. interp = first + (((second - first) * (int)sampleFrac14) >> 14);
  3619. FillMonoOutput<MODE>( interp, pOutput + 3, pVolume );
  3620. sampleFrac14 += rateScaleFix14;
  3621. pData += FIX_INTPART14(sampleFrac14);
  3622. sampleFrac14 = FIX_FRACPART14(sampleFrac14);
  3623. pOutput += 4;
  3624. nOutCount -= 4;
  3625. }
  3626. while ( nOutCount > 0 )
  3627. {
  3628. first = (int)(pData[0]);
  3629. second = (int)(pData[1]);
  3630. interp = first + (((second - first) * (int)sampleFrac14) >> 14);
  3631. FillMonoOutput<MODE>( interp, pOutput, pVolume );
  3632. sampleFrac14 += rateScaleFix14;
  3633. pData += FIX_INTPART14(sampleFrac14);
  3634. sampleFrac14 = FIX_FRACPART14(sampleFrac14);
  3635. ++pOutput;
  3636. --nOutCount;
  3637. }
  3638. }
  3639. void SW_Mix16Mono_Interp_Opt( portable_samplepair_t * RESTRICT pOutput, float * RESTRICT pVolume, short * RESTRICT pData, int nInputOffset, fixedint nRateScaleFix, int nOutCount )
  3640. {
  3641. // Besides unrolling, there are 2 other possible optimizations:
  3642. // In some cases both volumes are the same.
  3643. // In other cases, one of the volume is zero. (no case where both volumes are zero).
  3644. // Would doing one 32 bit load and one 64 bits write instead of 2 be better? (although the 32 bit load would be unaligned, so may not be possible).
  3645. // We "save" on the potential memory access, on the other hand we have to mask / shift, etc... to get the two members. (On PPC, it could save on the numbers of write that can be scheduled out of order).
  3646. // Except for the multiplication, there would be a potential to use integer VMX. It is not clear if that would be a real gain though as we would only do the calculation 2 samples at a time. :(
  3647. // There is also a potential for not always load 2 samples every time (can at least re-use a previous one) but I don't know how much this would save though.
  3648. // Would have to do a branch-less select and still load one regardless, may not be worth the effort.
  3649. int nVolumeLeft = pVolume[0];
  3650. int nVolumeRight = pVolume[1];
  3651. if ( nVolumeLeft == nVolumeRight )
  3652. {
  3653. SW_Mix16Mono_Interp_OptMeta<FM_SAME_VOL>( pOutput, pVolume, pData, nInputOffset, nRateScaleFix, nOutCount );
  3654. }
  3655. else
  3656. {
  3657. if ( nVolumeLeft <= CULLED_VOLUME )
  3658. {
  3659. SW_Mix16Mono_Interp_OptMeta<FM_LEFT_ZERO>( pOutput, pVolume, pData, nInputOffset, nRateScaleFix, nOutCount );
  3660. }
  3661. else if ( nVolumeRight <= CULLED_VOLUME )
  3662. {
  3663. SW_Mix16Mono_Interp_OptMeta<FM_RIGHT_ZERO>( pOutput, pVolume, pData, nInputOffset, nRateScaleFix, nOutCount );
  3664. }
  3665. else
  3666. {
  3667. SW_Mix16Mono_Interp_OptMeta<FM_NORMAL>( pOutput, pVolume, pData, nInputOffset, nRateScaleFix, nOutCount );
  3668. }
  3669. }
  3670. }
  3671. // Try to keep the number of parameters to 4 to make sure the optimizer is not doing something too stupid.
  3672. // Pass the volume by pointer instead of left and right values. It seems that the compiler has harder time optimizing with one more variable.
  3673. template <SW_FillMode MODE>
  3674. void FillStereoOutput(short * RESTRICT pInput, portable_samplepair_t * RESTRICT pOutput, float * RESTRICT pVolume);
  3675. template <>
  3676. FORCEINLINE
  3677. void FillStereoOutput<FM_SAME_VOL>(short * RESTRICT pInput, portable_samplepair_t * RESTRICT pOutput, float * RESTRICT pVolume)
  3678. {
  3679. int nVolume = pVolume[0];
  3680. pOutput->left += int((nVolume * (int)(pInput[0])) / 256.0f);
  3681. pOutput->right += int((nVolume * (int)(pInput[1])) / 256.0f);
  3682. }
  3683. template <>
  3684. FORCEINLINE
  3685. void FillStereoOutput<FM_LEFT_ZERO>(short * RESTRICT pInput, portable_samplepair_t * RESTRICT pOutput, float * RESTRICT pVolume)
  3686. {
  3687. pOutput->right += int((pVolume[1] * (int)(pInput[1])) / 256.0f);
  3688. }
  3689. template <>
  3690. FORCEINLINE
  3691. void FillStereoOutput<FM_RIGHT_ZERO>(short * RESTRICT pInput, portable_samplepair_t * RESTRICT pOutput, float * RESTRICT pVolume)
  3692. {
  3693. pOutput->left += int((pVolume[0] * (int)(pInput[0])) / 256.0f);
  3694. }
  3695. template <>
  3696. FORCEINLINE
  3697. void FillStereoOutput<FM_NORMAL>( short * RESTRICT pInput, portable_samplepair_t * RESTRICT pOutput, float * RESTRICT pVolume )
  3698. {
  3699. pOutput->left += int((pVolume[0] * (int)(pInput[0])) / 256.0f);
  3700. pOutput->right += int((pVolume[1] * (int)(pInput[1])) / 256.0f);
  3701. }
  3702. template <SW_FillMode MODE>
  3703. void SW_Mix16Stereo_NoShift_OptMeta( portable_samplepair_t * RESTRICT pOutput, float * RESTRICT pVolume, short * RESTRICT pData, int nOutCount )
  3704. {
  3705. while ( nOutCount >= 4 )
  3706. {
  3707. FillStereoOutput<MODE>( pData + 0, pOutput + 0, pVolume );
  3708. FillStereoOutput<MODE>( pData + 2, pOutput + 1, pVolume );
  3709. FillStereoOutput<MODE>( pData + 4, pOutput + 2, pVolume );
  3710. FillStereoOutput<MODE>( pData + 6, pOutput + 3, pVolume );
  3711. pOutput += 4;
  3712. pData += 8;
  3713. nOutCount -= 4;
  3714. }
  3715. while ( nOutCount > 0 )
  3716. {
  3717. FillStereoOutput<MODE>( pData, pOutput, pVolume );
  3718. ++pOutput;
  3719. pData += 2;
  3720. --nOutCount;
  3721. }
  3722. }
  3723. template <SW_FillMode MODE>
  3724. void SW_Mix16Stereo_Shift_OptMeta( portable_samplepair_t * RESTRICT pOutput, float * RESTRICT pVolume, short * RESTRICT pData, int nInputOffset, fixedint nRateScaleFix, int nOutCount )
  3725. {
  3726. fixedint nSampleFrac = nInputOffset;
  3727. while ( nOutCount >= 4 )
  3728. {
  3729. FillStereoOutput<MODE>( pData, pOutput, pVolume );
  3730. nSampleFrac += nRateScaleFix;
  3731. pData += FIX_INTPART(nSampleFrac)<<1;
  3732. nSampleFrac = FIX_FRACPART(nSampleFrac);
  3733. FillStereoOutput<MODE>( pData, pOutput + 1, pVolume );
  3734. nSampleFrac += nRateScaleFix;
  3735. pData += FIX_INTPART(nSampleFrac)<<1;
  3736. nSampleFrac = FIX_FRACPART(nSampleFrac);
  3737. FillStereoOutput<MODE>( pData, pOutput + 2, pVolume );
  3738. nSampleFrac += nRateScaleFix;
  3739. pData += FIX_INTPART(nSampleFrac)<<1;
  3740. nSampleFrac = FIX_FRACPART(nSampleFrac);
  3741. FillStereoOutput<MODE>( pData, pOutput + 3, pVolume );
  3742. nSampleFrac += nRateScaleFix;
  3743. pData += FIX_INTPART(nSampleFrac)<<1;
  3744. nSampleFrac = FIX_FRACPART(nSampleFrac);
  3745. pOutput += 4;
  3746. nOutCount -= 4;
  3747. }
  3748. while ( nOutCount > 0 )
  3749. {
  3750. FillStereoOutput<MODE>( pData, pOutput, pVolume );
  3751. nSampleFrac += nRateScaleFix;
  3752. pData += FIX_INTPART(nSampleFrac)<<1;
  3753. nSampleFrac = FIX_FRACPART(nSampleFrac);
  3754. ++pOutput;
  3755. --nOutCount;
  3756. }
  3757. }
  3758. void SW_Mix16Stereo_Opt( portable_samplepair_t * RESTRICT pOutput, float * RESTRICT pVolume, short * RESTRICT pData, int nInputOffset, fixedint nRateScaleFix, int nOutCount )
  3759. {
  3760. int nVolumeLeft = pVolume[0];
  3761. int nVolumeRight = pVolume[1];
  3762. if ( nRateScaleFix == FIX(1) )
  3763. {
  3764. if ( nVolumeLeft == nVolumeRight )
  3765. {
  3766. SW_Mix16Stereo_NoShift_OptMeta<FM_SAME_VOL>( pOutput, pVolume, pData, nOutCount );
  3767. }
  3768. else
  3769. {
  3770. if ( nVolumeLeft <= CULLED_VOLUME )
  3771. {
  3772. SW_Mix16Stereo_NoShift_OptMeta<FM_LEFT_ZERO>( pOutput, pVolume, pData, nOutCount );
  3773. }
  3774. else if ( nVolumeRight <= CULLED_VOLUME )
  3775. {
  3776. SW_Mix16Stereo_NoShift_OptMeta<FM_RIGHT_ZERO>( pOutput, pVolume, pData, nOutCount );
  3777. }
  3778. else
  3779. {
  3780. SW_Mix16Stereo_NoShift_OptMeta<FM_NORMAL>( pOutput, pVolume, pData, nOutCount );
  3781. }
  3782. }
  3783. }
  3784. else
  3785. {
  3786. if ( nVolumeLeft == nVolumeRight )
  3787. {
  3788. SW_Mix16Stereo_Shift_OptMeta<FM_SAME_VOL>( pOutput, pVolume, pData, nInputOffset, nRateScaleFix, nOutCount );
  3789. }
  3790. else
  3791. {
  3792. if ( nVolumeLeft <= CULLED_VOLUME )
  3793. {
  3794. SW_Mix16Stereo_Shift_OptMeta<FM_LEFT_ZERO>( pOutput, pVolume, pData, nInputOffset, nRateScaleFix, nOutCount );
  3795. }
  3796. else if ( nVolumeRight <= CULLED_VOLUME )
  3797. {
  3798. SW_Mix16Stereo_Shift_OptMeta<FM_RIGHT_ZERO>( pOutput, pVolume, pData, nInputOffset, nRateScaleFix, nOutCount );
  3799. }
  3800. else
  3801. {
  3802. SW_Mix16Stereo_Shift_OptMeta<FM_NORMAL>( pOutput, pVolume, pData, nInputOffset, nRateScaleFix, nOutCount );
  3803. }
  3804. }
  3805. }
  3806. }
  3807. void SW_Mix16Stereo_NoOpt( portable_samplepair_t * RESTRICT pOutput, float * RESTRICT pVolume, short * RESTRICT pData, int nInputOffset, fixedint nRateScaleFix, int nOutCount )
  3808. {
  3809. int nSampleIndex = 0;
  3810. fixedint nSampleFrac = nInputOffset;
  3811. for ( int i = 0; i < nOutCount; i++ )
  3812. {
  3813. pOutput[i].left += int( (pVolume[0] * (int)(pData[nSampleIndex])) / 256.0f);
  3814. pOutput[i].right += int( (pVolume[1] * (int)(pData[nSampleIndex+1])) / 256.0f);
  3815. nSampleFrac += nRateScaleFix;
  3816. nSampleIndex += FIX_INTPART(nSampleFrac)<<1;
  3817. nSampleFrac = FIX_FRACPART(nSampleFrac);
  3818. }
  3819. }
  3820. void SW_Mix16Stereo( portable_samplepair_t * RESTRICT pOutput, float * RESTRICT pVolume, short * RESTRICT pData, int nInputOffset, fixedint nRateScaleFix, int nOutCount )
  3821. {
  3822. #if CHECK_VALUES_AFTER_REFACTORING
  3823. // Backup the output and apply the same changes
  3824. portable_samplepair_t * pOldOutput = DuplicateSamplePairs( pOutput, nOutCount );
  3825. // Run the old code
  3826. SW_Mix16Stereo_NoOpt( pOldOutput, pVolume, pData, nInputOffset, nRateScaleFix, nOutCount );
  3827. #endif
  3828. if ( snd_mix_optimization.GetBool() )
  3829. {
  3830. SW_Mix16Stereo_Opt( pOutput, pVolume, pData, nInputOffset, nRateScaleFix, nOutCount );
  3831. }
  3832. else
  3833. {
  3834. SW_Mix16Stereo_NoOpt( pOutput, pVolume, pData, nInputOffset, nRateScaleFix, nOutCount );
  3835. }
  3836. #if CHECK_VALUES_AFTER_REFACTORING
  3837. // Compare side by side
  3838. bool bFailed = ( memcmp( pOutput, pOldOutput, nOutCount * sizeof( portable_samplepair_t ) ) != 0 );
  3839. Assert( bFailed == false );
  3840. FreeDuplicatedSamplePairs( pOldOutput, nOutCount );
  3841. #endif
  3842. }
  3843. // interpolating pitch shifter - sample(s) from preceding buffer are preloaded in
  3844. // pData buffer, ensuring we can always provide 'outCount' samples.
  3845. // The loop is already long, unrolling more is not going to help much.
  3846. void SW_Mix16Stereo_Interp( portable_samplepair_t * RESTRICT pOutput, float * RESTRICT pVolume, short * RESTRICT pData, int inputOffset, fixedint rateScaleFix, int outCount )
  3847. {
  3848. fixedint sampleIndex = 0;
  3849. fixedint rateScaleFix14 = FIX_28TO14(rateScaleFix); // convert 28 bit fixed point to 14 bit fixed point
  3850. fixedint sampleFrac14 = FIX_28TO14(inputOffset);
  3851. int first, second, interpl, interpr;
  3852. for ( int i = 0; i < outCount; i++ )
  3853. {
  3854. first = (int)(pData[sampleIndex]);
  3855. second = (int)(pData[sampleIndex+2]);
  3856. interpl = first + (((second - first) * (int)sampleFrac14) >> 14);
  3857. first = (int)(pData[sampleIndex+1]);
  3858. second = (int)(pData[sampleIndex+3]);
  3859. interpr = first + (((second - first) * (int)sampleFrac14) >> 14);
  3860. pOutput[i].left += int((pVolume[0] * interpl) / 256.0f);
  3861. pOutput[i].right += int((pVolume[1] * interpr) / 256.0f);
  3862. sampleFrac14 += rateScaleFix14;
  3863. sampleIndex += FIX_INTPART14(sampleFrac14)<<1;
  3864. sampleFrac14 = FIX_FRACPART14(sampleFrac14);
  3865. }
  3866. }
  3867. // return true if mixer should use high quality pitch interpolation for this sound
  3868. bool FUseHighQualityPitch( channel_t *pChannel )
  3869. {
  3870. // do not use interpolating pitch shifter if:
  3871. // low quality flag set on sound (ie: wave name is prepended with CHAR_FAST_PITCH)
  3872. // or pitch has no fractional part
  3873. // or snd_pitchquality is 0
  3874. if ( !snd_pitchquality.GetInt() || pChannel->flags.bfast_pitch )
  3875. return false;
  3876. return ( (pChannel->pitch != floor(pChannel->pitch)) );
  3877. }
  3878. //===============================================================================
  3879. // DISPATCHERS FOR MIXING ROUTINES
  3880. //===============================================================================
  3881. void Mix8MonoWavtype( channel_t *pChannel, portable_samplepair_t *pOutput, float *volume, byte *pData, int inputOffset, fixedint rateScaleFix, int outCount )
  3882. {
  3883. if ( FUseHighQualityPitch( pChannel ) )
  3884. SW_Mix8Mono_Interp( pOutput, volume, pData, inputOffset, rateScaleFix, outCount );
  3885. else
  3886. SW_Mix8Mono( pOutput, volume, pData, inputOffset, rateScaleFix, outCount );
  3887. }
  3888. void Mix16MonoWavtype( channel_t *pChannel, portable_samplepair_t *pOutput, float *volume, short *pData, int inputOffset, fixedint rateScaleFix, int outCount )
  3889. {
  3890. float fTotalVolume = volume[0] + volume[1];
  3891. if ( fTotalVolume <= SKIP_MIXING_IF_TOTAL_VOLUME_LESS_OR_EQUAL_THAN )
  3892. {
  3893. // Not enough volume to mix, skip it
  3894. return;
  3895. }
  3896. #if CHECK_VALUES_AFTER_REFACTORING
  3897. // Backup the output and apply the same changes
  3898. portable_samplepair_t * pOldOutput = DuplicateSamplePairs( pOutput, outCount );
  3899. // Run the old code
  3900. if ( FUseHighQualityPitch( pChannel ) )
  3901. SW_Mix16Mono_Interp( pOldOutput, volume, pData, inputOffset, rateScaleFix, outCount );
  3902. else
  3903. // fast native coded mixers with lower quality pitch shift
  3904. SW_Mix16Mono( pOldOutput, volume, pData, inputOffset, rateScaleFix, outCount );
  3905. #endif
  3906. // The optimized path has not been ported to PC, run the normal mode, except in debug to test the optimization process.
  3907. #if ( !IsPlatformWindowsPC() || defined(_DEBUG) )
  3908. if ( snd_mix_optimization.GetBool() )
  3909. #else
  3910. if ( false )
  3911. #endif
  3912. {
  3913. if ( FUseHighQualityPitch( pChannel ) )
  3914. SW_Mix16Mono_Interp_Opt( pOutput, volume, pData, inputOffset, rateScaleFix, outCount );
  3915. else
  3916. // fast native coded mixers with lower quality pitch shift
  3917. SW_Mix16Mono_Opt( pOutput, volume, pData, inputOffset, rateScaleFix, outCount );
  3918. }
  3919. else
  3920. {
  3921. if ( FUseHighQualityPitch( pChannel ) )
  3922. SW_Mix16Mono_Interp( pOutput, volume, pData, inputOffset, rateScaleFix, outCount );
  3923. else
  3924. // fast native coded mixers with lower quality pitch shift
  3925. SW_Mix16Mono( pOutput, volume, pData, inputOffset, rateScaleFix, outCount );
  3926. }
  3927. #if CHECK_VALUES_AFTER_REFACTORING
  3928. // Compare side by side
  3929. bool bFailed = ( memcmp( pOutput, pOldOutput, outCount * sizeof( portable_samplepair_t ) ) != 0 );
  3930. Assert( bFailed == false );
  3931. FreeDuplicatedSamplePairs( pOldOutput, outCount );
  3932. #endif
  3933. }
  3934. void Mix8StereoWavtype(channel_t *pChannel, portable_samplepair_t *pOutput, float *volume, byte *pData, int inputOffset, fixedint rateScaleFix, int outCount)
  3935. {
  3936. char nWavType = pChannel->wavtype;
  3937. if ( snd_mix_soundchar_enabled.GetBool() == false )
  3938. {
  3939. nWavType = 0; // Let's use the default value
  3940. }
  3941. switch ( nWavType )
  3942. {
  3943. case CHAR_DIRSTEREO:
  3944. case CHAR_DOPPLER:
  3945. SW_Mix8StereoDopplerLeft( pOutput, volume, pData, inputOffset, rateScaleFix, outCount );
  3946. SW_Mix8StereoDopplerRight( pOutput, &volume[IFRONT_LEFTD], pData, inputOffset, rateScaleFix, outCount );
  3947. break;
  3948. case CHAR_DIRECTIONAL:
  3949. if ( FUseHighQualityPitch( pChannel ) )
  3950. SW_Mix8StereoDirectional_Interp( pChannel->dspface, pOutput, volume, pData, inputOffset, rateScaleFix, outCount );
  3951. else
  3952. SW_Mix8StereoDirectional( pChannel->dspface, pOutput, volume, pData, inputOffset, rateScaleFix, outCount );
  3953. break;
  3954. case CHAR_DISTVARIANT:
  3955. if ( FUseHighQualityPitch( pChannel ) )
  3956. SW_Mix8StereoDistVar_Interp( pChannel->distmix, pOutput, volume, pData, inputOffset, rateScaleFix, outCount);
  3957. else
  3958. SW_Mix8StereoDistVar( pChannel->distmix, pOutput, volume, pData, inputOffset, rateScaleFix, outCount);
  3959. break;
  3960. case CHAR_OMNI:
  3961. // non directional stereo - all channel volumes are the same
  3962. if ( FUseHighQualityPitch( pChannel ) )
  3963. SW_Mix8Stereo_Interp( pOutput, volume, pData, inputOffset, rateScaleFix, outCount );
  3964. else
  3965. SW_Mix8Stereo( pOutput, volume, pData, inputOffset, rateScaleFix, outCount );
  3966. break;
  3967. default:
  3968. case CHAR_SPATIALSTEREO:
  3969. if ( FUseHighQualityPitch( pChannel ) )
  3970. SW_Mix8Stereo_Interp( pOutput, volume, pData, inputOffset, rateScaleFix, outCount );
  3971. else
  3972. SW_Mix8Stereo( pOutput, volume, pData, inputOffset, rateScaleFix, outCount );
  3973. break;
  3974. }
  3975. }
  3976. void Mix16StereoWavtype(channel_t *pChannel, portable_samplepair_t *pOutput, float *volume, short *pData, int inputOffset, fixedint rateScaleFix, int outCount)
  3977. {
  3978. float fTotalVolume = volume[0] + volume[1];
  3979. if ( fTotalVolume <= SKIP_MIXING_IF_TOTAL_VOLUME_LESS_OR_EQUAL_THAN )
  3980. {
  3981. // Not enough volume to mix, skip it
  3982. return;
  3983. }
  3984. bool bUseHighQualityPitch = FUseHighQualityPitch( pChannel );
  3985. char nWavType = pChannel->wavtype;
  3986. if ( snd_mix_soundchar_enabled.GetBool() == false )
  3987. {
  3988. nWavType = 0; // Let's use the default value
  3989. }
  3990. switch ( nWavType )
  3991. {
  3992. case CHAR_HRTF:
  3993. float volumes_averaged[2];
  3994. volumes_averaged[0] = float((volume[0] + volume[1]) * 4 * pChannel->hrtf.lerp + volume[0] * 8 * (1.0f - pChannel->hrtf.lerp));
  3995. volumes_averaged[1] = float((volume[0] + volume[1]) * 4 * pChannel->hrtf.lerp + volume[1] * 8 * (1.0f - pChannel->hrtf.lerp));
  3996. if (bUseHighQualityPitch)
  3997. SW_Mix16Stereo_Interp(pOutput, volumes_averaged, pData, inputOffset, rateScaleFix, outCount);
  3998. else
  3999. SW_Mix16Stereo(pOutput, volumes_averaged, pData, inputOffset, rateScaleFix, outCount);
  4000. break;
  4001. case CHAR_DIRSTEREO:
  4002. case CHAR_DOPPLER:
  4003. if ( bUseHighQualityPitch )
  4004. {
  4005. SW_Mix16StereoDopplerLeft_Interp( pOutput, volume, pData, inputOffset, rateScaleFix, outCount );
  4006. SW_Mix16StereoDopplerRight_Interp( pOutput, &volume[IFRONT_LEFTD], pData, inputOffset, rateScaleFix, outCount );
  4007. }
  4008. else
  4009. {
  4010. SW_Mix16StereoDopplerLeft( pOutput, volume, pData, inputOffset, rateScaleFix, outCount );
  4011. SW_Mix16StereoDopplerRight( pOutput, &volume[IFRONT_LEFTD], pData, inputOffset, rateScaleFix, outCount );
  4012. }
  4013. break;
  4014. case CHAR_DIRECTIONAL:
  4015. if ( bUseHighQualityPitch )
  4016. SW_Mix16StereoDirectional_Interp( pChannel->dspface, pOutput, volume, pData, inputOffset, rateScaleFix, outCount );
  4017. else
  4018. SW_Mix16StereoDirectional( pChannel->dspface, pOutput, volume, pData, inputOffset, rateScaleFix, outCount );
  4019. break;
  4020. case CHAR_DISTVARIANT:
  4021. if ( bUseHighQualityPitch )
  4022. SW_Mix16StereoDistVar_Interp( pChannel->distmix, pOutput, volume, pData, inputOffset, rateScaleFix, outCount);
  4023. else
  4024. SW_Mix16StereoDistVar( pChannel->distmix, pOutput, volume, pData, inputOffset, rateScaleFix, outCount);
  4025. break;
  4026. case CHAR_OMNI:
  4027. // non directional stereo - all channel volumes are same
  4028. if ( bUseHighQualityPitch )
  4029. SW_Mix16Stereo_Interp( pOutput, volume, pData, inputOffset, rateScaleFix, outCount );
  4030. else
  4031. SW_Mix16Stereo( pOutput, volume, pData, inputOffset, rateScaleFix, outCount );
  4032. break;
  4033. default:
  4034. case CHAR_SPATIALSTEREO:
  4035. if ( bUseHighQualityPitch )
  4036. SW_Mix16Stereo_Interp( pOutput, volume, pData, inputOffset, rateScaleFix, outCount );
  4037. else
  4038. SW_Mix16Stereo( pOutput, volume, pData, inputOffset, rateScaleFix, outCount );
  4039. break;
  4040. }
  4041. }
  4042. //===============================================================================
  4043. // Client entity mouth movement code. Set entity mouthopen variable, based
  4044. // on the sound envelope of the voice channel playing.
  4045. // KellyB 10/22/97
  4046. //===============================================================================
  4047. // called when voice channel is first opened on this entity
  4048. static CMouthInfo *GetMouthInfoForChannel( channel_t *pChannel )
  4049. {
  4050. int mouthentity = pChannel->speakerentity == -1 ? pChannel->soundsource : pChannel->speakerentity;
  4051. IClientEntity *pClientEntity = entitylist->GetClientEntity( mouthentity );
  4052. if( !pClientEntity )
  4053. return NULL;
  4054. return pClientEntity->GetMouth();
  4055. }
  4056. //-----------------------------------------------------------------------------
  4057. // Purpose:
  4058. // Input : *pChannel -
  4059. // Output : Returns true on success, false on failure.
  4060. //-----------------------------------------------------------------------------
  4061. static bool SND_IsMouth( channel_t *pChannel )
  4062. {
  4063. if ( !entitylist )
  4064. {
  4065. return false;
  4066. }
  4067. if ( pChannel->entchannel == CHAN_VOICE )
  4068. {
  4069. return true;
  4070. }
  4071. if ( pChannel->sfx &&
  4072. pChannel->sfx->pSource &&
  4073. pChannel->sfx->pSource->GetSentence() )
  4074. {
  4075. return true;
  4076. }
  4077. return false;
  4078. }
  4079. void SND_InitMouth( channel_t *pChannel )
  4080. {
  4081. if ( SND_IsMouth( pChannel ) )
  4082. {
  4083. CMouthInfo *pMouth = GetMouthInfoForChannel(pChannel);
  4084. // init mouth movement vars
  4085. if ( pMouth )
  4086. {
  4087. pMouth->mouthopen = 0;
  4088. pMouth->sndavg = 0;
  4089. pMouth->sndcount = 0;
  4090. pChannel->flags.m_bHasMouth = true;
  4091. pChannel->flags.m_bMouthEnvelope = pMouth->NeedsEnvelope();
  4092. if ( pChannel->sfx->pSource && pChannel->sfx->pSource->GetSentence() )
  4093. {
  4094. pMouth->AddSource( pChannel->sfx->pSource, pChannel->flags.m_bIgnorePhonemes );
  4095. }
  4096. }
  4097. }
  4098. }
  4099. // called when channel stops
  4100. // mouth updates are queued into these entries during mixing
  4101. // That way they can be applied during a time when the sound is synchronized with the client
  4102. // instead of mutexing the code inside the callbacks
  4103. struct mouthoutput_t
  4104. {
  4105. int entityId;
  4106. CAudioSource *pSource;
  4107. float elapsedTime; // if this is negative, we want to clear the mouth data
  4108. };
  4109. // mouth envelope data is queued here until it can be processed by the main thread
  4110. struct mouthenvelope_t
  4111. {
  4112. int entityId;
  4113. int sampleTotal;
  4114. int sampleCount;
  4115. };
  4116. // a couple of simple arrays for queuing the mouth data
  4117. static CUtlVector<mouthoutput_t> g_MouthOutput;
  4118. static CUtlVector<mouthenvelope_t> g_MouthEnvelope;
  4119. #define CAVGSAMPLES 10
  4120. // queue up a command to remove the channel's mouth source if playing
  4121. void SND_CloseMouth(channel_t *pChannel)
  4122. {
  4123. if ( pChannel->flags.m_bHasMouth )
  4124. {
  4125. int mouthentity = pChannel->speakerentity == -1 ? pChannel->soundsource : pChannel->speakerentity;
  4126. IClientEntity *pClientEntity = entitylist->GetClientEntity( mouthentity );
  4127. if ( pClientEntity )
  4128. {
  4129. CMouthInfo *pMouth = pClientEntity->GetMouth();
  4130. if ( pMouth )
  4131. {
  4132. int index = g_MouthOutput.AddToTail();
  4133. g_MouthOutput[index].entityId = mouthentity;
  4134. g_MouthOutput[index].pSource = pChannel->sfx->pSource;
  4135. g_MouthOutput[index].elapsedTime = -1;
  4136. }
  4137. }
  4138. }
  4139. }
  4140. // This processes all queued mouth updates
  4141. // Call this from the main thread to avoid callbacks while the client thread is running
  4142. void SND_MouthUpdateAll()
  4143. {
  4144. for ( int i = 0; i < g_MouthOutput.Count(); i++ )
  4145. {
  4146. const mouthoutput_t &rec = g_MouthOutput[i];
  4147. IClientEntity *pClientEntity = entitylist->GetClientEntity( rec.entityId );
  4148. if( !pClientEntity )
  4149. continue;
  4150. CMouthInfo *pMouth = pClientEntity->GetMouth();
  4151. if ( !pMouth )
  4152. continue;
  4153. Assert(rec.pSource);
  4154. if ( rec.elapsedTime < 0 )
  4155. {
  4156. pMouth->RemoveSource( rec.pSource );
  4157. pMouth->mouthopen = 0;
  4158. continue;
  4159. }
  4160. int idx = pMouth->GetIndexForSource( rec.pSource );
  4161. CVoiceData *vd = NULL;
  4162. if ( idx == UNKNOWN_VOICE_SOURCE )
  4163. {
  4164. vd = pMouth->AddSource( rec.pSource, false );
  4165. if ( vd == NULL )
  4166. {
  4167. // clear, any sources still playing will re-add themselves within a frame
  4168. pMouth->ClearVoiceSources();
  4169. char nameBuf[MAX_PATH];
  4170. DevMsg( 2, "out of voice sources, won't lipsync %s\n", rec.pSource->GetFileName(nameBuf, sizeof(nameBuf)) );
  4171. #if 0
  4172. for ( int i = 0; i < pMouth->GetNumVoiceSources(); i++ )
  4173. {
  4174. CVoiceData *pVoice = pMouth->GetVoiceSource(i);
  4175. CAudioSourceWave *pWave = dynamic_cast<CAudioSourceWave *>(pVoice->GetSource());
  4176. const char *pName = "unknown";
  4177. if ( pWave && pWave->GetName() )
  4178. pName = pWave->GetName();
  4179. Msg("Playing %s...\n", pName );
  4180. }
  4181. #endif
  4182. // try again to add after clearing
  4183. vd = pMouth->AddSource( rec.pSource, false );
  4184. }
  4185. }
  4186. else
  4187. {
  4188. vd = pMouth->GetVoiceSource(idx);
  4189. }
  4190. if ( vd )
  4191. {
  4192. // Update elapsed time from mixer
  4193. vd->SetElapsedTime( rec.elapsedTime );
  4194. }
  4195. }
  4196. g_MouthOutput.RemoveAll();
  4197. for ( int i = 0; i < g_MouthEnvelope.Count(); i++ )
  4198. {
  4199. const mouthenvelope_t &rec = g_MouthEnvelope[i];
  4200. IClientEntity *pClientEntity = entitylist->GetClientEntity( rec.entityId );
  4201. if( !pClientEntity )
  4202. continue;
  4203. CMouthInfo *pMouth = pClientEntity->GetMouth();
  4204. if ( !pMouth )
  4205. continue;
  4206. if ( pMouth->NeedsEnvelope() )
  4207. {
  4208. pMouth->sndavg = rec.sampleTotal + pMouth->sndavg;
  4209. int count = rec.sampleCount + pMouth->sndcount;
  4210. if ( count >= CAVGSAMPLES )
  4211. {
  4212. pMouth->mouthopen = pMouth->sndavg / count;
  4213. pMouth->sndavg = 0;
  4214. pMouth->sndcount = 0;
  4215. }
  4216. else
  4217. {
  4218. pMouth->sndcount = count;
  4219. }
  4220. }
  4221. else
  4222. {
  4223. pMouth->mouthopen = 0;
  4224. }
  4225. }
  4226. g_MouthEnvelope.RemoveAll();
  4227. }
  4228. // need this to make the debug code below work.
  4229. //#include "snd_wave_source.h"
  4230. // this will queue up a command to update the client-entity's mouth data
  4231. void SND_MoveMouth8( channel_t *ch, CAudioSource *pSource, int count )
  4232. {
  4233. if ( !ch->flags.m_bHasMouth )
  4234. return;
  4235. int mouthentity = ch->speakerentity == -1 ? ch->soundsource : ch->speakerentity;
  4236. if ( !ch->flags.m_bIgnorePhonemes )
  4237. {
  4238. if ( pSource->GetSentence() )
  4239. {
  4240. int index = g_MouthOutput.AddToTail();
  4241. g_MouthOutput[index].entityId = mouthentity;
  4242. g_MouthOutput[index].pSource = pSource;
  4243. Assert( pSource->SampleRate() > 0 );
  4244. float elapsed = ( float )ch->pMixer->GetSamplePosition() / ( float )pSource->SampleRate();
  4245. g_MouthOutput[index].elapsedTime = elapsed;
  4246. }
  4247. }
  4248. }
  4249. void SND_MouthEnvelopeFollower( channel_t *pChannel, char *pData, int count )
  4250. {
  4251. if ( !pChannel->flags.m_bHasMouth )
  4252. return;
  4253. if ( !pChannel->flags.m_bMouthEnvelope )
  4254. return;
  4255. if ( pData == NULL || count == 0 )
  4256. return;
  4257. int mouthentity = pChannel->speakerentity == -1 ? pChannel->soundsource : pChannel->speakerentity;
  4258. int mix_sample_size = pChannel->pMixer->GetMixSampleSize();
  4259. int i = 0;
  4260. int scount = 0;
  4261. int savg = 0;
  4262. int sample = 0;
  4263. while ( i < count && scount < CAVGSAMPLES )
  4264. {
  4265. if ( mix_sample_size == 1 )
  4266. {
  4267. sample = *(((char *)pData) + i );
  4268. }
  4269. else if ( mix_sample_size == 2 )
  4270. {
  4271. sample = *(((short *)pData) + i ) >> 8;
  4272. }
  4273. savg += abs(sample);
  4274. // skip ahead pseudo randomly
  4275. i += 80 + ((byte)sample & 0x1F);
  4276. scount++;
  4277. }
  4278. int index = g_MouthEnvelope.AddToTail();
  4279. g_MouthEnvelope[index].entityId = mouthentity;
  4280. g_MouthEnvelope[index].sampleTotal = savg;
  4281. g_MouthEnvelope[index].sampleCount = scount;
  4282. }
  4283. // note: since mixing may be threaded these calls are all queued now
  4284. // queue up a command to clear the current source out of the mouth for this entity
  4285. void SND_ClearMouth( channel_t *pChannel )
  4286. {
  4287. if ( pChannel->flags.m_bHasMouth && pChannel->sfx )
  4288. {
  4289. int mouthentity = pChannel->speakerentity == -1 ? pChannel->soundsource : pChannel->speakerentity;
  4290. int index = g_MouthOutput.AddToTail();
  4291. g_MouthOutput[index].entityId = mouthentity;
  4292. g_MouthOutput[index].pSource = pChannel->sfx->pSource;
  4293. g_MouthOutput[index].elapsedTime = -1;
  4294. }
  4295. }
  4296. //-----------------------------------------------------------------------------
  4297. // Purpose:
  4298. // Input : *pChannel -
  4299. // Output : Returns true on success, false on failure.
  4300. //-----------------------------------------------------------------------------
  4301. bool SND_ShouldPause( channel_t *pChannel )
  4302. {
  4303. return pChannel->flags.m_bShouldPause;
  4304. }
  4305. //===============================================================================
  4306. // Movie recording support
  4307. //===============================================================================
  4308. extern float host_time;
  4309. extern double g_soundtimeerror;
  4310. static int g_nMovieSamples = 0;
  4311. extern int host_tickcount;
  4312. // We don't want to record sound until the tick after we start the movie
  4313. static int g_nMovieStartTick;
  4314. static ConVar snd_moviefix( "snd_moviefix", "1", 0, "Defer sound recording until next tick when laying off movies." );
  4315. float g_moviestart;
  4316. void SND_MovieStart( void )
  4317. {
  4318. if ( IsGameConsole() )
  4319. return;
  4320. if ( !cl_movieinfo.IsRecording() )
  4321. return;
  4322. g_paintedtime = 0;
  4323. #if USE_AUDIO_DEVICE_V1
  4324. g_soundtime = 0;
  4325. g_soundtimeerror = 0.0;
  4326. #endif
  4327. g_moviestart = host_time;
  4328. g_nMovieStartTick = host_tickcount;
  4329. // TMP Wave file supports stereo only, so force stereo
  4330. if ( snd_surround.GetInt() != 2 )
  4331. {
  4332. snd_surround.SetValue( 2 );
  4333. }
  4334. // 44k: engine playback rate is now 44100...changed from 22050
  4335. if ( cl_movieinfo.DoWav() )
  4336. {
  4337. WaveCreateTmpFile( cl_movieinfo.moviename, SOUND_DMA_SPEED, 16, 2 );
  4338. }
  4339. }
  4340. void SND_MovieEnd( void )
  4341. {
  4342. if ( IsGameConsole() )
  4343. return;
  4344. if ( !cl_movieinfo.IsRecording() )
  4345. {
  4346. return;
  4347. }
  4348. if ( cl_movieinfo.DoWav() )
  4349. {
  4350. WaveFixupTmpFile( cl_movieinfo.moviename );
  4351. }
  4352. }
  4353. bool SND_IsRecording()
  4354. {
  4355. if ( cl_movieinfo.IsRecording() && !Con_IsVisible() )
  4356. {
  4357. // Defer first buffer until next tick if snd_moviefix is true
  4358. if ( ( host_tickcount == g_nMovieStartTick ) &&
  4359. snd_moviefix.GetBool() )
  4360. {
  4361. return false;
  4362. }
  4363. return true;
  4364. }
  4365. return false;
  4366. }
  4367. void SND_RecordBuffer( void )
  4368. {
  4369. if ( IsGameConsole() )
  4370. return;
  4371. if ( !SND_IsRecording() )
  4372. return;
  4373. int i;
  4374. int val;
  4375. int bufferSize = snd_linear_count * sizeof(short);
  4376. short *tmp = (short *)stackalloc( bufferSize );
  4377. for (i=0 ; i<snd_linear_count ; i+=2)
  4378. {
  4379. val = (snd_p[i]*snd_vol)>>8;
  4380. tmp[i] = iclip(val);
  4381. val = (snd_p[i+1]*snd_vol)>>8;
  4382. tmp[i+1] = iclip(val);
  4383. }
  4384. if ( cl_movieinfo.DoWav() )
  4385. {
  4386. WaveAppendTmpFile( cl_movieinfo.moviename, tmp, 16, snd_linear_count );
  4387. }
  4388. if ( cl_movieinfo.DoAVISound() )
  4389. {
  4390. g_pAVI->AppendMovieSound( g_hCurrentAVI, tmp, bufferSize );
  4391. }
  4392. g_nMovieSamples += ( snd_linear_count >> 1 );
  4393. //Msg( "%d %f %f sound file time %f\n", host_tickcount, host_time, host_time - g_moviestart, (double)g_nMovieSamples/(double)44100);
  4394. }