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.

2022 lines
69 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: X360 XAudio Version
  4. //
  5. //=====================================================================================//
  6. #include "audio_pch.h"
  7. #include "snd_dev_xaudio.h"
  8. #include "utllinkedlist.h"
  9. #include "server.h"
  10. #include "client.h"
  11. #include "matchmaking/imatchframework.h"
  12. #include "tier2/tier2.h"
  13. #include "vjobs/sndupsampler_shared.h"
  14. // from PS3
  15. #include <sysutil/sysutil_sysparam.h>
  16. #include <cell/audio.h>
  17. #include <sys/event.h>
  18. #include <cell/sysmodule.h>
  19. #include <cell/mic.h>
  20. #include <cell/voice.h>
  21. #include <vjobs_interface.h>
  22. #include <vjobs/root.h>
  23. #include <ps3/vjobutils.h>
  24. #include <vjobs/sndupsampler_shared.h>
  25. #include <ps3/vjobutils_shared.h>
  26. #include "engine/IPS3FrontPanelLED.h"
  27. #include <tier0/microprofiler.h>
  28. // memdbgon must be the last include file in a .cpp file!!!
  29. #include "tier0/memdbgon.h"
  30. #include "sys/tty.h"
  31. // The outer code mixes in PAINTBUFFER_SIZE (# of samples) chunks (see MIX_PaintChannels), we will never need more than
  32. // that many samples in a buffer. This ends up being about 20ms per buffer
  33. #define XAUDIO2_BUFFER_SAMPLES PAINTBUFFER_SIZE
  34. #define ENABLE_SOUND_DEBUG 0
  35. #define SOUND_PC_CELP_SUPPORTED
  36. extern IVEngineClient *engineClient;
  37. extern IVJobs * g_pVJobs;
  38. ConVar snd_ps3_back_channel_multiplier( "snd_ps3_back_channel_multiplier", "0" );
  39. //-----------------------------------------------------------------------------
  40. // An ugly workaround: our sound mixing library outputs at 44.1khz; PS3 inputs
  41. // at 48khz. We'll probably need to replace the mixer wholesale, but for now,
  42. // add an upsampling stage to the pipeline.
  43. //-----------------------------------------------------------------------------
  44. class CAudioIckyUpsampler // a non-virtual class
  45. {
  46. public:
  47. // local constants
  48. enum ConstEnum_t
  49. {
  50. INPUTFREQ = job_sndupsampler::INPUTFREQ,
  51. OUTPUTFREQ = job_sndupsampler::OUTPUTFREQ,
  52. BUFFERSIZE = job_sndupsampler::BUFFERSIZE, // < input samples, should be a power of two
  53. };
  54. CThreadMutex m_mutex;
  55. CAudioIckyUpsampler();
  56. ~CAudioIckyUpsampler(); // NON-VIRTUAL .. mark virtual if you inherit from here
  57. // initialize to stereo or surround. Can be called again to reinitialize.
  58. void Init( bool surround );
  59. // take in some number of input samples. Returns the number actually consumed (the buffer might be full)
  60. int IngestStereo( portable_samplepair_t *pInputSamples, int numSamples ) RESTRICT;
  61. int IngestSurround( portable_samplepair_t *pInputStereo, portable_samplepair_t *pInputSurround, portable_samplepair_t *pInputCenter, int numSamples ) RESTRICT;
  62. // upsample and emit the left and right channels into an interleaved float stream.
  63. // scalar gets multiplied onto each sample as it's written (ie to rescale -32767..32767 -> -1..1
  64. // returns number of samples actually emitted.
  65. // assumes PS3 interleaving (
  66. /*
  67. // you specify a pointer to the left channel, an offset (in words) between the left and right
  68. // channel, and the stride between successive samples. Returns the number of samples actually
  69. // emitted.
  70. // scalar gets multiplied onto each sample as it's written (ie to rescale -32767..32767 -> -1..1
  71. int Excrete( uint nSamplesRequested, float *pOutSamples, int nLeftToRightOffset, int nstride, float scalar );
  72. */
  73. // We output stereo in a surround channel.
  74. int ExcreteStereo( uint nSamplesRequested, libaudio_sample_surround_t *pOutSamples, float scalar ) RESTRICT;
  75. int ExcreteSurround( uint nSamplesRequested, libaudio_sample_surround_t *pOutSamples, float scalar ) RESTRICT;
  76. /// dump all data
  77. void Flush();
  78. // the number of INPUT samples already in the buffer
  79. inline int Count();
  80. inline int SpaceRemaining() { return BUFFERSIZE - Count(); }
  81. int OutputSamplesAvailable();
  82. inline bool IsSurround() { return m_bSurround; }
  83. public:
  84. /// ring buffers: one interleaved pair for stereo, or five channels for surround.
  85. /// I compress them back to signed shorts to conserve memory bandwidth.
  86. typedef job_sndupsampler::stereosample_t stereosample_t;
  87. typedef job_sndupsampler::surroundsample_t surroundsample_t;
  88. union // the pointer to the input buffer of samples at 44k
  89. {
  90. stereosample_t *m_p44kStereoIn;
  91. surroundsample_t *m_p44kSurroundIn;
  92. uintp m_ea44kSamplesIn;
  93. };
  94. // size in bytes of an input sample for the current channel mode
  95. inline int InputSampleSizeof( ) { return m_bSurround ? sizeof(surroundsample_t) : sizeof(stereosample_t); }
  96. CInterlockedInt m_ringHead; ///< "head" of the buffer, the first sample Excrete() will look at
  97. CInterlockedInt m_ringCount; ///< number of samples in the buffer
  98. float m_fractionalSamples; ///< we will have some fractional number of samples still waiting to be sent. ie 0.2f means we still have temporally 80% of the HEAD sample to emit.
  99. bool m_bSurround; // set to surround sound mode
  100. };
  101. CAudioIckyUpsampler::CAudioIckyUpsampler()
  102. : m_ringHead( 0 )
  103. , m_ringCount( 0 )
  104. , m_fractionalSamples( 0 )
  105. , m_p44kStereoIn( NULL )
  106. , m_bSurround( false )
  107. {
  108. }
  109. CAudioIckyUpsampler::~CAudioIckyUpsampler()
  110. {
  111. if ( IsSurround() )
  112. MemAlloc_FreeAligned( m_p44kSurroundIn );
  113. else
  114. MemAlloc_FreeAligned( m_p44kStereoIn );
  115. m_p44kStereoIn = NULL;
  116. }
  117. void CAudioIckyUpsampler::Init( bool surround )
  118. {
  119. // there's a buffer already, toss it. (in certain cases could just recycle it, but it's unlikely that this is ever actually called twice)
  120. if ( m_p44kStereoIn != NULL )
  121. {
  122. MemAlloc_FreeAligned( m_p44kStereoIn );
  123. m_p44kStereoIn = NULL;
  124. }
  125. // Note : we need to allocate aligned memory so that when DMA'ing ring buffer that wraps around,
  126. // there's no hole at the wrap point (so the end and start of the buffer must be aligned
  127. if ( m_bSurround = surround )
  128. { // we are in surround mode
  129. m_p44kSurroundIn = ( surroundsample_t* ) MemAlloc_AllocAligned( sizeof( surroundsample_t ) * BUFFERSIZE, 16 );
  130. memset( m_p44kSurroundIn, 0, sizeof(surroundsample_t)*BUFFERSIZE );
  131. }
  132. else
  133. { // stereo mode
  134. m_p44kStereoIn = ( stereosample_t* )MemAlloc_AllocAligned( sizeof( stereosample_t ) * BUFFERSIZE, 16 );
  135. memset( m_p44kStereoIn, 0, sizeof(stereosample_t)*BUFFERSIZE );
  136. }
  137. }
  138. void CAudioIckyUpsampler::Flush()
  139. {
  140. AUTO_LOCK( m_mutex ); // TODO: get rid of this lock, use head-tail counters instead of head+count
  141. m_fractionalSamples = 0;
  142. m_ringHead = m_ringCount = 0;
  143. }
  144. inline int CAudioIckyUpsampler::Count()
  145. {
  146. return m_ringCount;
  147. }
  148. int CAudioIckyUpsampler::IngestStereo( portable_samplepair_t * RESTRICT pInputSamples, int numSamples ) RESTRICT
  149. {
  150. if ( IsSurround() )
  151. {
  152. Error( "Audio: Tried to ingest stereo data into a surround upsampler!\n" );
  153. }
  154. const int nSamplesToConsume = MIN( numSamples, SpaceRemaining() );
  155. int i,o;
  156. for ( i = 0, o = (m_ringHead + m_ringCount)%BUFFERSIZE ;
  157. i < nSamplesToConsume ;
  158. ++i, o = (o + 1)%BUFFERSIZE ) // power-of-two mods become ands
  159. {
  160. m_p44kStereoIn[ o ].left = pInputSamples[i].left;
  161. m_p44kStereoIn[ o ].right = pInputSamples[i].right;
  162. }
  163. m_ringCount += nSamplesToConsume;
  164. return nSamplesToConsume;
  165. // ------------------------------------------
  166. // PURELY FOR EXAMPLE: a basic sine wave test
  167. if ( false ) {
  168. int q = 0 ;
  169. while ( q++ < numSamples && m_ringCount < BUFFERSIZE )
  170. {
  171. int i = (m_ringHead + m_ringCount)%BUFFERSIZE;
  172. float f = sin( ( i & 0x100 ? (2.0/256.0) : (2.0/128.0)) * M_PI * i ) * 20000;
  173. m_p44kStereoIn[i].left = f;
  174. m_p44kStereoIn[i].right = f;
  175. ++m_ringCount;
  176. }
  177. return numSamples;
  178. }
  179. }
  180. int CAudioIckyUpsampler::IngestSurround( portable_samplepair_t * RESTRICT pInputStereo, portable_samplepair_t * RESTRICT pInputSurround, portable_samplepair_t * RESTRICT pInputCenterAsPairs, int numSamples ) RESTRICT
  181. {
  182. if ( !IsSurround() )
  183. {
  184. Error( "Audio: Tried to ingest stereo data into a surround upsampler!\n" );
  185. }
  186. const int nSamplesToConsume = MIN( numSamples, SpaceRemaining() );
  187. int i,o;
  188. for ( i = 0, o = (m_ringHead + m_ringCount)%BUFFERSIZE ;
  189. i < nSamplesToConsume ;
  190. ++i, o = (o + 1)%BUFFERSIZE ) // power-of-two mods become ands
  191. {
  192. // hopefully the compiler knows how to pipeline these intelligently ( should be >1 IPC )
  193. m_p44kSurroundIn[ o ].left = pInputStereo[i].left;
  194. m_p44kSurroundIn[ o ].right = pInputStereo[i].right;
  195. m_p44kSurroundIn[ o ].center = pInputCenterAsPairs[i].left; // Center is stored as left / right, but only left contains the necessary information
  196. m_p44kSurroundIn[ o ].surleft = pInputSurround[i].left;
  197. m_p44kSurroundIn[ o ].surright = pInputSurround[i].right;
  198. }
  199. m_ringCount += nSamplesToConsume;
  200. return nSamplesToConsume;
  201. }
  202. #pragma message("TODO: obvious optimization opportunities")
  203. // TODO: move to SPU -- many obvious optimization opportunities
  204. float g_flSumStereo;
  205. int CAudioIckyUpsampler::ExcreteStereo( uint nSamplesRequested, libaudio_sample_surround_t * RESTRICT pOutSamples, float scalar ) RESTRICT
  206. {
  207. Assert( !IsSurround() );
  208. // do a braindead linear interpolation
  209. float t = m_fractionalSamples;
  210. float tstep = ((float)INPUTFREQ)/((float) OUTPUTFREQ);
  211. const int nSamplesToEmit = MIN( OutputSamplesAvailable(), nSamplesRequested );
  212. int curSamp = m_ringHead;
  213. int nextSamp = (curSamp + 1) & (BUFFERSIZE - 1);
  214. int n44kSamplesConsumed = 0;
  215. // iterator
  216. for ( int iRemain = nSamplesToEmit ;
  217. iRemain > 0 ;
  218. t += tstep, --iRemain )
  219. {
  220. // if we've passed a sample boundary, go onto the next sample
  221. if ( t >= 1.0f ) // ugh, fcmp, it's just a hacky prototype
  222. {
  223. t -= 1.0f;
  224. curSamp = nextSamp;
  225. nextSamp = (curSamp + 1) & (BUFFERSIZE - 1);
  226. ++n44kSamplesConsumed;
  227. }
  228. pOutSamples->left = Lerp( t, m_p44kStereoIn[curSamp].left, m_p44kStereoIn[nextSamp].left ) * scalar; // left
  229. pOutSamples->right = Lerp( t, m_p44kStereoIn[curSamp].right, m_p44kStereoIn[nextSamp].right ) * scalar; // right
  230. // Fill the remaining with blank
  231. pOutSamples->center = 0;
  232. pOutSamples->subwoofer = 0;
  233. pOutSamples->leftsurround = 0;
  234. pOutSamples->rightsurround = 0;
  235. pOutSamples->leftextend = 0;
  236. pOutSamples->rightextend = 0;
  237. g_flSumStereo += pOutSamples->left * pOutSamples->left + pOutSamples->right * pOutSamples->right;
  238. pOutSamples++;
  239. }
  240. // Make sure to update correctly the variables after the last sample (after all, we did t += tstep).
  241. if ( t >= 1.0f )
  242. {
  243. t -= 1.0f;
  244. curSamp = nextSamp;
  245. ++n44kSamplesConsumed;
  246. }
  247. m_fractionalSamples = t;
  248. m_ringHead = curSamp;
  249. m_ringCount -= n44kSamplesConsumed;
  250. return nSamplesToEmit;
  251. }
  252. int CAudioIckyUpsampler::ExcreteSurround( uint nSamplesRequested, libaudio_sample_surround_t *pOutSamples, float scalar ) RESTRICT
  253. {
  254. Assert( IsSurround() );
  255. // do a braindead linear interpolation
  256. float t = m_fractionalSamples;
  257. float tstep = ((float)INPUTFREQ)/((float) OUTPUTFREQ);
  258. const int nSamplesToEmit = MIN( OutputSamplesAvailable(), nSamplesRequested );
  259. int curSamp = m_ringHead;
  260. int nextSamp = (curSamp + 1) & (BUFFERSIZE - 1);
  261. int n44kSamplesConsumed = 0;
  262. const float flBackChannelMultipler = snd_ps3_back_channel_multiplier.GetFloat();
  263. // iterator
  264. for ( int iRemain = nSamplesToEmit ;
  265. iRemain > 0 ;
  266. t += tstep, --iRemain )
  267. {
  268. // if we've passed a sample boundary, go onto the next sample
  269. if ( t >= 1.0f ) // ugh, fcmp, it's just a hacky prototype
  270. {
  271. t -= 1.0f;
  272. curSamp = nextSamp;
  273. nextSamp = (curSamp + 1) & (BUFFERSIZE - 1);
  274. ++n44kSamplesConsumed;
  275. }
  276. pOutSamples->left = Lerp( t, m_p44kSurroundIn[curSamp].left, m_p44kSurroundIn[nextSamp].left ) * scalar; // left
  277. pOutSamples->right = Lerp( t, m_p44kSurroundIn[curSamp].right, m_p44kSurroundIn[nextSamp].right ) * scalar; // right
  278. pOutSamples->center = Lerp( t, m_p44kSurroundIn[curSamp].center, m_p44kSurroundIn[nextSamp].center ) * scalar; // left
  279. pOutSamples->subwoofer = 0; // As on X360, let the HW mix the sub from the main channel since we don't have any sub-specific sounds, or direct sub-addressing
  280. float fLeftSurround = Lerp( t, m_p44kSurroundIn[curSamp].surleft, m_p44kSurroundIn[nextSamp].surleft ) * scalar; // left
  281. float fRightSurround = Lerp( t, m_p44kSurroundIn[curSamp].surright, m_p44kSurroundIn[nextSamp].surright ) * scalar; // right
  282. pOutSamples->leftsurround = fLeftSurround;
  283. pOutSamples->rightsurround = fRightSurround;
  284. pOutSamples->leftextend = fLeftSurround * flBackChannelMultipler;
  285. pOutSamples->rightextend = fRightSurround * flBackChannelMultipler;
  286. pOutSamples++;
  287. }
  288. // Make sure to update correctly the variables after the last sample (after all, we did t += tstep).
  289. if ( t >= 1.0f )
  290. {
  291. t -= 1.0f;
  292. curSamp = nextSamp;
  293. ++n44kSamplesConsumed;
  294. }
  295. m_fractionalSamples = t;
  296. m_ringHead = curSamp;
  297. m_ringCount -= n44kSamplesConsumed;
  298. return nSamplesToEmit;
  299. }
  300. int CAudioIckyUpsampler::OutputSamplesAvailable()
  301. {
  302. // returns the number of output samples we can generate (notice this rounds down, ie we may leave some fractional samples for next time)
  303. return ( ( m_ringCount - 1 ) * OUTPUTFREQ) / INPUTFREQ;
  304. }
  305. //-----------------------------------------------------------------------------
  306. // Implementation of an audio device on top of PS3's libaudio
  307. //-----------------------------------------------------------------------------
  308. class CAudioPS3LibAudio: public CAudioDeviceBase, public VJobInstance
  309. {
  310. public:
  311. ~CAudioPS3LibAudio( void );
  312. CAudioPS3LibAudio( void );
  313. bool IsActive( void ) { return true; }
  314. bool Init( void ) ;
  315. void Shutdown( void );
  316. void Pause( void );
  317. void UnPause( void );
  318. int64 PaintBegin( float mixAheadTime, int64 soundtime, int64 paintedtime );
  319. void PaintEnd( void );
  320. int GetOutputPosition( void );
  321. void ClearBuffer( void );
  322. void TransferSamples( int64 end ) ;
  323. virtual const char *DeviceName( void );
  324. virtual int DeviceChannels( void );
  325. virtual int DeviceSampleBits( void );
  326. virtual int DeviceSampleBytes( void );
  327. virtual int DeviceDmaSpeed( void );
  328. virtual int DeviceSampleCount( void );
  329. CEngineVoicePs3 *GetVoiceData( void ) { return &m_VoiceData; }
  330. static CAudioPS3LibAudio *m_pSingleton;
  331. static void QuerySystemAudioConfiguration( bool bForceRefreshCache = false ); // retrieves information from the OS about what output devices are connected. Caches result.
  332. virtual void OnVjobsInit(){ s_LibAudioThread.OnVjobsInit( m_pRoot ); }
  333. virtual void OnVjobsShutdown() { s_LibAudioThread.OnVjobsShutdown(); }
  334. protected:
  335. inline float *GetAddressForBlockIdx( unsigned nBlock );
  336. inline uint GetPortBufferSize()const;
  337. inline float *GetNextReadBlockAddress();
  338. int UpdateBufferCount( void ); // update the count of how many filled buffers remain to be read. return m_nFilledBuffers to avoid a LHS
  339. #if 0 // These functions were deprecated by the move to a DMA thread:
  340. // blit the audio data to the hardware. returns the number of samples written.
  341. int TransferStereo( const portable_samplepair_t *pFrontBuffer, int nSamplesToWrite, int nStartBlock );
  342. int TransferSurroundInterleaved( const portable_samplepair_t *pFrontBuffer, const portable_samplepair_t *pRearBuffer, const int *pCenterBuffer,
  343. int nSamplesToWrite, int nStartBlock );
  344. #endif
  345. int m_deviceChannels; // channels per ring port ( either 2 for stereo or 8 for surround -- PS3's libaudio has no concept of 5.1 )
  346. // information from the OS.
  347. // right now this is all a little redundant because we are only using the "primary" sound device,
  348. // which always has exactly one logical device even if there are multiple things attached. But it
  349. // will be significant if we eventually create another class to handle "secondary" outputs, in which
  350. // case the members below should not be static, and QuerySystemAudioConfiguration() will need to take
  351. // a flag specifying CELL_AUDIO_OUT_PRIMARY or CELL_AUDIO_OUT_SECONDARY. You'll also need to
  352. // touch the init/shutdown functions so that you only intialize the OS's audio lib once.
  353. static bool s_bCellAudioStateInitialized;
  354. static CUtlVectorConservative<CellAudioOutState> s_vCellAudioOutputDeviceStates;
  355. uint32_t m_nPortNum; // the "port number" given us by Cell
  356. sys_addr_t m_pPortBufferAddr; // base address of the audio port's ring buffer
  357. float * m_pDebugBuffer;
  358. volatile uint64_t* m_pReadIndexAddr; // pointer to an int containing the index of next block in the buffer that the hardware will read
  359. char m_nPhysicalOutChannels;
  360. CInterlockedInt m_nTotalBlocksPlayed;
  361. /// ----- THE FOLLOWING ARE CONTROLLED BY ONE MUTEX ----------
  362. CAudioIckyUpsampler m_upsampler;
  363. /// ----------------------------------------------------------
  364. class CAudioPS3DMAThread : public CThread
  365. {
  366. public:
  367. CAudioPS3DMAThread( );
  368. bool Setup( CAudioPS3LibAudio *pOwner );
  369. CInterlockedInt m_eHalt; // owner can signal halt with this by setting it to TRUE
  370. job_sndupsampler::JobDescriptor_t * m_pSndUpsamplerJobDescriptor;
  371. job_sndupsampler::JobOutput_t * GetSndUpsamplerJobOutput() { return ( job_sndupsampler::JobOutput_t * )( m_pSndUpsamplerJobDescriptor + 1 ); }
  372. job_sndupsampler::JobParams_t * GetSndUpsamplerJobParams() { return job_sndupsampler::GetJobParams( m_pSndUpsamplerJobDescriptor ); }
  373. void OnVjobsInit( VJobsRoot* pRoot );
  374. void OnVjobsShutdown();
  375. protected:
  376. virtual int Run();
  377. virtual void OnExit(); // clean up
  378. int OutputData( int nCurrentAudioBlockReadIndex ) RESTRICT; // dump sounds from the upsampler into the DAC. return number of blocks written.
  379. int m_nFilledBuffers; // the number of block that are full of data but haven't been played yet.
  380. int m_nPreviouslySeenNextToUpdateBufferIndex;
  381. // the event queue (for counting how many blocks get played -- we could also use this for async sound writing)
  382. sys_event_queue_t m_AudioEventQueue;
  383. sys_ipc_key_t m_AudioIPCKey;
  384. CAudioPS3LibAudio *m_pOwner;
  385. enum { STACKSIZE=4095 };
  386. };
  387. // a tiny thread whose only job in existence is to feed libaudio from our upsampling buffer.
  388. // it has to be this way because the system only caches two events from libaudio, and so
  389. // we can't know how many blocks have been played if we happen to be polled less than once
  390. // every ten milliseconds.
  391. static CAudioPS3DMAThread s_LibAudioThread;
  392. friend class CAudioPS3DMAThread;
  393. CEngineVoicePs3 m_VoiceData;
  394. };
  395. CAudioPS3LibAudio *CAudioPS3LibAudio::m_pSingleton = NULL;
  396. CAudioPS3LibAudio::CAudioPS3DMAThread::CAudioPS3DMAThread()
  397. : m_nFilledBuffers(0)
  398. , m_eHalt(false)
  399. , m_pSndUpsamplerJobDescriptor( NULL )
  400. {
  401. }
  402. // [Main] called from the context of main thread
  403. //
  404. void CAudioPS3LibAudio::CAudioPS3DMAThread::OnVjobsInit( VJobsRoot* pRoot )
  405. {
  406. Assert( !m_pSndUpsamplerJobDescriptor );
  407. m_pSndUpsamplerJobDescriptor = ( job_sndupsampler::JobDescriptor_t * )MemAlloc_AllocAligned( sizeof( job_sndupsampler::JobDescriptor_t ) + sizeof( job_sndupsampler::JobOutput_t ), 128 );
  408. m_pSndUpsamplerJobDescriptor->header = *pRoot->m_pJobSndUpsampler;
  409. m_pSndUpsamplerJobDescriptor->header.useInOutBuffer = 0;
  410. job_sndupsampler::JobParams_t * pJobParams = job_sndupsampler::GetJobParams( m_pSndUpsamplerJobDescriptor );
  411. pJobParams->m_flMaxSumStereo = 500;
  412. pJobParams->m_nDebuggerBreak = 0;
  413. pJobParams->m_deviceChannels = m_pOwner->m_deviceChannels;
  414. Assert( pJobParams->m_deviceChannels == m_pOwner->m_deviceChannels );
  415. pJobParams->m_bIsSurround = m_pOwner->IsSurround();
  416. pJobParams->m_nCellAudioBlockSamplesLog2 = COMPILE_TIME_LOG2( CELL_AUDIO_BLOCK_SAMPLES );
  417. cellAudioCreateNotifyEventQueue( &m_AudioEventQueue, &m_AudioIPCKey );
  418. cellAudioSetNotifyEventQueue( m_AudioIPCKey );
  419. Start(STACKSIZE);
  420. }
  421. // [Main] called from the context of main thread
  422. //
  423. void CAudioPS3LibAudio::CAudioPS3DMAThread::OnVjobsShutdown()
  424. {
  425. m_eHalt = true ;
  426. Join();
  427. cellAudioRemoveNotifyEventQueue( m_AudioIPCKey );
  428. sys_event_queue_destroy( m_AudioEventQueue, SYS_EVENT_QUEUE_DESTROY_FORCE );
  429. Assert( m_pSndUpsamplerJobDescriptor );
  430. if( m_pSndUpsamplerJobDescriptor )
  431. {
  432. m_pSndUpsamplerJobDescriptor = NULL;
  433. job_sndupsampler::JobOutput_t * pJobOutput = GetSndUpsamplerJobOutput();
  434. MemAlloc_FreeAligned( m_pSndUpsamplerJobDescriptor );
  435. m_pSndUpsamplerJobDescriptor = NULL;
  436. }
  437. }
  438. bool CAudioPS3LibAudio::CAudioPS3DMAThread::Setup( CAudioPS3LibAudio *pOwner )
  439. {
  440. if ( m_pOwner )
  441. {
  442. AssertMsg( false, "Tried to make two CAudioPS3LibAudio s\n");
  443. return false;
  444. }
  445. SetName("CAudioPS3DMAThread");
  446. m_pOwner = pOwner;
  447. return true;
  448. }
  449. void CAudioPS3LibAudio::CAudioPS3DMAThread::OnExit()
  450. {
  451. // NOTE: please don't destruct resources created in the context of main thread here; Setup() is called in main thread; OnExit is called from the audio thread.
  452. // destroy those resources where you join the thread , the same way they are created before the thread Start() is called
  453. // NOTE: we need to shutdown AFTER exiting the thread, to avoid accessing thread structure during/after shutdown
  454. Assert( m_pSndUpsamplerJobDescriptor );
  455. }
  456. int CAudioPS3LibAudio::CAudioPS3DMAThread::Run()
  457. {
  458. // this needs to be a high priority thread since it is realtime
  459. SetPriority( 5 );
  460. m_nPreviouslySeenNextToUpdateBufferIndex = *(m_pOwner->m_pReadIndexAddr);
  461. while ( !m_eHalt )
  462. {
  463. // wait for the audio engine to signal that it's mixed out a buffer
  464. // and is ready for another
  465. sys_event_t audioevent;
  466. int waitresult = sys_event_queue_receive( m_AudioEventQueue, &audioevent, 5000000 );
  467. if ( waitresult == ETIMEDOUT )
  468. {
  469. // warnings are commented out here because CUtlString::Format() allocates a 4096byte buffer and thereby blows
  470. // the thread stack!
  471. // Warning("Went five seconds without a libaudio event, wtf?\n");
  472. continue;
  473. }
  474. else if ( waitresult != CELL_OK )
  475. {
  476. Error("Failed to wait on libaudio buffer, error %x\n", waitresult );
  477. return -1;
  478. }
  479. // down here, we successfully received an event
  480. // make sure we still haven't been halted
  481. if ( m_eHalt )
  482. break;
  483. int nextBufferToBeRead = *(m_pOwner->m_pReadIndexAddr);
  484. if ( nextBufferToBeRead != (m_nPreviouslySeenNextToUpdateBufferIndex + 1) % CELLAUDIO_PORT_BUFFER_BLOCKS )
  485. {
  486. // warnings are commented out here because CUtlString::Format() allocates a 4096byte buffer and thereby blows
  487. // the thread stack!
  488. // Warning( "Missed an audio buffer being sent through! previously seen was %d now is %d\n",
  489. // m_nPreviouslySeenNextToUpdateBufferIndex, nextBufferToBeRead );
  490. // flush everything and start over
  491. m_nFilledBuffers = 0;
  492. }
  493. else
  494. {
  495. // one more buffer has been eaten
  496. m_nFilledBuffers = m_nFilledBuffers <= 0 ? 0 : m_nFilledBuffers - 1;
  497. }
  498. // let's avoid a sys call in non-debug configurations
  499. if ( !ENABLE_SOUND_DEBUG || ( CPS3FrontPanelLED::GetSwitches() & CPS3FrontPanelLED::kPS3SWITCH2 ) == 0 )
  500. {
  501. int bufsRead = OutputData( nextBufferToBeRead );
  502. // Msg("Emited %d bufs\n", bufsRead);
  503. m_nFilledBuffers += bufsRead;
  504. }
  505. else
  506. {
  507. // An example of a dead simple sine wave test:
  508. m_pOwner->m_upsampler.Flush();
  509. float * pOut = m_pOwner->GetAddressForBlockIdx( nextBufferToBeRead );
  510. // brain-dead test, just a low note
  511. for ( int n = 0 ; n < CELL_AUDIO_BLOCK_SAMPLES ; ++n )
  512. {
  513. float f = 0.4f * sin( (4.0 / (double)CELL_AUDIO_BLOCK_SAMPLES) * M_PI * n );
  514. pOut[n*2 + 0] = f;
  515. pOut[n*2 + 1] = f;
  516. m_nFilledBuffers = 1;
  517. }
  518. /*
  519. char buffer[200];
  520. int len = V_snprintf( buffer, sizeof( buffer) , "Snd %x, @%p\n", CELL_AUDIO_BLOCK_SAMPLES * 2 * sizeof( float ), pOut );
  521. uint wrote;
  522. sys_tty_write( SYS_TTYP3, buffer, len, &wrote );
  523. */
  524. }
  525. m_nPreviouslySeenNextToUpdateBufferIndex = nextBufferToBeRead; // *(m_pOwner->m_pReadIndexAddr); // reread the volatile
  526. // Update the voice buffer here too
  527. Audio_GetXVoice()->PlayPortInterruptHandler();
  528. }
  529. return 0;
  530. }
  531. ConVar sound_on_spu( "sound_on_spu", "1" );
  532. #define AssertRt(X) //while(!(X)){DebuggerBreak();break;}
  533. uint g_nOutputDataBlocksWritten = 0;
  534. ConVar sound_sleep_rt_thread( "sound_sleep_rt_thread", "30" );
  535. int CAudioPS3LibAudio::CAudioPS3DMAThread::OutputData( int nCurrentAudioBlockReadIndex ) RESTRICT
  536. {
  537. Assert( m_pSndUpsamplerJobDescriptor ); // the SPU job code must be loaded/streamed in already
  538. // grab the upsampler mutex
  539. int availableInputSamples = m_pOwner->m_upsampler.OutputSamplesAvailable(); // at the 48khz rate
  540. if( availableInputSamples <= 0 )
  541. {
  542. return 0;
  543. }
  544. int nextBlockIdxToWrite = ( nCurrentAudioBlockReadIndex + m_nFilledBuffers ) % CELLAUDIO_PORT_BUFFER_BLOCKS;
  545. float volumeFactor = S_GetMasterVolume() / 32767.0f;
  546. // flush as many samples out to the device as possible
  547. // now output as many full audio blocks @48khz as we can
  548. int emptyblocks = CELLAUDIO_PORT_BUFFER_BLOCKS - m_nFilledBuffers;
  549. int availableInputBlocks = availableInputSamples / CELL_AUDIO_BLOCK_SAMPLES; // rounds down
  550. // don't emit more blocks than the DAC has room for
  551. availableInputBlocks = MIN( availableInputBlocks, emptyblocks );
  552. if( availableInputBlocks <= 0 )
  553. {
  554. return 0;
  555. }
  556. uint blocksWrit = 0;
  557. if( int nSoundOnSpu = sound_on_spu.GetInt() )
  558. {
  559. job_sndupsampler::JobOutput_t * pJobOutput = GetSndUpsamplerJobOutput();
  560. job_sndupsampler::JobParams_t * pJobParams = job_sndupsampler::GetJobParams( m_pSndUpsamplerJobDescriptor );
  561. float * pSpuResult = ENABLE_SOUND_DEBUG && nSoundOnSpu > 1 ? m_pOwner->m_pDebugBuffer : NULL;
  562. uint n44kSamplesAvailable = m_pOwner->m_upsampler.m_ringCount;
  563. {
  564. MICRO_PROFILE( g_mpOutputData );
  565. AssertRt( pJobParams->m_deviceChannels == m_pOwner->m_deviceChannels );
  566. pJobParams->m_eaPortBufferAddr = ( pSpuResult ? (sys_addr_t)pSpuResult : m_pOwner->m_pPortBufferAddr ); // may change when user switches surround <-> stereo
  567. pJobParams->m_availableInputBlocks = availableInputBlocks;
  568. pJobParams->m_volumeFactor = volumeFactor;
  569. pJobParams->m_nextBlockIdxToWrite = nextBlockIdxToWrite;
  570. pJobParams->m_fractionalSamples = m_pOwner->m_upsampler.m_fractionalSamples;
  571. pJobParams->m_flBackChannelMultipler = snd_ps3_back_channel_multiplier.GetFloat();
  572. if( ENABLE_SOUND_DEBUG )
  573. {
  574. // let's avoid a sys call (sys_gpio_get) in non-debug configurations
  575. if( ( CPS3FrontPanelLED::GetSwitches() & CPS3FrontPanelLED::kPS3SWITCH3 ) == 0 )
  576. {
  577. pJobParams->m_nDebuggerBreak &= 0x7F;
  578. }
  579. else
  580. {
  581. pJobParams->m_nDebuggerBreak |= 0x80;
  582. }
  583. }
  584. uint nSampleSizeIn, nSampleSizeOutLog2;
  585. // align the number of samples so that we don't have to deal with unaligned blocks
  586. if( m_pOwner->m_upsampler.IsSurround() )
  587. {
  588. nSampleSizeOutLog2 = COMPILE_TIME_LOG2( sizeof( libaudio_sample_surround_t ) );
  589. nSampleSizeIn = sizeof( job_sndupsampler::surroundsample_t );
  590. }
  591. else
  592. {
  593. nSampleSizeOutLog2 = COMPILE_TIME_LOG2( sizeof( libaudio_sample_surround_t ) );
  594. nSampleSizeIn = sizeof( job_sndupsampler::stereosample_t );
  595. }
  596. CDmaListConstructor dmaConstructor( m_pSndUpsamplerJobDescriptor->workArea.dmaList );
  597. int nRingHead = m_pOwner->m_upsampler.m_ringHead;
  598. uintp ea44kSamplesIn = m_pOwner->m_upsampler.m_ea44kSamplesIn;
  599. uint eaInputSamplesBegin = ea44kSamplesIn + ( nRingHead * nSampleSizeIn );
  600. pJobParams->m_eaInputSamplesBegin0xF = uint8( eaInputSamplesBegin & 0xF );
  601. pJobParams->m_ringHead = nRingHead;
  602. pJobParams->m_ringCount = n44kSamplesAvailable;
  603. if( nRingHead + n44kSamplesAvailable > CAudioIckyUpsampler::BUFFERSIZE )
  604. {
  605. // split into 2 transactions
  606. dmaConstructor.AddInputDmaLargeRegion( (void*)( eaInputSamplesBegin & -16 ), (void*)( ea44kSamplesIn + ( CAudioIckyUpsampler::BUFFERSIZE * nSampleSizeIn ) ) );
  607. // this is always aligned
  608. dmaConstructor.AddInputDmaLarge( AlignValue( ( nRingHead + n44kSamplesAvailable - CAudioIckyUpsampler::BUFFERSIZE ) * nSampleSizeIn, 16 ), ( void * )ea44kSamplesIn );
  609. }
  610. else
  611. {
  612. dmaConstructor.AddInputDmaLargeUnalignedRegion( (void*)eaInputSamplesBegin, (void*)( eaInputSamplesBegin + ( n44kSamplesAvailable * nSampleSizeIn ) ) );
  613. }
  614. m_pSndUpsamplerJobDescriptor->header.sizeOut =
  615. sizeof( job_sndupsampler::JobOutput_t ) // job output structure
  616. + ( ( 2 * CELL_AUDIO_BLOCK_SAMPLES ) << nSampleSizeOutLog2 ) // output samples
  617. + 16 // potential misalignment of output samples ( stereo samples are 8 bytes each )
  618. ;
  619. dmaConstructor.FinishInBuffer( &m_pSndUpsamplerJobDescriptor->header, pJobParams );
  620. pJobOutput->m_nBlockWritten = 0xFFFFFFFF; // invalid value
  621. // NOTE: if we need to wait for the job, we need to push it with CELL_SPURS_JOBQUEUE_FLAG_SYNC_JOB flag!
  622. if( int nError = m_pOwner->m_pRoot->m_queuePortSound.pushJob( &m_pSndUpsamplerJobDescriptor->header, sizeof( job_sndupsampler::JobDescriptor_t ), 0, 0 ) )
  623. {
  624. Warning( "Cannot start Sound job, error 0x%X\n", nError );
  625. }
  626. }
  627. //m_pOwner->m_pRoot->m_queuePortSound.sync( 0 ); // NOTE! if we use sync() here, we need to push job with CELL_SPURS_JOBQUEUE_FLAG_SYNC_JOB flag!
  628. sys_timer_usleep( sound_sleep_rt_thread.GetInt() );
  629. while( *(volatile uint32*)( &pJobOutput->m_nBlockWritten ) == 0xFFFFFFFF ); // must be synchronized by now
  630. {
  631. sys_timer_usleep( 100 ); // wait for the job to complete; we aren't in a hurry
  632. }
  633. AssertRt( n44kSamplesAvailable > pJobOutput->m_n44kSamplesConsumed ); // we must never consume the last sample, because we won't be able to extrapolate the value
  634. AssertRt( m_pOwner->m_upsampler.m_ringCount >= n44kSamplesAvailable );
  635. AUTO_LOCK( m_pOwner->m_upsampler.m_mutex ); // TODO: get rid of this lock, use head-tail counters instead of head+count
  636. if( !m_pOwner->m_upsampler.m_ringCount )
  637. {
  638. // if the ring count was flushed, don't do any checks - it's useless
  639. blocksWrit = pJobOutput->m_nBlockWritten;
  640. }
  641. else if( ENABLE_SOUND_DEBUG && pSpuResult )
  642. {
  643. int nRingCountBegin = m_pOwner->m_upsampler.m_ringCount, nRingHeadBegin = m_pOwner->m_upsampler.m_ringHead;
  644. // STEREO!
  645. while ( availableInputBlocks>0 )
  646. {
  647. // all sorts of obvious ways to optimize this (explicit iterators, VMX, SPU, etc)
  648. float * RESTRICT pWriteDest = (m_pOwner->GetAddressForBlockIdx( nextBlockIdxToWrite ));
  649. float *buffer = new float[CELL_AUDIO_BLOCK_SAMPLES * sizeof( libaudio_sample_surround_t ) / sizeof( float )];
  650. AssertRt( fabsf( m_pOwner->m_upsampler.m_fractionalSamples - pJobOutput->m_trace[blocksWrit].m_fractionalSamplesBefore ) < 1e-2f );
  651. int nRingHeadBefore = m_pOwner->m_upsampler.m_ringHead, nRingCountBefore = m_pOwner->m_upsampler.m_ringCount;
  652. float fractionalSamplesBefore = m_pOwner->m_upsampler.m_fractionalSamples;
  653. if( ENABLE_SOUND_DEBUG )
  654. {
  655. m_pOwner->m_upsampler.m_ringHead = nRingHeadBefore;
  656. m_pOwner->m_upsampler.m_ringCount = nRingCountBefore;
  657. m_pOwner->m_upsampler.m_fractionalSamples = fractionalSamplesBefore;
  658. }
  659. int samplesWritten = m_pOwner->IsSurround() ?
  660. m_pOwner->m_upsampler.ExcreteSurround( CELL_AUDIO_BLOCK_SAMPLES, reinterpret_cast<libaudio_sample_surround_t *>(buffer), volumeFactor ) :
  661. m_pOwner->m_upsampler.ExcreteStereo( CELL_AUDIO_BLOCK_SAMPLES, reinterpret_cast<libaudio_sample_surround_t *>(buffer), volumeFactor ) ;
  662. AssertRt( samplesWritten == CELL_AUDIO_BLOCK_SAMPLES );
  663. int nSamplesConsumedSoFar = nRingCountBegin - m_pOwner->m_upsampler.m_ringCount;
  664. AssertRt( nSamplesConsumedSoFar == pJobOutput->m_trace[blocksWrit].m_n44kSamplesConsumed );
  665. float * pSpuResultBlock = pSpuResult + ( pWriteDest - ( float * )m_pOwner->m_pPortBufferAddr );
  666. for( uint i = 0; i < CELL_AUDIO_BLOCK_SAMPLES * m_pOwner->m_deviceChannels; )
  667. {
  668. if( fabsf( buffer[i] - pSpuResultBlock[i] ) > 1e-6f )
  669. {
  670. DebuggerBreakIfDebugging();
  671. pJobOutput->m_nBlockWritten = 0xFFFFFFFF; // invalid value
  672. int nError = m_pOwner->m_pRoot->m_queuePortSound.pushJob( &m_pSndUpsamplerJobDescriptor->header, sizeof( job_sndupsampler::JobDescriptor_t ), 0, 0 );
  673. while( pJobOutput->m_nBlockWritten == 0xFFFFFFFF )
  674. continue;
  675. continue;
  676. }
  677. ++i;
  678. }
  679. --availableInputBlocks;
  680. nextBlockIdxToWrite = (nextBlockIdxToWrite + 1)%CELLAUDIO_PORT_BUFFER_BLOCKS;
  681. ++blocksWrit;
  682. delete []buffer;
  683. }
  684. AssertRt( blocksWrit == pJobOutput->m_nBlockWritten );
  685. AssertRt( m_pOwner->m_upsampler.m_ringHead == pJobOutput->m_ringHead );
  686. AssertRt( fabsf( m_pOwner->m_upsampler.m_fractionalSamples - pJobOutput->m_fractionalSamples ) < 1e-6f );
  687. }
  688. else
  689. {
  690. blocksWrit = pJobOutput->m_nBlockWritten;
  691. m_pOwner->m_upsampler.m_ringCount -= pJobOutput->m_n44kSamplesConsumed;
  692. m_pOwner->m_upsampler.m_ringHead = pJobOutput->m_ringHead;
  693. m_pOwner->m_upsampler.m_fractionalSamples = pJobOutput->m_fractionalSamples;
  694. }
  695. }
  696. else
  697. {
  698. // STEREO!
  699. AUTO_LOCK( m_pOwner->m_upsampler.m_mutex ); // TODO: get rid of this lock, use head-tail counters instead of head+count
  700. MICRO_PROFILE( g_mpOutputData );
  701. g_flSumStereo = 0;
  702. while ( availableInputBlocks>0 )
  703. {
  704. // all sorts of obvious ways to optimize this (explicit iterators, VMX, SPU, etc)
  705. float * RESTRICT pWriteDest = (m_pOwner->GetAddressForBlockIdx( nextBlockIdxToWrite ));
  706. int samplesWritten = m_pOwner->IsSurround() ?
  707. m_pOwner->m_upsampler.ExcreteSurround( CELL_AUDIO_BLOCK_SAMPLES, reinterpret_cast<libaudio_sample_surround_t *>(pWriteDest), volumeFactor ) :
  708. m_pOwner->m_upsampler.ExcreteStereo( CELL_AUDIO_BLOCK_SAMPLES, reinterpret_cast<libaudio_sample_surround_t *>(pWriteDest), volumeFactor ) ;
  709. AssertRt( samplesWritten == CELL_AUDIO_BLOCK_SAMPLES );
  710. --availableInputBlocks;
  711. nextBlockIdxToWrite = (nextBlockIdxToWrite + 1)%CELLAUDIO_PORT_BUFFER_BLOCKS;
  712. ++blocksWrit;
  713. }
  714. if( ENABLE_SOUND_DEBUG && g_flSumStereo > GetSndUpsamplerJobParams()->m_flMaxSumStereo )
  715. {
  716. char buffer[200];
  717. int len = V_snprintf( buffer, sizeof( buffer) , "PPU Stereo %g\n", g_flSumStereo );
  718. uint wrote;
  719. sys_tty_write( SYS_TTYP3, buffer, len, &wrote );
  720. }
  721. }
  722. m_pOwner->m_nTotalBlocksPlayed += blocksWrit;
  723. g_nOutputDataBlocksWritten += blocksWrit;
  724. return blocksWrit;
  725. }
  726. CAudioPS3LibAudio::CAudioPS3DMAThread CAudioPS3LibAudio::s_LibAudioThread;
  727. bool CAudioPS3LibAudio::s_bCellAudioStateInitialized = false;
  728. CUtlVectorConservative<CellAudioOutState> CAudioPS3LibAudio::s_vCellAudioOutputDeviceStates(0,1);
  729. // NULL out the pointers so that failures are more immediate, evident
  730. CAudioPS3LibAudio::CAudioPS3LibAudio( void ) : m_deviceChannels( 0 )
  731. , m_nPortNum( -1 )
  732. , m_nPhysicalOutChannels( 0 )
  733. , m_pPortBufferAddr( NULL )
  734. , m_pReadIndexAddr( NULL )
  735. , m_nTotalBlocksPlayed( 0 )
  736. {
  737. // AssertMsg( false, "Please implement CAudioPS3LibAudio\n" );
  738. }
  739. //-----------------------------------------------------------------------------
  740. // Create XAudio Device
  741. //-----------------------------------------------------------------------------
  742. IAudioDevice *Audio_CreatePS3AudioDevice( bool bInitVoice )
  743. {
  744. MEM_ALLOC_CREDIT();
  745. if ( CommandLine()->CheckParm( "-nosound" ) )
  746. {
  747. // respect forced lack of audio
  748. return NULL;
  749. }
  750. if ( !CAudioPS3LibAudio::m_pSingleton )
  751. {
  752. CAudioPS3LibAudio::m_pSingleton = new CAudioPS3LibAudio;
  753. if ( !CAudioPS3LibAudio::m_pSingleton->Init() )
  754. {
  755. AssertMsg( false, "Failed to init CAudioPS3LibAudio\n" );
  756. delete CAudioPS3LibAudio::m_pSingleton;
  757. CAudioPS3LibAudio::m_pSingleton = NULL;
  758. }
  759. }
  760. // need to support early init of XAudio (for bink startup video) without the voice
  761. // voice requires matchmaking which is not available at this early point
  762. // this defers the voice init to a later engine init mark
  763. if ( bInitVoice && CAudioPS3LibAudio::m_pSingleton )
  764. {
  765. CAudioPS3LibAudio::m_pSingleton->GetVoiceData()->VoiceInit();
  766. }
  767. return CAudioPS3LibAudio::m_pSingleton;
  768. }
  769. //-----------------------------------------------------------------------------
  770. // Destructor
  771. //-----------------------------------------------------------------------------
  772. CAudioPS3LibAudio::~CAudioPS3LibAudio( void )
  773. {
  774. if ( m_deviceChannels > 0 )
  775. {
  776. AssertMsg( false, "CAudioPS3LibAudio() called without being Shutdown() first! Initiating emergency purge.\n" );
  777. Shutdown();
  778. }
  779. g_pVJobs->Unregister( this );
  780. m_pSingleton = NULL;
  781. }
  782. void CAudioPS3LibAudio::QuerySystemAudioConfiguration( bool bForceRefreshCache )
  783. {
  784. if ( !s_bCellAudioStateInitialized || bForceRefreshCache )
  785. {
  786. // flush the data we've got if necessary
  787. s_vCellAudioOutputDeviceStates.RemoveAll();
  788. const int numDevices = cellAudioOutGetNumberOfDevice( CELL_AUDIO_OUT_PRIMARY );
  789. if ( numDevices < 0 )
  790. {
  791. // error!
  792. s_vCellAudioOutputDeviceStates.RemoveAll();
  793. AssertMsg1( false, "cellAudioOutGetNumberOfDevice failed: %x\n", numDevices );
  794. return;
  795. }
  796. s_vCellAudioOutputDeviceStates.EnsureCount(numDevices); // set the count of output devices
  797. for ( int d = 0 ; d < s_vCellAudioOutputDeviceStates.Count() ; ++d )
  798. {
  799. int suc = cellAudioOutGetState( CELL_AUDIO_OUT_PRIMARY, d, &s_vCellAudioOutputDeviceStates[d] );
  800. AssertMsg2( suc == CELL_OK, "cellAudioOutGetState(%d) failed: %x\n", d, suc );
  801. }
  802. s_bCellAudioStateInitialized = true;
  803. }
  804. }
  805. // This code is from Bink's examples - they are configuring the HW mixer
  806. // This may resolve the issue with center being lost when we are in stereo.
  807. // Note that we do not detect DTS. Maybe we should?
  808. static int init_audio_hardware( int minimum_chans, int &nPhysicalChannels )
  809. {
  810. int ret;
  811. int ch_pcm;
  812. int ch_bit;
  813. CellAudioOutConfiguration a_config;
  814. memset( &a_config, 0, sizeof( CellAudioOutConfiguration ) );
  815. // Note that we would probably need to do something smarter than what this function is doing.
  816. // It seems we should test these cases in this order (highest to lowest quality):
  817. // Do we have enough channels for:
  818. // - LPCM
  819. // - DTS
  820. // - AC3
  821. // If not, then we have to downmix:
  822. // - If from 8 to 6 (type B downmixer)
  823. // - Try LPCM encoder
  824. // - Otherwise try DTS encoder - Is this necessary?
  825. // - Then the AC3 encoder - Is this necessary?
  826. // - If it does not work (or from 8 to 2, 6 to 2) - use Type A downmixer:
  827. // - Try PCM encoder
  828. // Current code is doing:
  829. // - Do we have enough channels for LPCM
  830. // If yes, set it, done.
  831. // If not
  832. // Do we have support for 6 channels?
  833. // If yes, then set downmixer from 8 to 6 in LPCM mode.
  834. // If not then we support only 2 channels.
  835. // Do we have support for Dolby (any number of channels)?
  836. // If yes, do we have enough support for requested channels?
  837. // If yes, set it, done.
  838. // If not, set the downmixer to 5.1, if successful, done.
  839. // If not (or Dolby downmixer to 5.1 failed), downmix to 2 with LPCM.
  840. // first lets see how many pcm output channels we have
  841. ch_pcm = cellAudioOutGetSoundAvailability( CELL_AUDIO_OUT_PRIMARY,
  842. CELL_AUDIO_OUT_CODING_TYPE_LPCM,
  843. CELL_AUDIO_OUT_FS_48KHZ, 0 );
  844. nPhysicalChannels = ch_pcm;
  845. if ( ch_pcm >= minimum_chans )
  846. {
  847. a_config.channel = ch_pcm;
  848. a_config.encoder = CELL_AUDIO_OUT_CODING_TYPE_LPCM;
  849. a_config.downMixer = CELL_AUDIO_OUT_DOWNMIXER_NONE; /* No downmixer is used */
  850. cellAudioOutConfigure( CELL_AUDIO_OUT_PRIMARY, &a_config, NULL, 0 );
  851. ret = ch_pcm;
  852. }
  853. else
  854. {
  855. switch ( ch_pcm )
  856. {
  857. case 6:
  858. // this means we asked for 8 channels, but only 6 are available
  859. // so, we'll turn on the 7.1 to 5.1 downmixer.
  860. a_config.channel = 6;
  861. a_config.encoder = CELL_AUDIO_OUT_CODING_TYPE_LPCM;
  862. a_config.downMixer = CELL_AUDIO_OUT_DOWNMIXER_TYPE_B;
  863. if ( cellAudioOutConfigure( CELL_AUDIO_OUT_PRIMARY, &a_config, NULL, 0 ) != CELL_OK )
  864. {
  865. return 0; // error - the downmixer didn't init
  866. }
  867. ret = 8;
  868. break;
  869. case 2:
  870. // ok, this means they asked for multi-channel out, but only stereo
  871. // is supported. we'll try Dolby digital first and then the downmixer
  872. // Support for DTS - disabled as we can't test it right now
  873. #if 0
  874. ch_bit = cellAudioOutGetSoundAvailability( CELL_AUDIO_OUT_PRIMARY,
  875. CELL_AUDIO_OUT_CODING_TYPE_DTS,
  876. CELL_AUDIO_OUT_FS_48KHZ, 0 );
  877. if ( ch_bit > 0 )
  878. {
  879. a_config.channel = ch_bit;
  880. a_config.encoder = CELL_AUDIO_OUT_CODING_TYPE_DTS;
  881. if ( ch_bit >= minimum_chans )
  882. {
  883. // we have enough channels to support their minimum
  884. a_config.downMixer = CELL_AUDIO_OUT_DOWNMIXER_NONE;
  885. ret = ch_bit;
  886. }
  887. else
  888. {
  889. // we don't have enough channels to support their minimum, so use the downmixer
  890. a_config.downMixer = CELL_AUDIO_OUT_DOWNMIXER_TYPE_B; // Downmix to 5.1 channels
  891. ret = 8;
  892. }
  893. if ( cellAudioOutConfigure( CELL_AUDIO_OUT_PRIMARY, &a_config, NULL, 0 ) == CELL_OK )
  894. {
  895. nPhysicalChannels = ch_bit;
  896. break;
  897. }
  898. // if we got here the DTS encoder didn't init, so fall through to Dolby encoder
  899. }
  900. #endif
  901. ch_bit = cellAudioOutGetSoundAvailability( CELL_AUDIO_OUT_PRIMARY,
  902. CELL_AUDIO_OUT_CODING_TYPE_AC3,
  903. CELL_AUDIO_OUT_FS_48KHZ, 0 );
  904. if ( ch_bit > 0 )
  905. {
  906. a_config.channel = ch_bit;
  907. a_config.encoder = CELL_AUDIO_OUT_CODING_TYPE_AC3;
  908. if ( ch_bit >= minimum_chans )
  909. {
  910. // we have enough channels to support their minimum
  911. a_config.downMixer = CELL_AUDIO_OUT_DOWNMIXER_NONE;
  912. ret = ch_bit;
  913. }
  914. else
  915. {
  916. // we don't have enough channels to support their minimum, so use the downmixer
  917. a_config.downMixer = CELL_AUDIO_OUT_DOWNMIXER_TYPE_B; // Downmix to 5.1 channels
  918. ret = 8;
  919. }
  920. if ( cellAudioOutConfigure( CELL_AUDIO_OUT_PRIMARY, &a_config, NULL, 0 ) == CELL_OK )
  921. {
  922. nPhysicalChannels = ch_bit;
  923. break;
  924. }
  925. // if we got here the Dolby encoder didn't init, so fall through to downmixing to stereo
  926. }
  927. a_config.channel = 2;
  928. a_config.encoder = CELL_AUDIO_OUT_CODING_TYPE_LPCM;
  929. a_config.downMixer = CELL_AUDIO_OUT_DOWNMIXER_TYPE_A; // Downmix to 2 channels
  930. if ( cellAudioOutConfigure( CELL_AUDIO_OUT_PRIMARY, &a_config, NULL, 0 ) != CELL_OK )
  931. {
  932. return 0; // error - downmixer didn't work
  933. }
  934. ret = 7; // downmixer does 7.0 to 2.0 downmixing...
  935. break;
  936. default:
  937. // some other weird case that we don't understand
  938. return 0;
  939. }
  940. }
  941. /*
  942. // wait for the device to enable (not necessary anymore?)
  943. {
  944. CellAudioOutState a_state;
  945. do
  946. {
  947. if ( cellAudioOutGetState( CELL_AUDIO_OUT_PRIMARY, 0, &a_state ) != CELL_OK )
  948. {
  949. return( ret ); // If that failed, we are still returning the expected value. This call is not critical.
  950. }
  951. sys_timer_usleep( 5000 );
  952. }
  953. while ( a_state.state != CELL_AUDIO_OUT_OUTPUT_STATE_ENABLED );
  954. }
  955. */
  956. // turn off copy protection stupidness
  957. cellAudioOutSetCopyControl( CELL_AUDIO_OUT_PRIMARY,
  958. CELL_AUDIO_OUT_COPY_CONTROL_COPY_FREE );
  959. return( ret );
  960. }
  961. bool CAudioPS3LibAudio::Init( void )
  962. {
  963. if ( this == CAudioPS3LibAudio::m_pSingleton ) // guard against possible multiples of this thing
  964. {
  965. int success = cellAudioInit();
  966. if ( success != CELL_OK )
  967. {
  968. Warning( "Could not initalize PS3 audio, error code: %x\n", success );
  969. return false;
  970. }
  971. }
  972. // get relevant info from the OS
  973. QuerySystemAudioConfiguration( false );
  974. // work out our surround sound mode
  975. // assumption: we're only looking at CELL_AUDIO_OUT_PRIMARY
  976. // also: we could query headphones properly from some kind of UI setting
  977. m_bHeadphone = snd_surround.GetInt() == SURROUND_HEADPHONES; // in case we set this from the UI
  978. // number of physical out channels connected
  979. // (remember libaudio only logically supports 2 or 8 in the ring buffer)
  980. // also this is an enum rather than a direct count
  981. const int eOutputChannels = s_vCellAudioOutputDeviceStates[0].soundMode.channel;
  982. switch ( eOutputChannels )
  983. {
  984. case CELL_AUDIO_OUT_CHNUM_2:
  985. default:
  986. m_nPhysicalOutChannels = 2;
  987. break;
  988. case CELL_AUDIO_OUT_CHNUM_4:
  989. m_nPhysicalOutChannels = 4; // quad
  990. break;
  991. case CELL_AUDIO_OUT_CHNUM_6:
  992. m_nPhysicalOutChannels = 6; // 5.1
  993. break;
  994. case CELL_AUDIO_OUT_CHNUM_8:
  995. m_nPhysicalOutChannels = 8; // 7.1
  996. break;
  997. }
  998. int nPhysicalChannels = m_nPhysicalOutChannels;
  999. int nResult = init_audio_hardware( 6, nPhysicalChannels ); // Ask for 5.1, so it can down mix to stereo if necessary
  1000. // Then engine will support the right format regardless of what the down mixer can support
  1001. if ( nResult != 0 )
  1002. {
  1003. m_nPhysicalOutChannels = nPhysicalChannels; // We could initialize the down mixer, lets use the physical out channels.
  1004. }
  1005. CellAudioPortParam aparam;
  1006. float flBackChannelMultipler = 0.0f;
  1007. if ( m_nPhysicalOutChannels > 2 )
  1008. {
  1009. snd_surround.SetValue( SURROUND_DIGITAL7DOT1 ); // all internal mixing is 8 channels; downsampling occurs in hardware
  1010. m_bSurround = m_bSurroundCenter = true;
  1011. m_deviceChannels = 8; // you can only have stereo or 7.1
  1012. aparam.nChannel = CELL_AUDIO_PORT_8CH;
  1013. // If we are in 7.1, we want to fill the back channel with the same value as the side channels
  1014. // However, if we are in 5.1, we don't want to fill it in case the downmixer uses it and creates unwanted side-effects.
  1015. if ( m_nPhysicalOutChannels > 6 )
  1016. {
  1017. flBackChannelMultipler = 1.0f;
  1018. }
  1019. }
  1020. else
  1021. {
  1022. // Because we have the down mixer for Bink, we are going to output 7.1 even for stereo only
  1023. // That way Bink can output 5.1 normally, and the down mixer will change it to stereo.
  1024. // Same for the engine, however the engine is going to only output stereo values.
  1025. snd_surround.SetValue( m_bHeadphone ? SURROUND_HEADPHONES : SURROUND_STEREO );
  1026. m_bSurround = m_bSurroundCenter = false;
  1027. m_deviceChannels = 8;
  1028. aparam.nChannel = CELL_AUDIO_PORT_8CH;
  1029. }
  1030. snd_ps3_back_channel_multiplier.SetValue( flBackChannelMultipler );
  1031. // open the audio port
  1032. COMPILE_TIME_ASSERT( CELLAUDIO_PORT_BUFFER_BLOCKS == 8 || CELLAUDIO_PORT_BUFFER_BLOCKS == 16 ); // You're only allowed to have either 8 or 16 blocks in the ring buffer!
  1033. aparam.nBlock = CELLAUDIO_PORT_BUFFER_BLOCKS == 16 ? CELL_AUDIO_BLOCK_16 : CELL_AUDIO_BLOCK_8; // you can have 8 or 16 blocks in the ring buffer. 16 gives us about 80ms of buffer.
  1034. aparam.attr = 0; // no special attributes
  1035. aparam.level = 1; // this is actually ignored without setting CELL_AUDIO_PORTATTR_INITLEVEL to the attributes above.
  1036. int success = cellAudioPortOpen( &aparam, &m_nPortNum );
  1037. if ( success != CELL_OK )
  1038. {
  1039. Warning("Could not initialize libaudio, error code %x\n", success );
  1040. }
  1041. else
  1042. {
  1043. DevMsg( "PS3 libaudio device initialized:\n" );
  1044. DevMsg( " %d channel(s)\n"
  1045. " %d bits/sample\n"
  1046. " %d samples/sec\n", DeviceChannels(), DeviceSampleBits(), DeviceDmaSpeed() );
  1047. }
  1048. // get the pointers to the start and next-block-indicator of the ring buffer
  1049. CellAudioPortConfig configinfo;
  1050. cellAudioGetPortConfig( m_nPortNum, &configinfo );
  1051. m_pPortBufferAddr = configinfo.portAddr;
  1052. uint nPortBufferSize = GetPortBufferSize();
  1053. m_pDebugBuffer = ENABLE_SOUND_DEBUG ? (float*)MemAlloc_AllocAligned( nPortBufferSize, 16 ) : NULL ;
  1054. m_pReadIndexAddr = (uint64_t*)configinfo.readIndexAddr;
  1055. m_upsampler.Init( m_bSurround );
  1056. s_LibAudioThread.Setup( this );
  1057. g_pVJobs->Register( this );
  1058. cellAudioPortStart( m_nPortNum );
  1059. return success == CELL_OK;
  1060. }
  1061. int CAudioPS3LibAudio::DeviceChannels( void )
  1062. {
  1063. return m_deviceChannels;
  1064. }
  1065. int CAudioPS3LibAudio::DeviceSampleBits( void )
  1066. {
  1067. return sizeof(float)*8; // the only sound format is single precision floats. PERIOD.
  1068. }
  1069. int CAudioPS3LibAudio::DeviceSampleBytes( void )
  1070. {
  1071. return sizeof(float);
  1072. }
  1073. int CAudioPS3LibAudio::DeviceDmaSpeed( void )
  1074. {
  1075. return SOUND_DMA_SPEED; // the upsampler fakes 44.1
  1076. }
  1077. int CAudioPS3LibAudio::DeviceSampleCount( void )
  1078. {
  1079. return CELL_AUDIO_BLOCK_SAMPLES * CELLAUDIO_PORT_BUFFER_BLOCKS; // each block is 256 samples long. OR ELSE.
  1080. }
  1081. inline float *CAudioPS3LibAudio::GetAddressForBlockIdx( unsigned nBlock )
  1082. {
  1083. nBlock %= CELLAUDIO_PORT_BUFFER_BLOCKS;
  1084. return reinterpret_cast<float *>( m_pPortBufferAddr + ( nBlock * sizeof(float) * CELL_AUDIO_BLOCK_SAMPLES * m_deviceChannels ) );
  1085. }
  1086. uint CAudioPS3LibAudio::GetPortBufferSize()const
  1087. {
  1088. Assert( m_deviceChannels == 2 || m_deviceChannels == 8 );
  1089. return CELLAUDIO_PORT_BUFFER_BLOCKS * CELL_AUDIO_BLOCK_SAMPLES * sizeof( float ) * m_deviceChannels;
  1090. }
  1091. inline float *CAudioPS3LibAudio::GetNextReadBlockAddress()
  1092. {
  1093. return GetAddressForBlockIdx((uint)(*m_pReadIndexAddr));
  1094. }
  1095. void CAudioPS3LibAudio::Shutdown( void )
  1096. {
  1097. // need to release ref to voice library
  1098. m_VoiceData.VoiceShutdown();
  1099. Assert( s_LibAudioThread.m_pSndUpsamplerJobDescriptor );
  1100. // unregistering with Vjobs will cascade shutdown of all SPU-related and SPU-using resources (like the audio thread - it's using SPU resources)
  1101. // this'll happen synchronously
  1102. g_pVJobs->Unregister( this );
  1103. Assert( !s_LibAudioThread.m_pSndUpsamplerJobDescriptor ); // must be shut down already
  1104. int suc = cellAudioPortClose(m_nPortNum);
  1105. AssertMsg2( suc == CELL_OK, "Couldn't close libaudio port(%d): %x\n", m_nPortNum, suc );
  1106. m_nPortNum = -1;
  1107. m_deviceChannels = 0;
  1108. m_nPhysicalOutChannels = 0;
  1109. m_pPortBufferAddr = NULL;
  1110. m_pReadIndexAddr = NULL;
  1111. m_nTotalBlocksPlayed = 0;
  1112. if ( this == CAudioPS3LibAudio::m_pSingleton )
  1113. {
  1114. CAudioPS3LibAudio::m_pSingleton = NULL;
  1115. cellAudioQuit();
  1116. s_bCellAudioStateInitialized = false;
  1117. }
  1118. }
  1119. void CAudioPS3LibAudio::Pause( void )
  1120. {
  1121. cellAudioPortStop( m_nPortNum );
  1122. }
  1123. void CAudioPS3LibAudio::UnPause( void )
  1124. {
  1125. cellAudioPortStart( m_nPortNum );
  1126. }
  1127. //-----------------------------------------------------------------------------
  1128. // Fill the output buffers with silence
  1129. //-----------------------------------------------------------------------------
  1130. void CAudioPS3LibAudio::ClearBuffer( void )
  1131. {
  1132. memset( reinterpret_cast<void *>(m_pPortBufferAddr), 0, CELLAUDIO_PORT_BUFFER_BLOCKS * CELL_AUDIO_BLOCK_SAMPLES * m_deviceChannels * sizeof(float) );
  1133. m_upsampler.Flush();
  1134. }
  1135. //-----------------------------------------------------------------------------
  1136. // Calc the paint position
  1137. //-----------------------------------------------------------------------------
  1138. int64 CAudioPS3LibAudio::PaintBegin( float mixAheadTime, int64 soundtime, int64 paintedtime )
  1139. {
  1140. // soundtime = total full samples that have been played out to hardware at dmaspeed
  1141. // paintedtime = total full samples that have been mixed at speed
  1142. // endtime = target for full samples in mixahead buffer at speed
  1143. int mixaheadsamples = mixAheadTime * DeviceDmaSpeed();
  1144. // the upsampler will consume as much as it can
  1145. int64 endtime = paintedtime + MIN(mixaheadsamples, m_upsampler.SpaceRemaining());
  1146. // only get samples in numbers divisible by four
  1147. endtime = endtime & ~0x3;
  1148. return endtime;
  1149. }
  1150. void CAudioPS3LibAudio::TransferSamples( int64 endTime )
  1151. {
  1152. AUTO_LOCK( m_upsampler.m_mutex );
  1153. // first, pull as many samples into the upsampler as we can
  1154. int sampleCountToWrite = endTime - g_paintedtime; // copied from xaudio version. a global? wtf?
  1155. Assert( sampleCountToWrite <= m_upsampler.SpaceRemaining() );
  1156. if ( IsSurround() != m_upsampler.IsSurround() )
  1157. {
  1158. AssertMsg2( false, "Tried to write %d channels onto a %d-channel audio port!\n", m_deviceChannels, m_upsampler.IsSurround() ? 8 : 2 );
  1159. // abort! abort!
  1160. m_upsampler.Flush();
  1161. return;
  1162. }
  1163. if ( IsSurround() )
  1164. m_upsampler.IngestSurround( PAINTBUFFER, REARPAINTBUFFER, CENTERPAINTBUFFER, sampleCountToWrite );
  1165. else
  1166. m_upsampler.IngestStereo( PAINTBUFFER, sampleCountToWrite );
  1167. }
  1168. void CAudioPS3LibAudio::PaintEnd( void )
  1169. {}
  1170. #if 0
  1171. #pragma message("TODO: obvious optimization opportunities")
  1172. // TODO: move to SPU -- many obvious optimization opportunities
  1173. int CAudioPS3LibAudio::TransferStereo( const portable_samplepair_t * RESTRICT pFrontBuffer, int nSamplesToWrite, int nStartBlock )
  1174. {
  1175. // Assert that the compiler hasn't failed to pack the structs properly
  1176. COMPILE_TIME_ASSERT( sizeof(libaudio_sample_stereo_t) == 8 );
  1177. COMPILE_TIME_ASSERT( sizeof(libaudio_sample_surround_t) == 32 );
  1178. AssertMsg1( m_deviceChannels == 2, "Transferred stereo onto a %d-channel audio port\n", m_deviceChannels );
  1179. float volumeFactor = S_GetMasterVolume() / 32767.0f;
  1180. uint blocksWrit = 0;
  1181. while ( nSamplesToWrite>0 )
  1182. {
  1183. // all sorts of obvious ways to optimize this (explicit iterators, VMX, SPU, etc)
  1184. libaudio_sample_stereo_t * RESTRICT pWriteDest = reinterpret_cast<libaudio_sample_stereo_t *>(GetAddressForBlockIdx( nStartBlock ));
  1185. const uint batchsize = MIN( nSamplesToWrite, CELL_AUDIO_BLOCK_SAMPLES );
  1186. for ( uint s = 0 ; s < batchsize ; ++s )
  1187. {
  1188. pWriteDest[s].left = pFrontBuffer[s].left * volumeFactor;
  1189. pWriteDest[s].right = pFrontBuffer[s].right * volumeFactor;
  1190. }
  1191. pFrontBuffer += batchsize;
  1192. nSamplesToWrite -= batchsize;
  1193. nStartBlock = (nStartBlock + 1)%CELLAUDIO_PORT_BUFFER_BLOCKS;
  1194. ++blocksWrit;
  1195. }
  1196. return blocksWrit;
  1197. }
  1198. // TODO: move to SPU -- many obvious optimization opportunities
  1199. // TODO: make a LFE channel
  1200. int CAudioPS3LibAudio::TransferSurroundInterleaved( const portable_samplepair_t * RESTRICT pFrontBuffer,
  1201. const portable_samplepair_t * RESTRICT pRearBuffer,
  1202. const int * RESTRICT pCenterBuffer,
  1203. int nSamplesToWrite, int nStartBlock )
  1204. {
  1205. if ( m_deviceChannels != 8 ) //!!EMERGENCY! EMERGENCY! WILL CAUSE CRASH!
  1206. {
  1207. AssertMsg1( false, "Tried to write 7.1 surround onto a %d-channel audio port!\n", m_deviceChannels );
  1208. return TransferStereo( pFrontBuffer, nSamplesToWrite, nStartBlock ); // CRASH AVERTED!
  1209. }
  1210. int volumeFactor = S_GetMasterVolume() * 256; // a legacy WTF
  1211. uint blocksWrit = 0;
  1212. while ( nSamplesToWrite>0 )
  1213. {
  1214. // all sorts of obvious ways to optimize this (explicit iterators, VMX, SPU, etc)
  1215. libaudio_sample_surround_t * RESTRICT pWriteDest = reinterpret_cast<libaudio_sample_surround_t*>(GetAddressForBlockIdx( nStartBlock ));
  1216. const uint batchsize = MIN( nSamplesToWrite, CELL_AUDIO_BLOCK_SAMPLES );
  1217. for ( uint s = 0 ; s < batchsize ; ++s )
  1218. {
  1219. // perform the writes in order to be gentle on the write aggregator
  1220. // (really I should go back and use dcbz+vmx here)
  1221. pWriteDest[s].left = pFrontBuffer[s].left * volumeFactor;
  1222. pWriteDest[s].right = pFrontBuffer[s].right * volumeFactor;
  1223. pWriteDest[s].center = pCenterBuffer[s] * volumeFactor;
  1224. pWriteDest[s].subwoofer = 0;
  1225. pWriteDest[s].leftsurround = pRearBuffer[s].left * volumeFactor;
  1226. pWriteDest[s].rightsurround = pRearBuffer[s].right * volumeFactor;
  1227. pWriteDest[s].leftextend = 0;
  1228. pWriteDest[s].rightextend = 0;
  1229. }
  1230. pFrontBuffer += batchsize;
  1231. pRearBuffer += batchsize;
  1232. pCenterBuffer += batchsize;
  1233. nSamplesToWrite -= batchsize;
  1234. nStartBlock = (nStartBlock + 1)%CELLAUDIO_PORT_BUFFER_BLOCKS;
  1235. ++blocksWrit;
  1236. }
  1237. return blocksWrit;
  1238. }
  1239. #endif
  1240. //-----------------------------------------------------------------------------
  1241. // Get our device name
  1242. //-----------------------------------------------------------------------------
  1243. const char *CAudioPS3LibAudio::DeviceName( void )
  1244. {
  1245. if ( m_bSurround )
  1246. return "PS3 libaudio: 7.1 Channel Surround";
  1247. else
  1248. return "PS3 libaudio: Stereo";
  1249. }
  1250. int CAudioPS3LibAudio::GetOutputPosition( void )
  1251. {
  1252. return m_nTotalBlocksPlayed * CELL_AUDIO_BLOCK_SAMPLES; // makes this effectively a 24-bit number, but the higher level code expects a count of samples, not sample blocks.
  1253. }
  1254. #ifdef PS3_SUPPORT_XVOICE
  1255. ConVar voice_xplay_enable( "voice_xplay_enable", "1" );
  1256. ConVar voice_xplay_debug( "voice_xplay_debug", "0" );
  1257. ConVar voice_xplay_bandwidth_debug( "voice_xplay_bandwidth_debug", "0" );
  1258. ConVar voice_xplay_echo( "voice_xplay_echo", "0" );
  1259. CEngineVoicePs3::CEngineVoicePs3() :
  1260. m_memContainer( SYS_MEMORY_CONTAINER_ID_INVALID )
  1261. {
  1262. }
  1263. //-----------------------------------------------------------------------------
  1264. // Initialize Voice
  1265. //-----------------------------------------------------------------------------
  1266. void CEngineVoicePs3::VoiceInit( void )
  1267. {
  1268. memset( m_bUserRegistered, kVoiceNotInitialized, sizeof( m_bUserRegistered ) );
  1269. // Reset voice data for all ctrlrs
  1270. for ( int k = 0; k < m_numVoiceUsers; ++ k )
  1271. {
  1272. VoiceResetLocalData( k );
  1273. }
  1274. }
  1275. void CEngineVoicePs3::VoiceShutdown( void )
  1276. {
  1277. RemoveAllTalkers(); // this should shutdown the voice engine on PS3
  1278. }
  1279. void CEngineVoicePs3::AddPlayerToVoiceList( XUID xPlayer, int iController, uint64 uiFlags )
  1280. {
  1281. if ( !voice_xplay_enable.GetBool() )
  1282. return;
  1283. // This should only happen if we are not initialized and matchmaking
  1284. // is in an online session
  1285. if ( XBX_GetNumGameUsers() != 1 )
  1286. return;
  1287. if ( !g_pMatchFramework )
  1288. return;
  1289. if ( !g_pMatchFramework->GetMatchSession() )
  1290. return;
  1291. if ( Q_stricmp( g_pMatchFramework->GetMatchSession()->GetSessionSettings()->GetString( "system/network" ), "LIVE" ) )
  1292. return;
  1293. // Any connecting player initializes the voice system
  1294. if ( m_bUserRegistered[ GetVoiceUserIndex( iController ) ] == kVoiceNotInitialized )
  1295. {
  1296. //
  1297. // Initialize syslib
  1298. //
  1299. int res = cellSysmoduleLoadModule( CELL_SYSMODULE_VOICE );
  1300. if ( res != CELL_OK )
  1301. {
  1302. Warning( "VOICE PS3: Failed to load libvoice! Error code %d\n", res );
  1303. return;
  1304. }
  1305. CellVoiceInitParam cvip;
  1306. Q_memset( &cvip, 0, sizeof( cvip ) );
  1307. cvip.appType = CELLVOICE_APPTYPE_GAME_1MB;
  1308. cvip.version = CELLVOICE_VERSION_100;
  1309. res = cellVoiceInitEx( &cvip );
  1310. if ( res != CELL_OK )
  1311. {
  1312. Warning( "VOICE PS3: Failed to cellVoiceInit! Error code %d\n", res );
  1313. cellSysmoduleUnloadModule( CELL_SYSMODULE_VOICE );
  1314. return;
  1315. }
  1316. // Otherwise we have successfully initialized syslib
  1317. m_bUserRegistered[ GetVoiceUserIndex( iController ) ] = kVoiceInit;
  1318. DevMsg( "PS3 Voice: microphone library loaded and started\n" );
  1319. // Create ports
  1320. res = CreateVoicePortsLocal( uiFlags );
  1321. if ( res < 0 )
  1322. {
  1323. Warning( "VOICE PS3: Failed to create voice ports! Error code %d\n", res );
  1324. cellVoiceEnd();
  1325. cellSysmoduleUnloadModule( CELL_SYSMODULE_VOICE );
  1326. m_bUserRegistered[ GetVoiceUserIndex( iController ) ] = kVoiceNotInitialized;
  1327. }
  1328. }
  1329. if ( xPlayer )
  1330. {
  1331. if ( m_bUserRegistered[ GetVoiceUserIndex( iController ) ] < kVoiceOpen )
  1332. {
  1333. DevWarning( "VOICE PS3: Cannot add remote talkers since voice system is not initialized (state %d)\n", m_bUserRegistered[ GetVoiceUserIndex( iController ) ] );
  1334. Assert( 0 );
  1335. return;
  1336. }
  1337. for ( int k = 0; k < m_arrRemoteTalkers.Count(); ++ k )
  1338. {
  1339. if ( m_arrRemoteTalkers[k].m_xuid == xPlayer )
  1340. return;
  1341. }
  1342. RemoteTalker_t rt = { xPlayer, 0, uiFlags, 0 };
  1343. int res = CreateVoicePortsRemote( rt );
  1344. if ( res < 0 )
  1345. {
  1346. DevWarning( "VOICE PS3: Cannot add remote talker %llx! Failed to create ports, error %d\n", xPlayer, res );
  1347. return;
  1348. }
  1349. m_arrRemoteTalkers.AddToTail( rt );
  1350. DevMsg( "VOICE PS3: added remote talker %llx\n", xPlayer );
  1351. }
  1352. }
  1353. int CEngineVoicePs3::CreateVoicePortsLocal( uint64 uiFlags )
  1354. {
  1355. int res = CELL_OK;
  1356. CellVoicePortParam pp;
  1357. Q_memset( &pp, 0, sizeof( pp ) );
  1358. pp.threshold = 100;
  1359. pp.volume = 1.0f;
  1360. // MIC > [IMic]
  1361. pp.portType = CELLVOICE_PORTTYPE_IN_MIC;
  1362. pp.device.playerId = 0;
  1363. res = cellVoiceCreatePort( &m_portIMic, &pp );
  1364. if ( res < 0 ) return res;
  1365. // MIC > [IMic] > [OVoice]
  1366. pp.portType = CELLVOICE_PORTTYPE_OUT_VOICE;
  1367. pp.voice.bitrate = CELLVOICE_BITRATE_7300;
  1368. res = cellVoiceCreatePort( &m_portOVoice, &pp );
  1369. if ( res < 0 ) return res;
  1370. res = cellVoiceConnectIPortToOPort( m_portIMic, m_portOVoice );
  1371. if ( res < 0 ) return res;
  1372. #ifndef SOUND_PC_CELP_SUPPORTED
  1373. // PCM output
  1374. pp.portType = CELLVOICE_PORTTYPE_OUT_PCMAUDIO;
  1375. pp.pcmaudio.bufSize = 8*1024;
  1376. pp.pcmaudio.format.numChannels = 1;
  1377. pp.pcmaudio.format.sampleAlignment = 1;
  1378. pp.pcmaudio.format.dataType = CELLVOICE_PCM_SHORT_LITTLE_ENDIAN;
  1379. pp.pcmaudio.format.sampleRate = CELLVOICE_SAMPLINGRATE_16000;
  1380. res = cellVoiceCreatePort( &m_portOPcm, &pp );
  1381. if ( res < 0 ) return res;
  1382. res = cellVoiceConnectIPortToOPort( m_portIMic, m_portOPcm );
  1383. if ( res < 0 ) return res;
  1384. #endif
  1385. // Remote > [OEarphone]
  1386. pp.portType = CELLVOICE_PORTTYPE_OUT_SECONDARY;
  1387. pp.device.playerId = 0;
  1388. res = cellVoiceCreatePort( &m_portOEarphone, &pp );
  1389. if ( res < 0 ) return res;
  1390. // IVoiceEcho
  1391. pp.portType = CELLVOICE_PORTTYPE_IN_VOICE;
  1392. pp.voice.bitrate = CELLVOICE_BITRATE_7300;
  1393. res = cellVoiceCreatePort( &m_portIVoiceEcho, &pp );
  1394. if ( res < 0 ) return res;
  1395. res = cellVoiceConnectIPortToOPort( m_portIVoiceEcho, m_portOEarphone );
  1396. if ( res < 0 ) return res;
  1397. // Start
  1398. res = sys_memory_container_create( &m_memContainer, 1024*1024 );
  1399. if ( res < 0 ) return res;
  1400. CellVoiceStartParam cvsp;
  1401. Q_memset( &cvsp, 0, sizeof( cvsp ) );
  1402. cvsp.container = m_memContainer;
  1403. res = cellVoiceStartEx( &cvsp );
  1404. if ( ( res < 0 ) && ( m_memContainer != SYS_MEMORY_CONTAINER_ID_INVALID ) )
  1405. {
  1406. sys_memory_container_destroy( m_memContainer );
  1407. m_memContainer = SYS_MEMORY_CONTAINER_ID_INVALID;
  1408. }
  1409. if ( res < 0 ) return res;
  1410. m_portOSendForRemote = m_portOVoice;
  1411. m_bUserRegistered[ GetVoiceUserIndex( XBX_GetPrimaryUserId() ) ] = kVoiceOpen;
  1412. return res;
  1413. }
  1414. int CEngineVoicePs3::CreateVoicePortsRemote( RemoteTalker_t &rt )
  1415. {
  1416. VoiceResetLocalData( XBX_GetPrimaryUserId() );
  1417. CellVoicePortParam pp;
  1418. Q_memset( &pp, 0, sizeof( pp ) );
  1419. pp.threshold = 100;
  1420. pp.volume = 1.0f;
  1421. #ifndef SOUND_PC_CELP_SUPPORTED
  1422. if ( !( rt.m_uiFlags & ENGINE_VOICE_FLAG_PS3 ) )
  1423. {
  1424. pp.portType = CELLVOICE_PORTTYPE_IN_PCMAUDIO;
  1425. pp.threshold = 0; // can't have PCM threshold (needs huge buffer)
  1426. pp.pcmaudio.bufSize = 8 * 1024;
  1427. pp.pcmaudio.format.numChannels = 1;
  1428. pp.pcmaudio.format.sampleAlignment = 1;
  1429. pp.pcmaudio.format.dataType = CELLVOICE_PCM_SHORT_LITTLE_ENDIAN;
  1430. pp.pcmaudio.format.sampleRate = CELLVOICE_SAMPLINGRATE_16000;
  1431. m_portOSendForRemote = m_portOPcm;
  1432. }
  1433. else
  1434. #endif
  1435. {
  1436. pp.portType = CELLVOICE_PORTTYPE_IN_VOICE;
  1437. pp.voice.bitrate = CELLVOICE_BITRATE_7300;
  1438. m_portOSendForRemote = m_portOVoice;
  1439. }
  1440. int res = cellVoiceCreatePort( &rt.m_portIRemoteVoice, &pp );
  1441. if ( res < 0 ) return res;
  1442. res = cellVoiceConnectIPortToOPort( rt.m_portIRemoteVoice, m_portOEarphone );
  1443. if ( res < 0 )
  1444. {
  1445. cellVoiceDeletePort( rt.m_portIRemoteVoice );
  1446. }
  1447. return res;
  1448. }
  1449. void CEngineVoicePs3::RemovePlayerFromVoiceList( XUID xPlayer, int iController )
  1450. {
  1451. if ( !m_bUserRegistered[ GetVoiceUserIndex( iController ) ] )
  1452. return;
  1453. if ( xPlayer )
  1454. {
  1455. // Find the remote player in our list
  1456. for ( int k = 0; k < m_arrRemoteTalkers.Count(); ++ k )
  1457. {
  1458. if ( m_arrRemoteTalkers[k].m_xuid == xPlayer )
  1459. {
  1460. cellVoiceDisconnectIPortFromOPort( m_arrRemoteTalkers[k].m_portIRemoteVoice, m_portOEarphone );
  1461. cellVoiceDeletePort( m_arrRemoteTalkers[k].m_portIRemoteVoice );
  1462. m_arrRemoteTalkers.Remove( k );
  1463. DevMsg( "VOICE PS3: removed remote talker %llx\n", xPlayer );
  1464. return;
  1465. }
  1466. }
  1467. DevWarning( "CEngineVoicePs3::RemovePlayerFromVoiceList for unregistered remote talker %llx!\n", xPlayer );
  1468. return;
  1469. }
  1470. // Local player removed shuts down the whole voice system
  1471. cellVoiceEnd();
  1472. cellSysmoduleUnloadModule( CELL_SYSMODULE_VOICE );
  1473. if ( m_memContainer != SYS_MEMORY_CONTAINER_ID_INVALID )
  1474. {
  1475. sys_memory_container_destroy( m_memContainer );
  1476. m_memContainer = SYS_MEMORY_CONTAINER_ID_INVALID;
  1477. }
  1478. m_bUserRegistered[ GetVoiceUserIndex( iController ) ] = kVoiceNotInitialized;
  1479. DevMsg( "PS3 Voice: microphone library stopped and unloaded\n" );
  1480. m_arrRemoteTalkers.Purge();
  1481. }
  1482. void CEngineVoicePs3::PlayIncomingVoiceData( XUID xuid, const byte *pbData, unsigned int dwDataSize, const bool *bAudiblePlayers )
  1483. {
  1484. if ( !voice_xplay_enable.GetBool() )
  1485. return;
  1486. if ( m_bUserRegistered[ GetVoiceUserIndex( XBX_GetPrimaryUserId() ) ] < kVoiceOpen )
  1487. return;
  1488. for ( DWORD dwSlot = 0; dwSlot < XBX_GetNumGameUsers(); ++ dwSlot )
  1489. {
  1490. int iCtrlr = XBX_GetUserId( dwSlot );
  1491. IPlayerLocal *pPlayer = g_pMatchFramework->GetMatchSystem()->GetPlayerManager()->GetLocalPlayer( iCtrlr );
  1492. if ( pPlayer && pPlayer->GetXUID() == xuid )
  1493. {
  1494. //Hack: Don't play stuff that comes from ourselves.
  1495. if ( voice_xplay_echo.GetBool() && ( m_portOSendForRemote == m_portOVoice ) )
  1496. {
  1497. cellVoiceWriteToIPort( m_portIVoiceEcho, pbData, &dwDataSize );
  1498. }
  1499. return;
  1500. }
  1501. }
  1502. if ( g_pMatchFramework->GetMatchSystem()->GetMatchVoice()->CanPlaybackTalker( xuid ) )
  1503. {
  1504. // Set the playback priority for talkers that we can't hear due to game rules.
  1505. for ( DWORD dwSlot = 0; dwSlot < XBX_GetNumGameUsers(); ++dwSlot )
  1506. {
  1507. bool bCanHearPlayer = !bAudiblePlayers || bAudiblePlayers[dwSlot];
  1508. if ( voice_xplay_debug.GetBool() )
  1509. {
  1510. DevMsg( "XVoiceDebug: %llx -> %d = %s\n", xuid, XBX_GetUserId( dwSlot ), bCanHearPlayer ? "yes" : "no" );
  1511. }
  1512. // m_pXHVEngine->SetPlaybackPriority( xuid, XBX_GetUserId( dwSlot ), bCanHearPlayer ? XHV_PLAYBACK_PRIORITY_MAX : XHV_PLAYBACK_PRIORITY_NEVER );
  1513. }
  1514. }
  1515. else
  1516. {
  1517. // Cannot submit voice for the player that we are not allowed to playback!
  1518. if ( voice_xplay_debug.GetBool() )
  1519. {
  1520. DevMsg( "XVoiceDebug: %llx muted\n", xuid );
  1521. }
  1522. return;
  1523. }
  1524. //
  1525. // Save incoming stream
  1526. //
  1527. for ( int k = 0; k < m_arrRemoteTalkers.Count(); ++ k )
  1528. {
  1529. if ( m_arrRemoteTalkers[k].m_xuid == xuid )
  1530. {
  1531. cellVoiceWriteToIPort( m_arrRemoteTalkers[k].m_portIRemoteVoice, pbData, &dwDataSize );
  1532. m_arrRemoteTalkers[k].m_flLastTalkTimestamp = Plat_FloatTime();
  1533. return;
  1534. }
  1535. }
  1536. }
  1537. void CEngineVoicePs3::PlayPortInterruptHandler()
  1538. {
  1539. }
  1540. void CEngineVoicePs3::UpdateHUDVoiceStatus( void )
  1541. {
  1542. for ( int iClient = 0; iClient < GetBaseLocalClient().m_nMaxClients; iClient++ )
  1543. {
  1544. // Information about local client if it's a local client speaking
  1545. bool bLocalClient = false;
  1546. int iSsSlot = -1;
  1547. int iCtrlr = -1;
  1548. // Detection if it's a local client
  1549. for ( DWORD k = 0; k < XBX_GetNumGameUsers(); ++ k )
  1550. {
  1551. CClientState &cs = GetLocalClient( k );
  1552. if ( cs.m_nPlayerSlot == iClient )
  1553. {
  1554. bLocalClient = true;
  1555. iSsSlot = k;
  1556. iCtrlr = XBX_GetUserId( iSsSlot );
  1557. }
  1558. }
  1559. // Convert into index and XUID
  1560. int iIndex = iClient + 1;
  1561. XUID xid = NULL;
  1562. player_info_t infoClient;
  1563. if ( engineClient->GetPlayerInfo( iIndex, &infoClient ) )
  1564. {
  1565. xid = infoClient.xuid;
  1566. }
  1567. if ( !xid )
  1568. // No XUID means no VOIP
  1569. {
  1570. g_pSoundServices->OnChangeVoiceStatus( iIndex, -1, false );
  1571. if ( bLocalClient )
  1572. g_pSoundServices->OnChangeVoiceStatus( iIndex, iSsSlot, false );
  1573. continue;
  1574. }
  1575. // Determine talking status
  1576. bool bTalking = false;
  1577. if ( bLocalClient )
  1578. {
  1579. //Make sure the player's own "remote" label is not on.
  1580. g_pSoundServices->OnChangeVoiceStatus( iIndex, -1, false );
  1581. iIndex = -1; // issue notification as ent=-1
  1582. bTalking = IsLocalPlayerTalking( iCtrlr );
  1583. }
  1584. else
  1585. {
  1586. bTalking = IsPlayerTalking( xid );
  1587. }
  1588. g_pSoundServices->OnChangeVoiceStatus( iIndex, iSsSlot, bTalking );
  1589. }
  1590. }
  1591. bool CEngineVoicePs3::VoiceUpdateData( int iCtrlr )
  1592. {
  1593. int32 dwBytes = 0;
  1594. int32 wVoiceBytes = 0;
  1595. bool bShouldSend = false;
  1596. //Update UI stuff.
  1597. UpdateHUDVoiceStatus();
  1598. if ( m_bUserRegistered[ GetVoiceUserIndex( iCtrlr ) ] < kVoiceOpen )
  1599. return false;
  1600. if ( 1 )
  1601. {
  1602. int i = GetVoiceUserIndex( iCtrlr );
  1603. dwBytes = m_ChatBufferSize - m_wLocalDataSize[i];
  1604. if( dwBytes < ( m_ChatBufferSize/10 ) )
  1605. {
  1606. bShouldSend = true;
  1607. }
  1608. else
  1609. {
  1610. int res = cellVoiceReadFromOPort( m_portOSendForRemote, m_ChatBuffer[i] + m_wLocalDataSize[i], (uint32*) &dwBytes );
  1611. if ( ( res >= 0 ) && ( dwBytes > 0 ) )
  1612. {
  1613. m_wLocalDataSize[i] += ( dwBytes ) & 0xFFFF;
  1614. wVoiceBytes += ( dwBytes ) & 0xFFFF;
  1615. if( m_wLocalDataSize[i] >= 64 ) // voice buffer enough size that it should get sent
  1616. {
  1617. bShouldSend = true;
  1618. }
  1619. }
  1620. }
  1621. }
  1622. return bShouldSend ||
  1623. ( wVoiceBytes &&
  1624. ( Plat_FloatTime() - m_dwLastVoiceSend[ GetVoiceUserIndex( iCtrlr ) ] ) > MAX_VOICE_BUFFER_TIME );
  1625. }
  1626. void CEngineVoicePs3::SetPlaybackPriority( XUID remoteTalker, int iController, int iAllowPlayback )
  1627. {
  1628. // No muting support in voice engine
  1629. }
  1630. void CEngineVoicePs3::GetRemoteTalkers( int *pNumTalkers, XUID *pRemoteTalkers )
  1631. {
  1632. if ( pNumTalkers )
  1633. *pNumTalkers = m_arrRemoteTalkers.Count();
  1634. if ( pRemoteTalkers )
  1635. {
  1636. for ( int k = 0; k < m_arrRemoteTalkers.Count(); ++ k )
  1637. pRemoteTalkers[k] = m_arrRemoteTalkers[k].m_xuid;
  1638. }
  1639. }
  1640. void CEngineVoicePs3::GetVoiceData( int iController, const byte **ppvVoiceDataBuffer, unsigned int *pnumVoiceDataBytes )
  1641. {
  1642. iController = GetVoiceUserIndex( iController );
  1643. *pnumVoiceDataBytes = m_wLocalDataSize[ iController ];
  1644. *ppvVoiceDataBuffer = m_ChatBuffer[ iController ];
  1645. }
  1646. void CEngineVoicePs3::GetVoiceData( int iCtrlr, CCLCMsg_VoiceData_t *pMessage )
  1647. {
  1648. IPlayerLocal *pPlayer = g_pMatchFramework->GetMatchSystem()->GetPlayerManager()->GetLocalPlayer( iCtrlr );
  1649. pMessage->set_xuid( pPlayer ? pPlayer->GetXUID() : 0ull );
  1650. if ( !pMessage->xuid() )
  1651. return;
  1652. int iController = GetVoiceUserIndex( iCtrlr );
  1653. if ( !m_wLocalDataSize[ iController ] )
  1654. return;
  1655. pMessage->set_data( m_ChatBuffer[ iController ], m_wLocalDataSize[ iController ] );
  1656. }
  1657. void CEngineVoicePs3::VoiceSendData( int iCtrlr, INetChannel *pChannel )
  1658. {
  1659. if ( !pChannel )
  1660. return;
  1661. CCLCMsg_VoiceData_t voiceMsg;
  1662. GetVoiceData( iCtrlr, &voiceMsg );
  1663. pChannel->SendNetMsg( voiceMsg, false, true );
  1664. VoiceResetLocalData( iCtrlr );
  1665. }
  1666. void CEngineVoicePs3::VoiceResetLocalData( int iCtrlr )
  1667. {
  1668. iCtrlr = GetVoiceUserIndex( iCtrlr );
  1669. if ( voice_xplay_bandwidth_debug.GetBool() && m_wLocalDataSize[iCtrlr] )
  1670. {
  1671. DevMsg( "PS3 Voice: microphone stream %0.2fKb\n", m_wLocalDataSize[iCtrlr]/1024.0f );
  1672. }
  1673. m_dwLastVoiceSend[ iCtrlr ] = Plat_FloatTime();
  1674. Q_memset( m_ChatBuffer[ iCtrlr ], 0, m_ChatBufferSize );
  1675. m_wLocalDataSize[ iCtrlr ] = 0;
  1676. }
  1677. #pragma warning(push) // warning C4800 is meaningless or worse
  1678. #pragma warning( disable: 4800 )
  1679. bool CEngineVoicePs3::IsLocalPlayerTalking( int controllerID )
  1680. {
  1681. controllerID = GetVoiceUserIndex( controllerID );
  1682. return ( m_wLocalDataSize[controllerID] > 0 ) || ( Plat_FloatTime() - m_dwLastVoiceSend[controllerID] <= MAX_VOICE_BUFFER_TIME );
  1683. }
  1684. bool CEngineVoicePs3::IsPlayerTalking( XUID uid )
  1685. {
  1686. if ( !g_pMatchFramework->GetMatchSystem()->GetMatchVoice()->CanPlaybackTalker( uid ) )
  1687. return false;
  1688. for ( int k = 0; k < m_arrRemoteTalkers.Count(); ++ k )
  1689. {
  1690. if ( m_arrRemoteTalkers[k].m_xuid == uid )
  1691. {
  1692. return ( ( Plat_FloatTime() - m_arrRemoteTalkers[k].m_flLastTalkTimestamp ) < 0.2 );
  1693. }
  1694. }
  1695. return false;
  1696. }
  1697. bool CEngineVoicePs3::IsHeadsetPresent( int id )
  1698. {
  1699. return ( m_bUserRegistered[ GetVoiceUserIndex( id ) ] >= kVoiceInit );
  1700. }
  1701. #pragma warning(pop)
  1702. void CEngineVoicePs3::RemoveAllTalkers()
  1703. {
  1704. RemovePlayerFromVoiceList( NULL, XBX_GetPrimaryUserId() ); // there's only one user possibly registered, unregister it
  1705. }
  1706. CEngineVoicePs3 *Audio_GetXVoice( void )
  1707. {
  1708. if ( CAudioPS3LibAudio::m_pSingleton )
  1709. {
  1710. return CAudioPS3LibAudio::m_pSingleton->GetVoiceData();
  1711. }
  1712. return NULL;
  1713. }
  1714. EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CEngineVoicePs3, IEngineVoice,
  1715. IENGINEVOICE_INTERFACE_VERSION, *Audio_GetXVoice() );
  1716. #endif