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