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