1 | /* darwin_native.c |
---|
2 | * |
---|
3 | * Mac OS X backend for OpenAL |
---|
4 | * only PowerPC target on MacOS X is supported (Darwin is not supported alone) |
---|
5 | * |
---|
6 | * file originally created on jan 2000 by |
---|
7 | * Guillaume Borios (gborios@free.fr) and Florent Boudet (flobo@ifrance.com) |
---|
8 | * to help the PineApple project (http://ios.free.fr) run on MOSX |
---|
9 | * |
---|
10 | * Version : Alpha 6 |
---|
11 | */ |
---|
12 | |
---|
13 | #include <CoreAudio/CoreAudio.h> |
---|
14 | |
---|
15 | #include "backends/alc_backend.h" |
---|
16 | |
---|
17 | #include <fcntl.h> |
---|
18 | #include <stdlib.h> |
---|
19 | #include <stdio.h> |
---|
20 | #include <string.h> |
---|
21 | |
---|
22 | #include "al_main.h" |
---|
23 | #include "al_debug.h" |
---|
24 | #include "al_rcvar.h" |
---|
25 | #include "alc/alc_context.h" |
---|
26 | |
---|
27 | /* Adding buffers improves response to temporary heavy CPU loads but increases latency... */ |
---|
28 | |
---|
29 | #define buildID 12 |
---|
30 | |
---|
31 | typedef struct { |
---|
32 | AudioDeviceID deviceW; /* the device ID */ |
---|
33 | void * deviceWBuffer; |
---|
34 | UInt32 deviceWBufferSize; /* Buffer size of the audio device */ |
---|
35 | AudioBufferList* deviceWBufferList; |
---|
36 | AudioStreamBasicDescription deviceFormat; /* format of the default device */ |
---|
37 | } globalVars, *globalPtr; |
---|
38 | |
---|
39 | /************************************** GLOBALS *********************************/ |
---|
40 | |
---|
41 | static globalVars libGlobals; /* my globals */ |
---|
42 | |
---|
43 | static unsigned int alWriteFormat; /* format of data from AL*/ |
---|
44 | static unsigned int alWriteSpeed; /* speed of data from AL*/ |
---|
45 | static unsigned int nativePreferedBuffSize; |
---|
46 | static void * coreAudioDestination; |
---|
47 | static int ratio; |
---|
48 | static int stillToPlay = 0; |
---|
49 | |
---|
50 | /************************************* PROTOTYPES *********************************/ |
---|
51 | |
---|
52 | static void implement_me(const char *fn); |
---|
53 | OSStatus deviceFillingProc (AudioDeviceID inDevice, const AudioTimeStamp* inNow, const AudioBufferList* inInputData, const AudioTimeStamp* inInputTime, AudioBufferList* outOutputData, const AudioTimeStamp* inOutputTime, void* inClientData); |
---|
54 | |
---|
55 | OSStatus GetAudioDevices (void **devices /*Dev IDs*/, short *devicesAvailable /*Dev number*/); |
---|
56 | |
---|
57 | int sync_mixer_iterate( void *dummy ); |
---|
58 | |
---|
59 | void playABuffer(void *realdata); |
---|
60 | |
---|
61 | /************************************** UTILITIES *********************************/ |
---|
62 | |
---|
63 | static int NoPrintf( UNUSED(const char *format), ... ) |
---|
64 | { |
---|
65 | return 0; |
---|
66 | } |
---|
67 | |
---|
68 | /* ToDo: Use vfprintf and nuke buffer */ |
---|
69 | static int _alDebugPrintf( const char *format, ... ) |
---|
70 | { |
---|
71 | static char formatbuf[256]; |
---|
72 | |
---|
73 | va_list ap; |
---|
74 | va_start(ap, format); |
---|
75 | vsnprintf(formatbuf, sizeof formatbuf, format, ap); |
---|
76 | va_end(ap); |
---|
77 | |
---|
78 | return fprintf(stderr, "%s", formatbuf); |
---|
79 | } |
---|
80 | |
---|
81 | static int (*DebugPrintf)( const char *format, ... ) = NoPrintf; |
---|
82 | |
---|
83 | |
---|
84 | static void implement_me(const char *fn) |
---|
85 | { |
---|
86 | DebugPrintf("[darwin_native.c] : %s is not implemented.\nPlease contact gborios@free.fr for information or help.\n", fn); |
---|
87 | } |
---|
88 | |
---|
89 | /*********************************** OS callback proc *****************************/ |
---|
90 | |
---|
91 | OSStatus deviceFillingProc (UNUSED(AudioDeviceID inDevice), UNUSED(const AudioTimeStamp* inNow), UNUSED(const AudioBufferList* inInputData), UNUSED(const AudioTimeStamp* inInputTime), AudioBufferList* outOutputData, UNUSED(const AudioTimeStamp* inOutputTime), void* inClientData) |
---|
92 | { |
---|
93 | coreAudioDestination = (outOutputData->mBuffers[0]).mData; |
---|
94 | |
---|
95 | if (stillToPlay) playABuffer(NULL); |
---|
96 | else sync_mixer_iterate(NULL); |
---|
97 | |
---|
98 | return 0; |
---|
99 | } |
---|
100 | |
---|
101 | /************************************** HAL Routines *********************************/ |
---|
102 | |
---|
103 | OSStatus GetAudioDevices (void **devices /*Dev IDs*/, short *devicesAvailable /*Dev number*/) |
---|
104 | { |
---|
105 | #ifdef DEBUG_MAXIMUS |
---|
106 | int i; |
---|
107 | char cStr[256]; |
---|
108 | #endif |
---|
109 | OSStatus err = NULL; |
---|
110 | UInt32 outSize; |
---|
111 | Boolean outWritable; |
---|
112 | |
---|
113 | DebugPrintf("OpenAL MOSX Backend : Build %d\n",buildID); |
---|
114 | |
---|
115 | // find out how many audio devices there are, if any |
---|
116 | err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &outSize, &outWritable); |
---|
117 | if (err != NULL) return (err); |
---|
118 | |
---|
119 | // calculate the number of device available |
---|
120 | *devicesAvailable = outSize / sizeof(AudioDeviceID); |
---|
121 | // Bail if there aren't any devices |
---|
122 | if (*devicesAvailable < 1) return (-1); |
---|
123 | |
---|
124 | // make space for the devices we are about to get |
---|
125 | if (*devices != NULL) free(*devices); |
---|
126 | *devices = malloc(outSize); |
---|
127 | // get an array of AudioDeviceIDs |
---|
128 | err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &outSize, (void *) *devices); |
---|
129 | if (err != NULL) free(*devices); |
---|
130 | #ifdef DEBUG_MAXIMUS |
---|
131 | DebugPrintf("Found %d Audio Device(s)\n",*devicesAvailable); |
---|
132 | |
---|
133 | for (i=0; i<*devicesAvailable;i++) |
---|
134 | { |
---|
135 | UInt32 ID = ((UInt32*)(*devices))[i]; |
---|
136 | err = AudioDeviceGetPropertyInfo(ID, 0, 0, kAudioDevicePropertyDeviceName, &outSize, &outWritable); |
---|
137 | err = AudioDeviceGetProperty(ID, 0, 0, kAudioDevicePropertyDeviceName, &outSize, cStr); |
---|
138 | DebugPrintf("Device #%d : %s",ID,cStr); |
---|
139 | err = AudioDeviceGetProperty(ID, 0, 0, kAudioDevicePropertyDeviceManufacturer, &outSize, cStr); |
---|
140 | DebugPrintf(" (%s)\n",cStr); |
---|
141 | |
---|
142 | } |
---|
143 | #endif |
---|
144 | return (err); |
---|
145 | } |
---|
146 | |
---|
147 | |
---|
148 | /************************************** INTERFACE *********************************/ |
---|
149 | |
---|
150 | static void *grab_read_native(void) |
---|
151 | { |
---|
152 | implement_me("void *grab_read_native()"); |
---|
153 | return NULL; |
---|
154 | } |
---|
155 | |
---|
156 | static void *grab_write_native(void) |
---|
157 | { |
---|
158 | OSStatus error = 0; |
---|
159 | UInt32 count; |
---|
160 | void * devices = 0; |
---|
161 | short devicesAvailable; |
---|
162 | Boolean outWritable; |
---|
163 | |
---|
164 | Rcvar native_debug = rc_lookup("native-backend-debug"); |
---|
165 | if((rc_type(native_debug) == ALRC_BOOL) && |
---|
166 | (rc_tobool(native_debug) == AL_TRUE)) |
---|
167 | { |
---|
168 | DebugPrintf = _alDebugPrintf; |
---|
169 | } |
---|
170 | else |
---|
171 | { |
---|
172 | DebugPrintf = NoPrintf; |
---|
173 | } |
---|
174 | |
---|
175 | /* Look for audio devices */ |
---|
176 | error = GetAudioDevices (&devices ,&devicesAvailable); |
---|
177 | if (error != 0) goto Crash; |
---|
178 | libGlobals.deviceW = ((AudioDeviceID*)(devices))[0]; /* Selecting first device */ |
---|
179 | |
---|
180 | /* Getting buffer size */ |
---|
181 | error = AudioDeviceGetPropertyInfo(libGlobals.deviceW, 0, 0, kAudioDevicePropertyBufferSize, &count, &outWritable); |
---|
182 | if (error != 0) goto Crash; |
---|
183 | error = AudioDeviceGetProperty(libGlobals.deviceW, 0, 0, kAudioDevicePropertyBufferSize, &count, &libGlobals.deviceWBufferSize); |
---|
184 | if (error != 0) goto Crash; |
---|
185 | DebugPrintf("IOProperties : Buffersize = %d\n", |
---|
186 | (int) libGlobals.deviceWBufferSize); |
---|
187 | |
---|
188 | /* getting streams configs */ |
---|
189 | error = AudioDeviceGetPropertyInfo(libGlobals.deviceW, 0, 0, kAudioDevicePropertyStreamConfiguration, &count, &outWritable); |
---|
190 | if (error != 0) goto Crash; |
---|
191 | { |
---|
192 | libGlobals.deviceWBufferList = malloc(count); |
---|
193 | |
---|
194 | error = AudioDeviceGetProperty(libGlobals.deviceW, 0, 0, kAudioDevicePropertyStreamConfiguration, &count, libGlobals.deviceWBufferList); |
---|
195 | if (error != 0) goto Crash; |
---|
196 | #ifdef DEBUG_MAXIMUS |
---|
197 | { |
---|
198 | unsigned int i; |
---|
199 | DebugPrintf("IOProperties : Buffer number = %d\n",libGlobals.deviceWBufferList->mNumberBuffers); |
---|
200 | /*device->outStreamsInfo = malloc(sizeof(StreamInfo) * device->totalOutputStreams);*/ |
---|
201 | for (i = 0; i < libGlobals.deviceWBufferList->mNumberBuffers; i++) |
---|
202 | { |
---|
203 | DebugPrintf(" Buffer %d Properties : DataByteSize = %d\n",i,libGlobals.deviceWBufferList->mBuffers[i].mDataByteSize); |
---|
204 | DebugPrintf(" Buffer %d Properties : NumberChannels = %d\n",i,libGlobals.deviceWBufferList->mBuffers[i].mNumberChannels); |
---|
205 | DebugPrintf(" Buffer %d Properties : Data = %d\n",i,libGlobals.deviceWBufferList->mBuffers[i].mData); |
---|
206 | /*error = GetPhysicalFormatCount(device, i + 1, &device->outStreamsInfo[i].pFormatCount, false); |
---|
207 | device->outStreamsInfo[i].pFormatMenuSelection = 1; |
---|
208 | |
---|
209 | error = GetActualFormatCount(device, i + 1, &device->outStreamsInfo[i].aFormatCount, false); |
---|
210 | device->outStreamsInfo[i].aFormatMenuSelection = 1;*/ |
---|
211 | } |
---|
212 | } |
---|
213 | #endif |
---|
214 | } |
---|
215 | |
---|
216 | error = AudioDeviceGetPropertyInfo(libGlobals.deviceW, 0, 0, kAudioDevicePropertyStreamFormats, &count, &outWritable); |
---|
217 | if (error != 0) goto Crash; |
---|
218 | error = AudioDeviceGetProperty(libGlobals.deviceW, 0, 0, kAudioDevicePropertyStreamFormats, &count, &libGlobals.deviceFormat); |
---|
219 | if (error != 0) goto Crash; |
---|
220 | |
---|
221 | #ifndef DEBUG_MAXIMUS |
---|
222 | DebugPrintf("IOProperties : SampleRate = %f\n",libGlobals.deviceFormat.mSampleRate); |
---|
223 | DebugPrintf("IOProperties : FormatFlags = %d\n",(int)libGlobals.deviceFormat.mFormatFlags); |
---|
224 | DebugPrintf("IOProperties : BytesPerPacket = %d\n",(int)libGlobals.deviceFormat.mBytesPerPacket); |
---|
225 | DebugPrintf("IOProperties : FramesPerPacket = %d\n",(int)libGlobals.deviceFormat.mFramesPerPacket); |
---|
226 | DebugPrintf("IOProperties : BytesPerFrame = %d\n",(int)libGlobals.deviceFormat.mBytesPerFrame); |
---|
227 | DebugPrintf("IOProperties : ChannelsPerFrame = %d\n",(int)libGlobals.deviceFormat.mChannelsPerFrame); |
---|
228 | DebugPrintf("IOProperties : BitsPerChannel = %d\n",(int)libGlobals.deviceFormat.mBitsPerChannel); |
---|
229 | #endif |
---|
230 | |
---|
231 | error = AudioDeviceGetPropertyInfo(libGlobals.deviceW, 0, 0, kAudioDevicePropertyStreamFormat, &count, &outWritable); |
---|
232 | if (error != 0) goto Crash; |
---|
233 | error = AudioDeviceGetProperty(libGlobals.deviceW, 0, 0, kAudioDevicePropertyStreamFormat, &count, &libGlobals.deviceFormat); |
---|
234 | if (error != 0) goto Crash; |
---|
235 | |
---|
236 | error = AudioDeviceAddIOProc(libGlobals.deviceW, deviceFillingProc, (void *) &libGlobals); /* Creates the callback proc */ |
---|
237 | if (error != 0) goto Crash; |
---|
238 | |
---|
239 | return &libGlobals.deviceW; |
---|
240 | |
---|
241 | Crash : |
---|
242 | DebugPrintf("An error occured during void *grab_write_native()\n"); |
---|
243 | libGlobals.deviceW = NULL; |
---|
244 | return NULL; |
---|
245 | } |
---|
246 | |
---|
247 | void * |
---|
248 | alcBackendOpenNative_( ALC_OpenMode mode ) |
---|
249 | { |
---|
250 | return mode == ALC_OPEN_INPUT_ ? grab_read_native() : grab_write_native(); |
---|
251 | } |
---|
252 | |
---|
253 | static ALboolean set_write_native(UNUSED(void *handle), |
---|
254 | unsigned int *bufsiz, |
---|
255 | unsigned int *fmt, |
---|
256 | unsigned int *speed) |
---|
257 | { |
---|
258 | OSStatus error = 0; |
---|
259 | |
---|
260 | DebugPrintf("Init Speed : %d\n",*speed); |
---|
261 | |
---|
262 | //*fmt = AL_FORMAT_STEREO16; |
---|
263 | //*speed = (unsigned int)libGlobals.deviceFormat.mSampleRate; |
---|
264 | |
---|
265 | alWriteFormat = *fmt; |
---|
266 | |
---|
267 | /* defines what the AL buffer size should be */ |
---|
268 | switch(alWriteFormat) |
---|
269 | { |
---|
270 | case AL_FORMAT_STEREO8:*bufsiz = libGlobals.deviceWBufferSize/4; |
---|
271 | DebugPrintf("Init fmt : AL_FORMAT_STEREO8\n"); |
---|
272 | break; |
---|
273 | case AL_FORMAT_MONO16: *bufsiz = libGlobals.deviceWBufferSize/4; |
---|
274 | DebugPrintf("Init fmt : AL_FORMAT_MONO16\n"); |
---|
275 | break; |
---|
276 | |
---|
277 | case AL_FORMAT_STEREO16: *bufsiz = libGlobals.deviceWBufferSize/2; |
---|
278 | DebugPrintf("Init fmt : AL_FORMAT_STEREO16\n"); |
---|
279 | break; |
---|
280 | |
---|
281 | case AL_FORMAT_MONO8: *bufsiz = libGlobals.deviceWBufferSize/8; |
---|
282 | DebugPrintf("Init fmt : AL_FORMAT_MONO8\n"); |
---|
283 | break; |
---|
284 | |
---|
285 | default: break; |
---|
286 | } |
---|
287 | *bufsiz /= ((unsigned int)libGlobals.deviceFormat.mSampleRate) / *speed; |
---|
288 | |
---|
289 | alWriteSpeed = *speed; |
---|
290 | ratio = ((unsigned int)libGlobals.deviceFormat.mSampleRate) / *speed; |
---|
291 | nativePreferedBuffSize = *bufsiz; |
---|
292 | |
---|
293 | /* start playing sound through the device */ |
---|
294 | error = AudioDeviceStart(libGlobals.deviceW, deviceFillingProc); |
---|
295 | if (error != 0) return NULL; |
---|
296 | |
---|
297 | return AL_TRUE; |
---|
298 | } |
---|
299 | |
---|
300 | |
---|
301 | static ALboolean set_read_native(UNUSED(void *handle), UNUSED(unsigned int *bufsiz), UNUSED(unsigned int *fmt), UNUSED(unsigned int *speed)) |
---|
302 | { |
---|
303 | implement_me("ALboolean set_read_native()"); |
---|
304 | return AL_FALSE; |
---|
305 | } |
---|
306 | |
---|
307 | ALboolean |
---|
308 | alcBackendSetAttributesNative_(ALC_OpenMode mode, void *handle, ALuint *bufsiz, ALenum *fmt, ALuint *speed) |
---|
309 | { |
---|
310 | return mode == ALC_OPEN_INPUT_ ? |
---|
311 | set_read_native(handle, bufsiz, fmt, speed) : |
---|
312 | set_write_native(handle, bufsiz, fmt, speed); |
---|
313 | } |
---|
314 | |
---|
315 | void native_blitbuffer(void *handle, void *data, int bytes) |
---|
316 | { |
---|
317 | stillToPlay = bytes / nativePreferedBuffSize; |
---|
318 | |
---|
319 | if (handle == NULL) return; |
---|
320 | |
---|
321 | if (coreAudioDestination == 0) |
---|
322 | { |
---|
323 | fprintf(stderr,"Something wrong happened between CoreAudio and OpenAL.\n"); |
---|
324 | return; |
---|
325 | } |
---|
326 | |
---|
327 | // Gyom FIXME: Is this useful? |
---|
328 | assert(nativePreferedBuffSize <= bytes); |
---|
329 | |
---|
330 | playABuffer(data); |
---|
331 | } |
---|
332 | |
---|
333 | void playABuffer(void *realdata) |
---|
334 | { |
---|
335 | register unsigned int count; |
---|
336 | register Float32 *outDataPtr = coreAudioDestination; |
---|
337 | register SInt16 *inDataPtr16; |
---|
338 | register SInt8 *inDataPtr8; |
---|
339 | static void * data; |
---|
340 | |
---|
341 | if (realdata!=NULL) data = realdata; |
---|
342 | |
---|
343 | inDataPtr16 = (SInt16*)(data); |
---|
344 | inDataPtr8 = (SInt8*)(data); |
---|
345 | |
---|
346 | stillToPlay--; |
---|
347 | |
---|
348 | switch(alWriteFormat) |
---|
349 | { |
---|
350 | int i; |
---|
351 | case AL_FORMAT_STEREO16: |
---|
352 | assert(nativePreferedBuffSize <= libGlobals.deviceWBufferSize/2); |
---|
353 | for (count = nativePreferedBuffSize/2; count > 0; count--) |
---|
354 | { |
---|
355 | for (i = ratio; i>0; i--) |
---|
356 | { |
---|
357 | *outDataPtr = ((Float32)(*inDataPtr16))/32767.0; |
---|
358 | outDataPtr++; |
---|
359 | } |
---|
360 | inDataPtr16++; |
---|
361 | } |
---|
362 | data = inDataPtr16; |
---|
363 | break; |
---|
364 | |
---|
365 | case AL_FORMAT_MONO16: |
---|
366 | assert(nativePreferedBuffSize <= libGlobals.deviceWBufferSize/2); |
---|
367 | for (count = nativePreferedBuffSize/2; count >0; count--) |
---|
368 | { |
---|
369 | for (i = ratio*2; i>0; i--) |
---|
370 | { |
---|
371 | *outDataPtr = ((Float32)(*inDataPtr16))/32767.0; |
---|
372 | outDataPtr++; |
---|
373 | } |
---|
374 | inDataPtr16++; |
---|
375 | } |
---|
376 | data = inDataPtr16; |
---|
377 | break; |
---|
378 | |
---|
379 | case AL_FORMAT_STEREO8: |
---|
380 | assert(nativePreferedBuffSize <= libGlobals.deviceWBufferSize); |
---|
381 | for (count = nativePreferedBuffSize; count >0; count--) |
---|
382 | { |
---|
383 | for (i = ratio; i>0; i--) |
---|
384 | { |
---|
385 | *outDataPtr = ((Float32)(*inDataPtr8))/32767.0; |
---|
386 | outDataPtr++; |
---|
387 | } |
---|
388 | inDataPtr8++; |
---|
389 | } |
---|
390 | data = inDataPtr8; |
---|
391 | break; |
---|
392 | |
---|
393 | case AL_FORMAT_MONO8: |
---|
394 | assert(nativePreferedBuffSize <= libGlobals.deviceWBufferSize); |
---|
395 | for (count = nativePreferedBuffSize; count >0; count--) |
---|
396 | { |
---|
397 | for (i = ratio*2; i>0; i--) |
---|
398 | { |
---|
399 | *outDataPtr = ((Float32)(*inDataPtr8))/32767.0; |
---|
400 | outDataPtr++; |
---|
401 | } |
---|
402 | inDataPtr8++; |
---|
403 | } |
---|
404 | data = inDataPtr8; |
---|
405 | break; |
---|
406 | |
---|
407 | default: |
---|
408 | DebugPrintf("Format not recognized... Try again ;-)"); |
---|
409 | break; |
---|
410 | } |
---|
411 | } |
---|
412 | |
---|
413 | |
---|
414 | |
---|
415 | void release_native(void *handle) |
---|
416 | { |
---|
417 | if (libGlobals.deviceW == *(AudioDeviceID*)handle) |
---|
418 | { |
---|
419 | AudioDeviceStop(libGlobals.deviceW, deviceFillingProc); |
---|
420 | AudioDeviceRemoveIOProc(libGlobals.deviceW, deviceFillingProc); |
---|
421 | } |
---|
422 | } |
---|
423 | |
---|
424 | ALfloat get_nativechannel(UNUSED(void *handle), UNUSED(ALuint channel)) |
---|
425 | { |
---|
426 | implement_me("float get_nativechannel()"); |
---|
427 | return 0; |
---|
428 | } |
---|
429 | |
---|
430 | int set_nativechannel(UNUSED(void *handle),UNUSED(ALuint channel),UNUSED(ALfloat volume)) |
---|
431 | { |
---|
432 | implement_me("int set_nativechannel()"); |
---|
433 | return 0; |
---|
434 | } |
---|
435 | |
---|
436 | void pause_nativedevice(void *handle) /* Not tested :-( */ |
---|
437 | { |
---|
438 | if (libGlobals.deviceW == *(AudioDeviceID*)handle) |
---|
439 | AudioDeviceStop(libGlobals.deviceW, deviceFillingProc); |
---|
440 | } |
---|
441 | |
---|
442 | void resume_nativedevice(void *handle) /* Not tested :-( */ |
---|
443 | { |
---|
444 | if (libGlobals.deviceW == *(AudioDeviceID*)handle) |
---|
445 | AudioDeviceStart(libGlobals.deviceW, deviceFillingProc); |
---|
446 | } |
---|
447 | |
---|
448 | ALsizei capture_nativedevice(UNUSED(void *handle), UNUSED(void *capture_buffer), UNUSED(int bufsiz)) |
---|
449 | { |
---|
450 | implement_me("void capture_nativedevice()"); |
---|
451 | return NULL; |
---|
452 | } |
---|