e14743d1 |
1 | /* |
2 | SDL - Simple DirectMedia Layer |
3 | Copyright (C) 1997-2009 Sam Lantinga |
4 | |
5 | This library is free software; you can redistribute it and/or |
6 | modify it under the terms of the GNU Lesser General Public |
7 | License as published by the Free Software Foundation; either |
8 | version 2.1 of the License, or (at your option) any later version. |
9 | |
10 | This library 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 GNU |
13 | Lesser General Public License for more details. |
14 | |
15 | You should have received a copy of the GNU Lesser General Public |
16 | License along with this library; if not, write to the Free Software |
17 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
18 | |
19 | Carsten Griwodz |
20 | griff@kom.tu-darmstadt.de |
21 | |
22 | based on linux/SDL_dspaudio.c by Sam Lantinga |
23 | */ |
24 | #include "SDL_config.h" |
25 | |
26 | /* Allow access to a raw mixing buffer */ |
27 | |
28 | #include <errno.h> |
29 | #include <unistd.h> |
30 | #include <fcntl.h> |
31 | #include <sys/time.h> |
32 | #include <sys/ioctl.h> |
33 | #include <sys/stat.h> |
34 | |
35 | #include "SDL_timer.h" |
36 | #include "SDL_audio.h" |
37 | #include "../SDL_audiomem.h" |
38 | #include "../SDL_audio_c.h" |
39 | #include "../SDL_audiodev_c.h" |
40 | #include "SDL_paudio.h" |
41 | |
42 | #define DEBUG_AUDIO 1 |
43 | |
44 | /* A conflict within AIX 4.3.3 <sys/> headers and probably others as well. |
45 | * I guess nobody ever uses audio... Shame over AIX header files. */ |
46 | #include <sys/machine.h> |
47 | #undef BIG_ENDIAN |
48 | #include <sys/audio.h> |
49 | |
50 | /* The tag name used by paud audio */ |
51 | #define Paud_DRIVER_NAME "paud" |
52 | |
53 | /* Open the audio device for playback, and don't block if busy */ |
54 | /* #define OPEN_FLAGS (O_WRONLY|O_NONBLOCK) */ |
55 | #define OPEN_FLAGS O_WRONLY |
56 | |
57 | /* Audio driver functions */ |
58 | static int Paud_OpenAudio(_THIS, SDL_AudioSpec *spec); |
59 | static void Paud_WaitAudio(_THIS); |
60 | static void Paud_PlayAudio(_THIS); |
61 | static Uint8 *Paud_GetAudioBuf(_THIS); |
62 | static void Paud_CloseAudio(_THIS); |
63 | |
64 | /* Audio driver bootstrap functions */ |
65 | |
66 | static int Audio_Available(void) |
67 | { |
68 | int fd; |
69 | int available; |
70 | |
71 | available = 0; |
72 | fd = SDL_OpenAudioPath(NULL, 0, OPEN_FLAGS, 0); |
73 | if ( fd >= 0 ) { |
74 | available = 1; |
75 | close(fd); |
76 | } |
77 | return(available); |
78 | } |
79 | |
80 | static void Audio_DeleteDevice(SDL_AudioDevice *device) |
81 | { |
82 | SDL_free(device->hidden); |
83 | SDL_free(device); |
84 | } |
85 | |
86 | static SDL_AudioDevice *Audio_CreateDevice(int devindex) |
87 | { |
88 | SDL_AudioDevice *this; |
89 | |
90 | /* Initialize all variables that we clean on shutdown */ |
91 | this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice)); |
92 | if ( this ) { |
93 | SDL_memset(this, 0, (sizeof *this)); |
94 | this->hidden = (struct SDL_PrivateAudioData *) |
95 | SDL_malloc((sizeof *this->hidden)); |
96 | } |
97 | if ( (this == NULL) || (this->hidden == NULL) ) { |
98 | SDL_OutOfMemory(); |
99 | if ( this ) { |
100 | SDL_free(this); |
101 | } |
102 | return(0); |
103 | } |
104 | SDL_memset(this->hidden, 0, (sizeof *this->hidden)); |
105 | audio_fd = -1; |
106 | |
107 | /* Set the function pointers */ |
108 | this->OpenAudio = Paud_OpenAudio; |
109 | this->WaitAudio = Paud_WaitAudio; |
110 | this->PlayAudio = Paud_PlayAudio; |
111 | this->GetAudioBuf = Paud_GetAudioBuf; |
112 | this->CloseAudio = Paud_CloseAudio; |
113 | |
114 | this->free = Audio_DeleteDevice; |
115 | |
116 | return this; |
117 | } |
118 | |
119 | AudioBootStrap Paud_bootstrap = { |
120 | Paud_DRIVER_NAME, "AIX Paudio", |
121 | Audio_Available, Audio_CreateDevice |
122 | }; |
123 | |
124 | /* This function waits until it is possible to write a full sound buffer */ |
125 | static void Paud_WaitAudio(_THIS) |
126 | { |
127 | fd_set fdset; |
128 | |
129 | /* See if we need to use timed audio synchronization */ |
130 | if ( frame_ticks ) { |
131 | /* Use timer for general audio synchronization */ |
132 | Sint32 ticks; |
133 | |
134 | ticks = ((Sint32)(next_frame - SDL_GetTicks()))-FUDGE_TICKS; |
135 | if ( ticks > 0 ) { |
136 | SDL_Delay(ticks); |
137 | } |
138 | } else { |
139 | audio_buffer paud_bufinfo; |
140 | |
141 | /* Use select() for audio synchronization */ |
142 | struct timeval timeout; |
143 | FD_ZERO(&fdset); |
144 | FD_SET(audio_fd, &fdset); |
145 | |
146 | if ( ioctl(audio_fd, AUDIO_BUFFER, &paud_bufinfo) < 0 ) { |
147 | #ifdef DEBUG_AUDIO |
148 | fprintf(stderr, "Couldn't get audio buffer information\n"); |
149 | #endif |
150 | timeout.tv_sec = 10; |
151 | timeout.tv_usec = 0; |
152 | } else { |
153 | long ms_in_buf = paud_bufinfo.write_buf_time; |
154 | timeout.tv_sec = ms_in_buf/1000; |
155 | ms_in_buf = ms_in_buf - timeout.tv_sec*1000; |
156 | timeout.tv_usec = ms_in_buf*1000; |
157 | #ifdef DEBUG_AUDIO |
158 | fprintf( stderr, |
159 | "Waiting for write_buf_time=%ld,%ld\n", |
160 | timeout.tv_sec, |
161 | timeout.tv_usec ); |
162 | #endif |
163 | } |
164 | |
165 | #ifdef DEBUG_AUDIO |
166 | fprintf(stderr, "Waiting for audio to get ready\n"); |
167 | #endif |
168 | if ( select(audio_fd+1, NULL, &fdset, NULL, &timeout) <= 0 ) { |
169 | const char *message = "Audio timeout - buggy audio driver? (disabled)"; |
170 | /* |
171 | * In general we should never print to the screen, |
172 | * but in this case we have no other way of letting |
173 | * the user know what happened. |
174 | */ |
175 | fprintf(stderr, "SDL: %s - %s\n", strerror(errno), message); |
176 | this->enabled = 0; |
177 | /* Don't try to close - may hang */ |
178 | audio_fd = -1; |
179 | #ifdef DEBUG_AUDIO |
180 | fprintf(stderr, "Done disabling audio\n"); |
181 | #endif |
182 | } |
183 | #ifdef DEBUG_AUDIO |
184 | fprintf(stderr, "Ready!\n"); |
185 | #endif |
186 | } |
187 | } |
188 | |
189 | static void Paud_PlayAudio(_THIS) |
190 | { |
191 | int written; |
192 | |
193 | /* Write the audio data, checking for EAGAIN on broken audio drivers */ |
194 | do { |
195 | written = write(audio_fd, mixbuf, mixlen); |
196 | if ( (written < 0) && ((errno == 0) || (errno == EAGAIN)) ) { |
197 | SDL_Delay(1); /* Let a little CPU time go by */ |
198 | } |
199 | } while ( (written < 0) && |
200 | ((errno == 0) || (errno == EAGAIN) || (errno == EINTR)) ); |
201 | |
202 | /* If timer synchronization is enabled, set the next write frame */ |
203 | if ( frame_ticks ) { |
204 | next_frame += frame_ticks; |
205 | } |
206 | |
207 | /* If we couldn't write, assume fatal error for now */ |
208 | if ( written < 0 ) { |
209 | this->enabled = 0; |
210 | } |
211 | #ifdef DEBUG_AUDIO |
212 | fprintf(stderr, "Wrote %d bytes of audio data\n", written); |
213 | #endif |
214 | } |
215 | |
216 | static Uint8 *Paud_GetAudioBuf(_THIS) |
217 | { |
218 | return mixbuf; |
219 | } |
220 | |
221 | static void Paud_CloseAudio(_THIS) |
222 | { |
223 | if ( mixbuf != NULL ) { |
224 | SDL_FreeAudioMem(mixbuf); |
225 | mixbuf = NULL; |
226 | } |
227 | if ( audio_fd >= 0 ) { |
228 | close(audio_fd); |
229 | audio_fd = -1; |
230 | } |
231 | } |
232 | |
233 | static int Paud_OpenAudio(_THIS, SDL_AudioSpec *spec) |
234 | { |
235 | char audiodev[1024]; |
236 | int format; |
237 | int bytes_per_sample; |
238 | Uint16 test_format; |
239 | audio_init paud_init; |
240 | audio_buffer paud_bufinfo; |
241 | audio_status paud_status; |
242 | audio_control paud_control; |
243 | audio_change paud_change; |
244 | |
245 | /* Reset the timer synchronization flag */ |
246 | frame_ticks = 0.0; |
247 | |
248 | /* Open the audio device */ |
249 | audio_fd = SDL_OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 0); |
250 | if ( audio_fd < 0 ) { |
251 | SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno)); |
252 | return -1; |
253 | } |
254 | |
255 | /* |
256 | * We can't set the buffer size - just ask the device for the maximum |
257 | * that we can have. |
258 | */ |
259 | if ( ioctl(audio_fd, AUDIO_BUFFER, &paud_bufinfo) < 0 ) { |
260 | SDL_SetError("Couldn't get audio buffer information"); |
261 | return -1; |
262 | } |
263 | |
264 | mixbuf = NULL; |
265 | |
266 | if ( spec->channels > 1 ) |
267 | spec->channels = 2; |
268 | else |
269 | spec->channels = 1; |
270 | |
271 | /* |
272 | * Fields in the audio_init structure: |
273 | * |
274 | * Ignored by us: |
275 | * |
276 | * paud.loadpath[LOAD_PATH]; * DSP code to load, MWave chip only? |
277 | * paud.slot_number; * slot number of the adapter |
278 | * paud.device_id; * adapter identification number |
279 | * |
280 | * Input: |
281 | * |
282 | * paud.srate; * the sampling rate in Hz |
283 | * paud.bits_per_sample; * 8, 16, 32, ... |
284 | * paud.bsize; * block size for this rate |
285 | * paud.mode; * ADPCM, PCM, MU_LAW, A_LAW, SOURCE_MIX |
286 | * paud.channels; * 1=mono, 2=stereo |
287 | * paud.flags; * FIXED - fixed length data |
288 | * * LEFT_ALIGNED, RIGHT_ALIGNED (var len only) |
289 | * * TWOS_COMPLEMENT - 2's complement data |
290 | * * SIGNED - signed? comment seems wrong in sys/audio.h |
291 | * * BIG_ENDIAN |
292 | * paud.operation; * PLAY, RECORD |
293 | * |
294 | * Output: |
295 | * |
296 | * paud.flags; * PITCH - pitch is supported |
297 | * * INPUT - input is supported |
298 | * * OUTPUT - output is supported |
299 | * * MONITOR - monitor is supported |
300 | * * VOLUME - volume is supported |
301 | * * VOLUME_DELAY - volume delay is supported |
302 | * * BALANCE - balance is supported |
303 | * * BALANCE_DELAY - balance delay is supported |
304 | * * TREBLE - treble control is supported |
305 | * * BASS - bass control is supported |
306 | * * BESTFIT_PROVIDED - best fit returned |
307 | * * LOAD_CODE - DSP load needed |
308 | * paud.rc; * NO_PLAY - DSP code can't do play requests |
309 | * * NO_RECORD - DSP code can't do record requests |
310 | * * INVALID_REQUEST - request was invalid |
311 | * * CONFLICT - conflict with open's flags |
312 | * * OVERLOADED - out of DSP MIPS or memory |
313 | * paud.position_resolution; * smallest increment for position |
314 | */ |
315 | |
316 | paud_init.srate = spec->freq; |
317 | paud_init.mode = PCM; |
318 | paud_init.operation = PLAY; |
319 | paud_init.channels = spec->channels; |
320 | |
321 | /* Try for a closest match on audio format */ |
322 | format = 0; |
323 | for ( test_format = SDL_FirstAudioFormat(spec->format); |
324 | ! format && test_format; ) { |
325 | #ifdef DEBUG_AUDIO |
326 | fprintf(stderr, "Trying format 0x%4.4x\n", test_format); |
327 | #endif |
328 | switch ( test_format ) { |
329 | case AUDIO_U8: |
330 | bytes_per_sample = 1; |
331 | paud_init.bits_per_sample = 8; |
332 | paud_init.flags = TWOS_COMPLEMENT | FIXED; |
333 | format = 1; |
334 | break; |
335 | case AUDIO_S8: |
336 | bytes_per_sample = 1; |
337 | paud_init.bits_per_sample = 8; |
338 | paud_init.flags = SIGNED | |
339 | TWOS_COMPLEMENT | FIXED; |
340 | format = 1; |
341 | break; |
342 | case AUDIO_S16LSB: |
343 | bytes_per_sample = 2; |
344 | paud_init.bits_per_sample = 16; |
345 | paud_init.flags = SIGNED | |
346 | TWOS_COMPLEMENT | FIXED; |
347 | format = 1; |
348 | break; |
349 | case AUDIO_S16MSB: |
350 | bytes_per_sample = 2; |
351 | paud_init.bits_per_sample = 16; |
352 | paud_init.flags = BIG_ENDIAN | |
353 | SIGNED | |
354 | TWOS_COMPLEMENT | FIXED; |
355 | format = 1; |
356 | break; |
357 | case AUDIO_U16LSB: |
358 | bytes_per_sample = 2; |
359 | paud_init.bits_per_sample = 16; |
360 | paud_init.flags = TWOS_COMPLEMENT | FIXED; |
361 | format = 1; |
362 | break; |
363 | case AUDIO_U16MSB: |
364 | bytes_per_sample = 2; |
365 | paud_init.bits_per_sample = 16; |
366 | paud_init.flags = BIG_ENDIAN | |
367 | TWOS_COMPLEMENT | FIXED; |
368 | format = 1; |
369 | break; |
370 | default: |
371 | break; |
372 | } |
373 | if ( ! format ) { |
374 | test_format = SDL_NextAudioFormat(); |
375 | } |
376 | } |
377 | if ( format == 0 ) { |
378 | #ifdef DEBUG_AUDIO |
379 | fprintf(stderr, "Couldn't find any hardware audio formats\n"); |
380 | #endif |
381 | SDL_SetError("Couldn't find any hardware audio formats"); |
382 | return -1; |
383 | } |
384 | spec->format = test_format; |
385 | |
386 | /* |
387 | * We know the buffer size and the max number of subsequent writes |
388 | * that can be pending. If more than one can pend, allow the application |
389 | * to do something like double buffering between our write buffer and |
390 | * the device's own buffer that we are filling with write() anyway. |
391 | * |
392 | * We calculate spec->samples like this because SDL_CalculateAudioSpec() |
393 | * will give put paud_bufinfo.write_buf_cap (or paud_bufinfo.write_buf_cap/2) |
394 | * into spec->size in return. |
395 | */ |
396 | if ( paud_bufinfo.request_buf_cap == 1 ) |
397 | { |
398 | spec->samples = paud_bufinfo.write_buf_cap |
399 | / bytes_per_sample |
400 | / spec->channels; |
401 | } |
402 | else |
403 | { |
404 | spec->samples = paud_bufinfo.write_buf_cap |
405 | / bytes_per_sample |
406 | / spec->channels |
407 | / 2; |
408 | } |
409 | paud_init.bsize = bytes_per_sample * spec->channels; |
410 | |
411 | SDL_CalculateAudioSpec(spec); |
412 | |
413 | /* |
414 | * The AIX paud device init can't modify the values of the audio_init |
415 | * structure that we pass to it. So we don't need any recalculation |
416 | * of this stuff and no reinit call as in linux dsp and dma code. |
417 | * |
418 | * /dev/paud supports all of the encoding formats, so we don't need |
419 | * to do anything like reopening the device, either. |
420 | */ |
421 | if ( ioctl(audio_fd, AUDIO_INIT, &paud_init) < 0 ) { |
422 | switch ( paud_init.rc ) |
423 | { |
424 | case 1 : |
425 | SDL_SetError("Couldn't set audio format: DSP can't do play requests"); |
426 | return -1; |
427 | break; |
428 | case 2 : |
429 | SDL_SetError("Couldn't set audio format: DSP can't do record requests"); |
430 | return -1; |
431 | break; |
432 | case 4 : |
433 | SDL_SetError("Couldn't set audio format: request was invalid"); |
434 | return -1; |
435 | break; |
436 | case 5 : |
437 | SDL_SetError("Couldn't set audio format: conflict with open's flags"); |
438 | return -1; |
439 | break; |
440 | case 6 : |
441 | SDL_SetError("Couldn't set audio format: out of DSP MIPS or memory"); |
442 | return -1; |
443 | break; |
444 | default : |
445 | SDL_SetError("Couldn't set audio format: not documented in sys/audio.h"); |
446 | return -1; |
447 | break; |
448 | } |
449 | } |
450 | |
451 | /* Allocate mixing buffer */ |
452 | mixlen = spec->size; |
453 | mixbuf = (Uint8 *)SDL_AllocAudioMem(mixlen); |
454 | if ( mixbuf == NULL ) { |
455 | return -1; |
456 | } |
457 | SDL_memset(mixbuf, spec->silence, spec->size); |
458 | |
459 | /* |
460 | * Set some paramters: full volume, first speaker that we can find. |
461 | * Ignore the other settings for now. |
462 | */ |
463 | paud_change.input = AUDIO_IGNORE; /* the new input source */ |
464 | paud_change.output = OUTPUT_1; /* EXTERNAL_SPEAKER,INTERNAL_SPEAKER,OUTPUT_1 */ |
465 | paud_change.monitor = AUDIO_IGNORE; /* the new monitor state */ |
466 | paud_change.volume = 0x7fffffff; /* volume level [0-0x7fffffff] */ |
467 | paud_change.volume_delay = AUDIO_IGNORE; /* the new volume delay */ |
468 | paud_change.balance = 0x3fffffff; /* the new balance */ |
469 | paud_change.balance_delay = AUDIO_IGNORE; /* the new balance delay */ |
470 | paud_change.treble = AUDIO_IGNORE; /* the new treble state */ |
471 | paud_change.bass = AUDIO_IGNORE; /* the new bass state */ |
472 | paud_change.pitch = AUDIO_IGNORE; /* the new pitch state */ |
473 | |
474 | paud_control.ioctl_request = AUDIO_CHANGE; |
475 | paud_control.request_info = (char*)&paud_change; |
476 | if ( ioctl(audio_fd, AUDIO_CONTROL, &paud_control) < 0 ) { |
477 | #ifdef DEBUG_AUDIO |
478 | fprintf(stderr, "Can't change audio display settings\n" ); |
479 | #endif |
480 | } |
481 | |
482 | /* |
483 | * Tell the device to expect data. Actual start will wait for |
484 | * the first write() call. |
485 | */ |
486 | paud_control.ioctl_request = AUDIO_START; |
487 | paud_control.position = 0; |
488 | if ( ioctl(audio_fd, AUDIO_CONTROL, &paud_control) < 0 ) { |
489 | #ifdef DEBUG_AUDIO |
490 | fprintf(stderr, "Can't start audio play\n" ); |
491 | #endif |
492 | SDL_SetError("Can't start audio play"); |
493 | return -1; |
494 | } |
495 | |
496 | /* Check to see if we need to use select() workaround */ |
497 | { char *workaround; |
498 | workaround = SDL_getenv("SDL_DSP_NOSELECT"); |
499 | if ( workaround ) { |
500 | frame_ticks = (float)(spec->samples*1000)/spec->freq; |
501 | next_frame = SDL_GetTicks()+frame_ticks; |
502 | } |
503 | } |
504 | |
505 | /* Get the parent process id (we're the parent of the audio thread) */ |
506 | parent = getpid(); |
507 | |
508 | /* We're ready to rock and roll. :-) */ |
509 | return 0; |
510 | } |
511 | |