macosx_sound.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449
  1. /*
  2. ===========================================================================
  3. Doom 3 GPL Source Code
  4. Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
  5. This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
  6. Doom 3 Source Code is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. Doom 3 Source Code is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
  16. In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
  17. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
  18. ===========================================================================
  19. */
  20. #include "../../idlib/precompiled.h"
  21. #include "../../sound/snd_local.h"
  22. #include <Carbon/Carbon.h>
  23. #include <CoreAudio/CoreAudio.h>
  24. idCVar s_device( "s_device", "-1", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_INTEGER, "Sound device to use. -1 for default device" );
  25. class idAudioHardwareOSX : public idAudioHardware {
  26. public:
  27. idAudioHardwareOSX();
  28. ~idAudioHardwareOSX();
  29. bool Initialize( );
  30. // OSX driver doesn't support memory map API
  31. bool Lock( void **pDSLockedBuffer, ulong *dwDSLockedBufferSize ) { return false; }
  32. bool Unlock( void *pDSLockedBuffer, dword dwDSLockedBufferSize ) { return false; }
  33. bool GetCurrentPosition( ulong *pdwCurrentWriteCursor ) { return false; }
  34. int GetMixBufferSize( void ) { return 0; }
  35. int GetNumberOfSpeakers( void );
  36. // OSX driver doesn't support write API
  37. bool Flush( void ) { return false; }
  38. void Write( bool ) { }
  39. short* GetMixBuffer( void ) { return NULL; }
  40. private:
  41. AudioDeviceID selectedDevice;
  42. bool activeIOProc;
  43. void Reset( void );
  44. void InitFailed( void );
  45. const char* ExtractStatus( OSStatus status );
  46. void GetAvailableNominalSampleRates( void );
  47. // AudioDevicePropertyListenerProc
  48. static OSStatus DeviceListener( AudioDeviceID inDevice,
  49. UInt32 inChannel,
  50. Boolean isInput,
  51. AudioDevicePropertyID inPropertyID,
  52. void* inClientData );
  53. // AudioDeviceIOProc
  54. static OSStatus DeviceIOProc( AudioDeviceID inDevice,
  55. const AudioTimeStamp* inNow,
  56. const AudioBufferList* inInputData,
  57. const AudioTimeStamp* inInputTime,
  58. AudioBufferList* outOutputData,
  59. const AudioTimeStamp* inOutputTime,
  60. void* inClientData );
  61. };
  62. /*
  63. ==========
  64. iAudioHardware::Alloc
  65. ==========
  66. */
  67. idAudioHardware *idAudioHardware::Alloc() { return new idAudioHardwareOSX; }
  68. /*
  69. ==========
  70. idAudioHardware::~idAudioHardware
  71. ==========
  72. */
  73. idAudioHardware::~idAudioHardware() { }
  74. /*
  75. ==========
  76. idAudioHardwareOSX::idAudioHardwareOSX
  77. ==========
  78. */
  79. idAudioHardwareOSX::idAudioHardwareOSX() {
  80. selectedDevice = kAudioDeviceUnknown;
  81. activeIOProc = false;
  82. }
  83. /*
  84. ==========
  85. idAudioHardwareOSX::~idAudioHardwareOSX
  86. ==========
  87. */
  88. idAudioHardwareOSX::~idAudioHardwareOSX() {
  89. Reset();
  90. }
  91. /*
  92. ==========
  93. idAudioHardwareOSX::Reset
  94. ==========
  95. */
  96. void idAudioHardwareOSX::Reset() {
  97. OSStatus status;
  98. if ( activeIOProc ) {
  99. status = AudioDeviceStop( selectedDevice, DeviceIOProc );
  100. if ( status != kAudioHardwareNoError ) {
  101. common->Warning( "idAudioHardwareOSX::Reset: AudioDeviceStop failed. status: %s", ExtractStatus( status ) );
  102. }
  103. status = AudioDeviceRemoveIOProc( selectedDevice, DeviceIOProc );
  104. if ( status != kAudioHardwareNoError ) {
  105. common->Warning( "idAudioHardwareOSX::Reset: AudioDeviceRemoveIOProc failed. status %s\n", ExtractStatus( status ) );
  106. }
  107. activeIOProc = false;
  108. }
  109. selectedDevice = kAudioDeviceUnknown;
  110. AudioHardwareUnload();
  111. }
  112. /*
  113. =================
  114. idAudioHardwareOSX::InitFailed
  115. =================
  116. */
  117. void idAudioHardwareOSX::InitFailed() {
  118. Reset();
  119. cvarSystem->SetCVarBool( "s_noSound", true );
  120. common->Warning( "sound subsystem disabled" );
  121. common->Printf( "------------------------------------------------\n" );
  122. }
  123. /*
  124. ==========
  125. idAudioHardwareOSX::DeviceListener
  126. ==========
  127. */
  128. OSStatus idAudioHardwareOSX::DeviceListener( AudioDeviceID inDevice,
  129. UInt32 inChannel,
  130. Boolean isInput,
  131. AudioDevicePropertyID inPropertyID,
  132. void* inClientData) {
  133. common->Printf( "DeviceListener\n" );
  134. return kAudioHardwareNoError;
  135. }
  136. /*
  137. ==========
  138. idAudioHardwareOSX::DeviceIOProc
  139. ==========
  140. */
  141. OSStatus idAudioHardwareOSX::DeviceIOProc( AudioDeviceID inDevice,
  142. const AudioTimeStamp* inNow,
  143. const AudioBufferList* inInputData,
  144. const AudioTimeStamp* inInputTime,
  145. AudioBufferList* outOutputData,
  146. const AudioTimeStamp* inOutputTime,
  147. void* inClientData ) {
  148. // setup similar to async thread
  149. Sys_EnterCriticalSection();
  150. soundSystem->AsyncMix( (int)inOutputTime->mSampleTime, (float*)outOutputData->mBuffers[ 0 ].mData );
  151. Sys_LeaveCriticalSection();
  152. // doom mixes sound to -32768.0f 32768.0f range, scale down to -1.0f 1.0f
  153. SIMDProcessor->Mul( (Float32*)outOutputData->mBuffers[ 0 ].mData, 1.0f / 32768.0f, (Float32*)outOutputData->mBuffers[ 0 ].mData, MIXBUFFER_SAMPLES * 2 );
  154. return kAudioHardwareNoError;
  155. }
  156. /*
  157. ==========
  158. idAudioHardwareOSX::ExtractStatus
  159. ==========
  160. */
  161. const char* idAudioHardwareOSX::ExtractStatus( OSStatus status ) {
  162. static char buf[ sizeof( OSStatus ) + 1 ];
  163. strncpy( buf, (const char *)&status, sizeof( OSStatus ) );
  164. buf[ sizeof( OSStatus ) ] = '\0';
  165. return buf;
  166. }
  167. /*
  168. ==========
  169. idAudioHardwareOSX::Initialize
  170. ==========
  171. */
  172. bool idAudioHardwareOSX::Initialize( ) {
  173. UInt32 size;
  174. OSStatus status;
  175. int i, deviceCount;
  176. AudioDeviceID *deviceList;
  177. char buf[ 1024 ];
  178. status = AudioHardwareGetPropertyInfo( kAudioHardwarePropertyDevices, &size, NULL );
  179. if ( status != kAudioHardwareNoError ) {
  180. common->Warning( "AudioHardwareGetPropertyInfo kAudioHardwarePropertyDevices failed. status: %s", ExtractStatus( status ) );
  181. InitFailed();
  182. return false;
  183. }
  184. deviceCount = size / sizeof( AudioDeviceID );
  185. if ( !deviceCount ) {
  186. common->Printf( "No sound device found\n" );
  187. InitFailed();
  188. return false;
  189. }
  190. deviceList = (AudioDeviceID*)malloc( size );
  191. status = AudioHardwareGetProperty( kAudioHardwarePropertyDevices, &size, deviceList );
  192. if ( status != kAudioHardwareNoError ) {
  193. common->Warning( "AudioHardwareGetProperty kAudioHardwarePropertyDevices failed. status: %s", ExtractStatus( status ) );
  194. free( deviceList );
  195. InitFailed();
  196. return false;
  197. }
  198. common->Printf( "%d sound device(s)\n", deviceCount );
  199. for( i = 0; i < deviceCount; i++ ) {
  200. size = 1024;
  201. status = AudioDeviceGetProperty( deviceList[ i ], 0, false, kAudioDevicePropertyDeviceName, &size, buf );
  202. if ( status != kAudioHardwareNoError ) {
  203. common->Warning( "AudioDeviceGetProperty kAudioDevicePropertyDeviceName %d failed. status: %s", i, ExtractStatus( status ) );
  204. free( deviceList );
  205. InitFailed();
  206. return false;
  207. }
  208. common->Printf( " %d: ID %d, %s - ", i, deviceList[ i ], buf );
  209. size = 1024;
  210. status = AudioDeviceGetProperty( deviceList[ i ], 0, false, kAudioDevicePropertyDeviceManufacturer, &size, buf );
  211. if ( status != kAudioHardwareNoError ) {
  212. common->Warning( "AudioDeviceGetProperty kAudioDevicePropertyDeviceManufacturer %d failed. status: %s", i, ExtractStatus( status ) );
  213. free( deviceList );
  214. InitFailed();
  215. return false;
  216. }
  217. common->Printf( "%s\n", buf );
  218. }
  219. if ( s_device.GetInteger() != -1 && s_device.GetInteger() < deviceCount ) {
  220. selectedDevice = deviceList[ s_device.GetInteger() ];
  221. common->Printf( "s_device: device ID %d\n", selectedDevice );
  222. } else {
  223. size = sizeof( selectedDevice );
  224. status = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice, &size, &selectedDevice );
  225. if ( status != kAudioHardwareNoError ) {
  226. common->Warning( "AudioHardwareGetProperty kAudioHardwarePropertyDefaultOutputDevice failed. status: %s", ExtractStatus( status ) );
  227. free( deviceList );
  228. InitFailed();
  229. return false;
  230. }
  231. common->Printf( "select default device, ID %d\n", selectedDevice );
  232. }
  233. free( deviceList );
  234. deviceList = NULL;
  235. /*
  236. // setup a listener to watch for changes to properties
  237. status = AudioDeviceAddPropertyListener( selectedDevice, 0, false, kAudioDeviceProcessorOverload, DeviceListener, this );
  238. if ( status != kAudioHardwareNoError ) {
  239. common->Warning( "AudioDeviceAddPropertyListener kAudioDeviceProcessorOverload failed. status: %s", ExtractStatus( status ) );
  240. InitFailed();
  241. return;
  242. }
  243. */
  244. Float64 sampleRate;
  245. size = sizeof( sampleRate );
  246. status = AudioDeviceGetProperty( selectedDevice, 0, false, kAudioDevicePropertyNominalSampleRate, &size, &sampleRate );
  247. if ( status != kAudioHardwareNoError ) {
  248. common->Warning( "AudioDeviceGetProperty %d kAudioDevicePropertyNominalSampleRate failed. status: %s", selectedDevice, ExtractStatus( status ) );
  249. InitFailed();
  250. return false;
  251. }
  252. common->Printf( "current nominal rate: %g\n", sampleRate );
  253. if ( sampleRate != PRIMARYFREQ ) {
  254. GetAvailableNominalSampleRates();
  255. sampleRate = PRIMARYFREQ;
  256. common->Printf( "setting rate to: %g\n", sampleRate );
  257. status = AudioDeviceSetProperty( selectedDevice, NULL, 0, false, kAudioDevicePropertyNominalSampleRate, size, &sampleRate );
  258. if ( status != kAudioHardwareNoError ) {
  259. common->Warning( "AudioDeviceSetProperty %d kAudioDevicePropertyNominalSampleRate %g failed. status: %s", selectedDevice, sampleRate, ExtractStatus( status ) );
  260. InitFailed();
  261. return false;
  262. }
  263. }
  264. UInt32 frameSize;
  265. size = sizeof( UInt32 );
  266. status = AudioDeviceGetProperty( selectedDevice, 0, false, kAudioDevicePropertyBufferFrameSize, &size, &frameSize );
  267. if ( status != kAudioHardwareNoError ) {
  268. common->Warning( "AudioDeviceGetProperty %d kAudioDevicePropertyBufferFrameSize failed.status: %s", selectedDevice, ExtractStatus( status ) );
  269. InitFailed();
  270. return false;
  271. }
  272. common->Printf( "current frame size: %d\n", frameSize );
  273. // get the allowed frame size range
  274. AudioValueRange frameSizeRange;
  275. size = sizeof( AudioValueRange );
  276. status = AudioDeviceGetProperty( selectedDevice, 0, false, kAudioDevicePropertyBufferFrameSizeRange, &size, &frameSizeRange );
  277. if ( status != kAudioHardwareNoError ) {
  278. common->Warning( "AudioDeviceGetProperty %d kAudioDevicePropertyBufferFrameSizeRange failed. status: %s", selectedDevice, ExtractStatus( status ) );
  279. InitFailed();
  280. return false;
  281. }
  282. common->Printf( "frame size allowed range: %g %g\n", frameSizeRange.mMinimum, frameSizeRange.mMaximum );
  283. if ( frameSizeRange.mMaximum < MIXBUFFER_SAMPLES ) {
  284. common->Warning( "can't obtain the required frame size of %d bits", MIXBUFFER_SAMPLES );
  285. InitFailed();
  286. return false;
  287. }
  288. if ( frameSize != (unsigned int)MIXBUFFER_SAMPLES ) {
  289. frameSize = MIXBUFFER_SAMPLES;
  290. common->Printf( "setting frame size to: %d\n", frameSize );
  291. size = sizeof( frameSize );
  292. status = AudioDeviceSetProperty( selectedDevice, NULL, 0, false, kAudioDevicePropertyBufferFrameSize, size, &frameSize );
  293. if ( status != kAudioHardwareNoError ) {
  294. common->Warning( "AudioDeviceSetProperty %d kAudioDevicePropertyBufferFrameSize failed. status: %s", selectedDevice, ExtractStatus( status ) );
  295. InitFailed();
  296. return false;
  297. }
  298. }
  299. if ( idSoundSystemLocal::s_numberOfSpeakers.GetInteger() != 2 ) {
  300. common->Warning( "only stereo sound currently supported" );
  301. idSoundSystemLocal::s_numberOfSpeakers.SetInteger( 2 );
  302. }
  303. UInt32 channels[ 2 ];
  304. size = 2 * sizeof( UInt32 );
  305. status = AudioDeviceGetProperty( selectedDevice, 0, false, kAudioDevicePropertyPreferredChannelsForStereo, &size, &channels );
  306. if ( status != kAudioHardwareNoError ) {
  307. common->Warning( "AudioDeviceGetProperty %d kAudioDevicePropertyPreferredChannelsForStereo failed. status: %s", selectedDevice, ExtractStatus( status ) );
  308. InitFailed();
  309. return false;
  310. }
  311. common->Printf( "using stereo channel IDs %d %d\n", channels[ 0 ], channels[ 1 ] );
  312. status = AudioDeviceAddIOProc( selectedDevice, DeviceIOProc, NULL );
  313. if ( status != kAudioHardwareNoError ) {
  314. common->Warning( "AudioDeviceAddIOProc failed. status: %s", ExtractStatus( status ) );
  315. InitFailed();
  316. return false;
  317. }
  318. activeIOProc = true;
  319. status = AudioDeviceStart( selectedDevice, DeviceIOProc );
  320. if ( status != kAudioHardwareNoError ) {
  321. common->Warning( "AudioDeviceStart failed. status: %s", ExtractStatus( status ) );
  322. InitFailed();
  323. return false;
  324. }
  325. /*
  326. // allocate the mix buffer
  327. // it has the space for ROOM_SLICES_IN_BUFFER DeviceIOProc loops
  328. mixBufferSize = dwSpeakers * dwSampleSize * dwPrimaryBitRate * ROOM_SLICES_IN_BUFFER / 8;
  329. mixBuffer = malloc( mixBufferSize );
  330. memset( mixBuffer, 0, mixBufferSize );
  331. */
  332. return true;
  333. }
  334. /*
  335. ==========
  336. idAudioHardwareOSX::GetAvailableNominalSampleRates
  337. ==========
  338. */
  339. void idAudioHardwareOSX::GetAvailableNominalSampleRates( void ) {
  340. UInt32 size;
  341. OSStatus status;
  342. int i, rangeCount;
  343. AudioValueRange *rangeArray;
  344. status = AudioDeviceGetPropertyInfo( selectedDevice, 0, false, kAudioDevicePropertyAvailableNominalSampleRates, &size, NULL );
  345. if ( status != kAudioHardwareNoError ) {
  346. common->Warning( "AudioDeviceGetPropertyInfo %d kAudioDevicePropertyAvailableNominalSampleRates failed. status: %s", selectedDevice, ExtractStatus( status ) );
  347. return;
  348. }
  349. rangeCount = size / sizeof( AudioValueRange );
  350. rangeArray = (AudioValueRange *)malloc( size );
  351. common->Printf( "%d possible rate(s)\n", rangeCount );
  352. status = AudioDeviceGetProperty( selectedDevice, 0, false, kAudioDevicePropertyAvailableNominalSampleRates, &size, rangeArray );
  353. if ( status != kAudioHardwareNoError ) {
  354. common->Warning( "AudioDeviceGetProperty %d kAudioDevicePropertyAvailableNominalSampleRates failed. status: %s", selectedDevice, ExtractStatus( status ) );
  355. free( rangeArray );
  356. return;
  357. }
  358. for( i = 0; i < rangeCount; i++ ) {
  359. common->Printf( " %d: min %g max %g\n", i, rangeArray[ i ].mMinimum, rangeArray[ i ].mMaximum );
  360. }
  361. free( rangeArray );
  362. }
  363. /*
  364. ==========
  365. idAudioHardwareOSX::GetNumberOfSpeakers
  366. ==========
  367. */
  368. int idAudioHardwareOSX::GetNumberOfSpeakers() {
  369. return idSoundSystemLocal::s_numberOfSpeakers.GetInteger();
  370. }
  371. /*
  372. ===============
  373. Sys_LoadOpenAL
  374. ===============
  375. */
  376. bool Sys_LoadOpenAL( void ) {
  377. OSErr err;
  378. long gestaltOSVersion;
  379. err = Gestalt(gestaltSystemVersion, &gestaltOSVersion);
  380. if ( err || gestaltOSVersion < 0x1040 ) {
  381. return false;
  382. }
  383. return true;
  384. }