• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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