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