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.

481 lines
12 KiB

  1. //===== Copyright (c) 1996-2010, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. //===========================================================================//
  6. #include "audio_pch.h"
  7. #include <AudioToolbox/AudioQueue.h>
  8. #include <AudioToolbox/AudioFile.h>
  9. #include <AudioToolbox/AudioFormat.h>
  10. // memdbgon must be the last include file in a .cpp file!!!
  11. #include "tier0/memdbgon.h"
  12. extern bool snd_firsttime;
  13. extern bool MIX_ScaleChannelVolume( paintbuffer_t *ppaint, channel_t *pChannel, int volume[CCHANVOLUMES], int mixchans );
  14. #define NUM_BUFFERS_SOURCES 128
  15. #define BUFF_MASK (NUM_BUFFERS_SOURCES - 1 )
  16. #define BUFFER_SIZE 0x0400
  17. //-----------------------------------------------------------------------------
  18. //
  19. // NOTE: This only allows 16-bit, stereo wave out
  20. //
  21. //-----------------------------------------------------------------------------
  22. class CAudioDeviceAudioQueue : public CAudioDeviceBase
  23. {
  24. public:
  25. CAudioDeviceAudioQueue()
  26. {
  27. m_pName = "AudioQueue";
  28. m_nChannels = 2;
  29. m_nSampleBits = 16;
  30. m_nSampleRate = 44100;
  31. m_bIsActive = true;
  32. }
  33. bool IsActive( void );
  34. bool Init( void );
  35. void Shutdown( void );
  36. int GetOutputPosition( void );
  37. void Pause( void );
  38. void UnPause( void );
  39. int64 PaintBegin( float mixAheadTime, int64 soundtime, int64 paintedtime );
  40. void PaintEnd( void );
  41. void ClearBuffer( void );
  42. void UpdateListener( const Vector& position, const Vector& forward, const Vector& right, const Vector& up );
  43. void TransferSamples( int end );
  44. int DeviceSampleCount( void ) { return m_deviceSampleCount; }
  45. void BufferCompleted() { m_buffersCompleted++; }
  46. void SetRunning( bool bState ) { m_bRunning = bState; }
  47. private:
  48. void OpenWaveOut( void );
  49. void CloseWaveOut( void );
  50. bool ValidWaveOut( void ) const;
  51. bool BIsPlaying();
  52. AudioStreamBasicDescription m_DataFormat;
  53. AudioQueueRef m_Queue;
  54. AudioQueueBufferRef m_Buffers[NUM_BUFFERS_SOURCES];
  55. int m_SndBufSize;
  56. void *m_sndBuffers;
  57. CInterlockedInt m_deviceSampleCount;
  58. int m_buffersSent;
  59. int m_buffersCompleted;
  60. int m_pauseCount;
  61. bool m_bSoundsShutdown;
  62. bool m_bFailed;
  63. bool m_bRunning;
  64. bool m_bSurround;
  65. bool m_bSurroundCenter;
  66. bool m_bHeadphone;
  67. };
  68. CAudioDeviceAudioQueue *wave = NULL;
  69. static void AudioCallback(void *pContext, AudioQueueRef pQueue, AudioQueueBufferRef pBuffer)
  70. {
  71. if ( wave )
  72. wave->BufferCompleted();
  73. }
  74. IAudioDevice *Audio_CreateMacAudioQueueDevice( void )
  75. {
  76. wave = new CAudioDeviceAudioQueue;
  77. if ( wave->Init() )
  78. return wave;
  79. delete wave;
  80. wave = NULL;
  81. return NULL;
  82. }
  83. void OnSndSurroundCvarChanged2( IConVar *pVar, const char *pOldString, float flOldValue );
  84. void OnSndSurroundLegacyChanged2( IConVar *pVar, const char *pOldString, float flOldValue );
  85. //-----------------------------------------------------------------------------
  86. // Init, shutdown
  87. //-----------------------------------------------------------------------------
  88. bool CAudioDeviceAudioQueue::Init( void )
  89. {
  90. m_SndBufSize = 0;
  91. m_sndBuffers = NULL;
  92. m_pauseCount = 0;
  93. m_bIsHeadphone = false;
  94. m_buffersSent = 0;
  95. m_buffersCompleted = 0;
  96. m_pauseCount = 0;
  97. m_bSoundsShutdown = false;
  98. m_bFailed = false;
  99. m_bRunning = false;
  100. m_Queue = NULL;
  101. static bool first = true;
  102. if ( first )
  103. {
  104. snd_surround.SetValue( 2 );
  105. snd_surround.InstallChangeCallback( &OnSndSurroundCvarChanged2 );
  106. snd_legacy_surround.InstallChangeCallback( &OnSndSurroundLegacyChanged2 );
  107. first = false;
  108. }
  109. OpenWaveOut();
  110. if ( snd_firsttime )
  111. {
  112. DevMsg( "Wave sound initialized\n" );
  113. }
  114. return ValidWaveOut() && !m_bFailed;
  115. }
  116. void CAudioDeviceAudioQueue::Shutdown( void )
  117. {
  118. CloseWaveOut();
  119. }
  120. //-----------------------------------------------------------------------------
  121. // WAV out device
  122. //-----------------------------------------------------------------------------
  123. inline bool CAudioDeviceAudioQueue::ValidWaveOut( void ) const
  124. {
  125. return m_sndBuffers != 0 && m_Queue;
  126. }
  127. //-----------------------------------------------------------------------------
  128. // called by the mac audioqueue code when we run out of playback buffers
  129. //-----------------------------------------------------------------------------
  130. void AudioQueueIsRunningCallback( void* inClientData, AudioQueueRef inAQ, AudioQueuePropertyID inID)
  131. {
  132. CAudioDeviceAudioQueue* audioqueue = (CAudioDeviceAudioQueue*)inClientData;
  133. UInt32 running = 0;
  134. UInt32 size;
  135. OSStatus err = AudioQueueGetProperty(inAQ, kAudioQueueProperty_IsRunning, &running, &size);
  136. audioqueue->SetRunning( running != 0 );
  137. //DevWarning( "AudioQueueStart %d\n", running );
  138. }
  139. //-----------------------------------------------------------------------------
  140. // Opens the windows wave out device
  141. //-----------------------------------------------------------------------------
  142. void CAudioDeviceAudioQueue::OpenWaveOut( void )
  143. {
  144. if ( m_Queue )
  145. return;
  146. m_buffersSent = 0;
  147. m_buffersCompleted = 0;
  148. m_DataFormat.mSampleRate = SOUND_DMA_SPEED;
  149. m_DataFormat.mFormatID = kAudioFormatLinearPCM;
  150. m_DataFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger|kAudioFormatFlagIsPacked;
  151. m_DataFormat.mBytesPerPacket = 4; // 16-bit samples * 2 channels
  152. m_DataFormat.mFramesPerPacket = 1;
  153. m_DataFormat.mBytesPerFrame = 4; // 16-bit samples * 2 channels
  154. m_DataFormat.mChannelsPerFrame = 2;
  155. m_DataFormat.mBitsPerChannel = 16;
  156. m_DataFormat.mReserved = 0;
  157. // Create the audio queue that will be used to manage the array of audio
  158. // buffers used to queue samples.
  159. OSStatus err = AudioQueueNewOutput(&m_DataFormat, AudioCallback, this, NULL, NULL, 0, &m_Queue);
  160. if ( err != noErr)
  161. {
  162. DevMsg( "Failed to create AudioQueue output %d\n", err );
  163. m_bFailed = true;
  164. return;
  165. }
  166. for ( int i = 0; i < NUM_BUFFERS_SOURCES; ++i)
  167. {
  168. err = AudioQueueAllocateBuffer( m_Queue, BUFFER_SIZE,&(m_Buffers[i]));
  169. if ( err != noErr)
  170. {
  171. DevMsg( "Failed to AudioQueueAllocateBuffer output %d (%i)\n", err,i );
  172. m_bFailed = true;
  173. }
  174. m_Buffers[i]->mAudioDataByteSize = BUFFER_SIZE;
  175. Q_memset( m_Buffers[i]->mAudioData, 0, BUFFER_SIZE );
  176. }
  177. err = AudioQueuePrime( m_Queue, 0, NULL);
  178. if ( err != noErr)
  179. {
  180. DevMsg( "Failed to create AudioQueue output %d\n", err );
  181. m_bFailed = true;
  182. return;
  183. }
  184. AudioQueueSetParameter( m_Queue, kAudioQueueParam_Volume, 1.0);
  185. err = AudioQueueAddPropertyListener( m_Queue, kAudioQueueProperty_IsRunning, AudioQueueIsRunningCallback, this );
  186. if ( err != noErr)
  187. {
  188. DevMsg( "Failed to create AudioQueue output %d\n", err );
  189. m_bFailed = true;
  190. return;
  191. }
  192. m_SndBufSize = NUM_BUFFERS_SOURCES*BUFFER_SIZE;
  193. m_deviceSampleCount = m_SndBufSize / DeviceSampleBytes();
  194. if ( !m_sndBuffers )
  195. {
  196. m_sndBuffers = malloc( m_SndBufSize );
  197. memset( m_sndBuffers, 0x0, m_SndBufSize );
  198. }
  199. }
  200. //-----------------------------------------------------------------------------
  201. // Closes the windows wave out device
  202. //-----------------------------------------------------------------------------
  203. void CAudioDeviceAudioQueue::CloseWaveOut( void )
  204. {
  205. if ( ValidWaveOut() )
  206. {
  207. AudioQueueStop(m_Queue, true);
  208. m_bRunning = false;
  209. AudioQueueRemovePropertyListener( m_Queue, kAudioQueueProperty_IsRunning, AudioQueueIsRunningCallback, this );
  210. for ( int i = 0; i < NUM_BUFFERS_SOURCES; i++ )
  211. AudioQueueFreeBuffer( m_Queue, m_Buffers[i]);
  212. AudioQueueDispose( m_Queue, true);
  213. m_Queue = NULL;
  214. }
  215. if ( m_sndBuffers )
  216. {
  217. free( m_sndBuffers );
  218. m_sndBuffers = NULL;
  219. }
  220. }
  221. //-----------------------------------------------------------------------------
  222. // Mixing setup
  223. //-----------------------------------------------------------------------------
  224. int64 CAudioDeviceAudioQueue::PaintBegin( float mixAheadTime, int64 soundtime, int64 paintedtime )
  225. {
  226. // soundtime - total samples that have been played out to hardware at dmaspeed
  227. // paintedtime - total samples that have been mixed at speed
  228. // endtime - target for samples in mixahead buffer at speed
  229. int64 endtime = soundtime + mixAheadTime * SampleRate();
  230. int samps = DeviceSampleCount() >> (ChannelCount()-1);
  231. if ((int)(endtime - soundtime) > samps)
  232. endtime = soundtime + samps;
  233. if ((endtime - paintedtime) & 0x3)
  234. {
  235. // The difference between endtime and painted time should align on
  236. // boundaries of 4 samples. This is important when upsampling from 11khz -> 44khz.
  237. endtime -= (endtime - paintedtime) & 0x3;
  238. }
  239. return endtime;
  240. }
  241. //-----------------------------------------------------------------------------
  242. // Actually performs the mixing
  243. //-----------------------------------------------------------------------------
  244. void CAudioDeviceAudioQueue::PaintEnd( void )
  245. {
  246. int cblocks = 8 << 1;
  247. //
  248. // submit a few new sound blocks
  249. //
  250. // 44K sound support
  251. while (((m_buffersSent - m_buffersCompleted) >> SAMPLE_16BIT_SHIFT) < cblocks)
  252. {
  253. int iBuf = m_buffersSent&BUFF_MASK;
  254. m_Buffers[iBuf]->mAudioDataByteSize = BUFFER_SIZE;
  255. Q_memcpy( m_Buffers[iBuf]->mAudioData, (char *)m_sndBuffers + iBuf*BUFFER_SIZE, BUFFER_SIZE);
  256. // Queue the buffer for playback.
  257. OSStatus err = AudioQueueEnqueueBuffer( m_Queue, m_Buffers[iBuf], 0, NULL);
  258. if ( err != noErr)
  259. {
  260. DevMsg( "Failed to AudioQueueEnqueueBuffer output %d\n", err );
  261. }
  262. m_buffersSent++;
  263. }
  264. if ( !m_bRunning )
  265. {
  266. DevMsg( "Restarting sound playback\n" );
  267. m_bRunning = true;
  268. AudioQueueStart( m_Queue, NULL);
  269. }
  270. }
  271. int CAudioDeviceAudioQueue::GetOutputPosition( void )
  272. {
  273. int s = m_buffersSent * BUFFER_SIZE;
  274. s >>= SAMPLE_16BIT_SHIFT;
  275. s &= (DeviceSampleCount()-1);
  276. return s / ChannelCount();
  277. }
  278. //-----------------------------------------------------------------------------
  279. // Pausing
  280. //-----------------------------------------------------------------------------
  281. void CAudioDeviceAudioQueue::Pause( void )
  282. {
  283. m_pauseCount++;
  284. if (m_pauseCount == 1)
  285. {
  286. m_bRunning = false;
  287. AudioQueueStop(m_Queue, true);
  288. }
  289. }
  290. void CAudioDeviceAudioQueue::UnPause( void )
  291. {
  292. if ( m_pauseCount > 0 )
  293. {
  294. m_pauseCount--;
  295. }
  296. if ( m_pauseCount == 0 )
  297. {
  298. m_bRunning = true;
  299. AudioQueueStart( m_Queue, NULL);
  300. }
  301. }
  302. bool CAudioDeviceAudioQueue::IsActive( void )
  303. {
  304. return ( m_pauseCount == 0 );
  305. }
  306. void CAudioDeviceAudioQueue::ClearBuffer( void )
  307. {
  308. if ( !m_sndBuffers )
  309. return;
  310. Q_memset( m_sndBuffers, 0x0, DeviceSampleCount() * DeviceSampleBytes() );
  311. }
  312. void CAudioDeviceAudioQueue::UpdateListener( const Vector& position, const Vector& forward, const Vector& right, const Vector& up )
  313. {
  314. }
  315. bool CAudioDeviceAudioQueue::BIsPlaying()
  316. {
  317. UInt32 isRunning;
  318. UInt32 propSize = sizeof(isRunning);
  319. OSStatus result = AudioQueueGetProperty( m_Queue, kAudioQueueProperty_IsRunning, &isRunning, &propSize);
  320. return isRunning != 0;
  321. }
  322. void CAudioDeviceAudioQueue::TransferSamples( int end )
  323. {
  324. int64 lpaintedtime = g_paintedtime;
  325. int64 endtime = end;
  326. // resumes playback...
  327. if ( m_sndBuffers )
  328. {
  329. S_TransferStereo16( m_sndBuffers, PAINTBUFFER, lpaintedtime, endtime );
  330. }
  331. }
  332. static uint32 GetOSXSpeakerConfig()
  333. {
  334. return 2;
  335. }
  336. static uint32 GetSpeakerConfigForSurroundMode( int surroundMode, const char **pConfigDesc )
  337. {
  338. uint32 newSpeakerConfig = 2;
  339. *pConfigDesc = "stereo speaker";
  340. return newSpeakerConfig;
  341. }
  342. void OnSndSurroundCvarChanged2( IConVar *pVar, const char *pOldString, float flOldValue )
  343. {
  344. // if the old value is -1, we're setting this from the detect routine for the first time
  345. // no need to reset the device
  346. if ( flOldValue == -1 )
  347. return;
  348. // get the user's previous speaker config
  349. uint32 speaker_config = GetOSXSpeakerConfig();
  350. // get the new config
  351. uint32 newSpeakerConfig = 0;
  352. const char *speakerConfigDesc = "";
  353. ConVarRef var( pVar );
  354. newSpeakerConfig = GetSpeakerConfigForSurroundMode( var.GetInt(), &speakerConfigDesc );
  355. // make sure the config has changed
  356. if (newSpeakerConfig == speaker_config)
  357. return;
  358. // set new configuration
  359. //SetWindowsSpeakerConfig(newSpeakerConfig);
  360. Msg("Speaker configuration has been changed to %s.\n", speakerConfigDesc);
  361. // restart sound system so it takes effect
  362. //g_pSoundServices->RestartSoundSystem();
  363. }
  364. void OnSndSurroundLegacyChanged2( IConVar *pVar, const char *pOldString, float flOldValue )
  365. {
  366. }