zenilib  0.5.3.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
SDL_paudio.c
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2013 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_config.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_audiomem.h"
39 #include "../SDL_audio_c.h"
40 #include "SDL_paudio.h"
41 
42 #define DEBUG_AUDIO 0
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
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
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
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 =
137  ((Sint32) (this->hidden->next_frame - SDL_GetTicks())) -
138  FUDGE_TICKS;
139  if (ticks > 0) {
140  SDL_Delay(ticks);
141  }
142  } else {
143  audio_buffer paud_bufinfo;
144 
145  /* Use select() for audio synchronization */
146  struct timeval timeout;
147  FD_ZERO(&fdset);
148  FD_SET(this->hidden->audio_fd, &fdset);
149 
150  if (ioctl(this->hidden->audio_fd, AUDIO_BUFFER, &paud_bufinfo) < 0) {
151 #ifdef DEBUG_AUDIO
152  fprintf(stderr, "Couldn't get audio buffer information\n");
153 #endif
154  timeout.tv_sec = 10;
155  timeout.tv_usec = 0;
156  } else {
157  long ms_in_buf = paud_bufinfo.write_buf_time;
158  timeout.tv_sec = ms_in_buf / 1000;
159  ms_in_buf = ms_in_buf - timeout.tv_sec * 1000;
160  timeout.tv_usec = ms_in_buf * 1000;
161 #ifdef DEBUG_AUDIO
162  fprintf(stderr,
163  "Waiting for write_buf_time=%ld,%ld\n",
164  timeout.tv_sec, timeout.tv_usec);
165 #endif
166  }
167 
168 #ifdef DEBUG_AUDIO
169  fprintf(stderr, "Waiting for audio to get ready\n");
170 #endif
171  if (select(this->hidden->audio_fd + 1, NULL, &fdset, NULL, &timeout)
172  <= 0) {
173  const char *message =
174  "Audio timeout - buggy audio driver? (disabled)";
175  /*
176  * In general we should never print to the screen,
177  * but in this case we have no other way of letting
178  * the user know what happened.
179  */
180  fprintf(stderr, "SDL: %s - %s\n", strerror(errno), message);
181  this->enabled = 0;
182  /* Don't try to close - may hang */
183  this->hidden->audio_fd = -1;
184 #ifdef DEBUG_AUDIO
185  fprintf(stderr, "Done disabling audio\n");
186 #endif
187  }
188 #ifdef DEBUG_AUDIO
189  fprintf(stderr, "Ready!\n");
190 #endif
191  }
192 }
193 
194 static void
195 PAUDIO_PlayDevice(_THIS)
196 {
197  int written = 0;
198  const Uint8 *mixbuf = this->hidden->mixbuf;
199  const size_t mixlen = this->hidden->mixlen;
200 
201  /* Write the audio data, checking for EAGAIN on broken audio drivers */
202  do {
203  written = write(this->hidden->audio_fd, mixbuf, mixlen);
204  if ((written < 0) && ((errno == 0) || (errno == EAGAIN))) {
205  SDL_Delay(1); /* Let a little CPU time go by */
206  }
207  } while ((written < 0) &&
208  ((errno == 0) || (errno == EAGAIN) || (errno == EINTR)));
209 
210  /* If timer synchronization is enabled, set the next write frame */
211  if (this->hidden->frame_ticks) {
212  this->hidden->next_frame += this->hidden->frame_ticks;
213  }
214 
215  /* If we couldn't write, assume fatal error for now */
216  if (written < 0) {
217  this->enabled = 0;
218  }
219 #ifdef DEBUG_AUDIO
220  fprintf(stderr, "Wrote %d bytes of audio data\n", written);
221 #endif
222 }
223 
224 static Uint8 *
225 PAUDIO_GetDeviceBuf(_THIS)
226 {
227  return this->hidden->mixbuf;
228 }
229 
230 static void
231 PAUDIO_CloseDevice(_THIS)
232 {
233  if (this->hidden != NULL) {
234  SDL_FreeAudioMem(this->hidden->mixbuf);
235  this->hidden->mixbuf = NULL;
236  if (this->hidden->audio_fd >= 0) {
237  close(this->hidden->audio_fd);
238  this->hidden->audio_fd = -1;
239  }
240  SDL_free(this->hidden);
241  this->hidden = NULL;
242  }
243 }
244 
245 static int
246 PAUDIO_OpenDevice(_THIS, const char *devname, int iscapture)
247 {
248  const char *workaround = SDL_getenv("SDL_DSP_NOSELECT");
249  char audiodev[1024];
250  const char *err = NULL;
251  int format;
252  int bytes_per_sample;
253  SDL_AudioFormat test_format;
254  audio_init paud_init;
255  audio_buffer paud_bufinfo;
256  audio_status paud_status;
257  audio_control paud_control;
258  audio_change paud_change;
259  int fd = -1;
260 
261  /* Initialize all variables that we clean on shutdown */
262  this->hidden = (struct SDL_PrivateAudioData *)
263  SDL_malloc((sizeof *this->hidden));
264  if (this->hidden == NULL) {
265  return SDL_OutOfMemory();
266  }
267  SDL_memset(this->hidden, 0, (sizeof *this->hidden));
268 
269  /* Open the audio device */
270  fd = OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 0);
271  this->hidden->audio_fd = fd;
272  if (fd < 0) {
273  PAUDIO_CloseDevice(this);
274  return SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno));
275  }
276 
277  /*
278  * We can't set the buffer size - just ask the device for the maximum
279  * that we can have.
280  */
281  if (ioctl(fd, AUDIO_BUFFER, &paud_bufinfo) < 0) {
282  PAUDIO_CloseDevice(this);
283  return SDL_SetError("Couldn't get audio buffer information");
284  }
285 
286  if (this->spec.channels > 1)
287  this->spec.channels = 2;
288  else
289  this->spec.channels = 1;
290 
291  /*
292  * Fields in the audio_init structure:
293  *
294  * Ignored by us:
295  *
296  * paud.loadpath[LOAD_PATH]; * DSP code to load, MWave chip only?
297  * paud.slot_number; * slot number of the adapter
298  * paud.device_id; * adapter identification number
299  *
300  * Input:
301  *
302  * paud.srate; * the sampling rate in Hz
303  * paud.bits_per_sample; * 8, 16, 32, ...
304  * paud.bsize; * block size for this rate
305  * paud.mode; * ADPCM, PCM, MU_LAW, A_LAW, SOURCE_MIX
306  * paud.channels; * 1=mono, 2=stereo
307  * paud.flags; * FIXED - fixed length data
308  * * LEFT_ALIGNED, RIGHT_ALIGNED (var len only)
309  * * TWOS_COMPLEMENT - 2's complement data
310  * * SIGNED - signed? comment seems wrong in sys/audio.h
311  * * BIG_ENDIAN
312  * paud.operation; * PLAY, RECORD
313  *
314  * Output:
315  *
316  * paud.flags; * PITCH - pitch is supported
317  * * INPUT - input is supported
318  * * OUTPUT - output is supported
319  * * MONITOR - monitor is supported
320  * * VOLUME - volume is supported
321  * * VOLUME_DELAY - volume delay is supported
322  * * BALANCE - balance is supported
323  * * BALANCE_DELAY - balance delay is supported
324  * * TREBLE - treble control is supported
325  * * BASS - bass control is supported
326  * * BESTFIT_PROVIDED - best fit returned
327  * * LOAD_CODE - DSP load needed
328  * paud.rc; * NO_PLAY - DSP code can't do play requests
329  * * NO_RECORD - DSP code can't do record requests
330  * * INVALID_REQUEST - request was invalid
331  * * CONFLICT - conflict with open's flags
332  * * OVERLOADED - out of DSP MIPS or memory
333  * paud.position_resolution; * smallest increment for position
334  */
335 
336  paud_init.srate = this->spec.freq;
337  paud_init.mode = PCM;
338  paud_init.operation = PLAY;
339  paud_init.channels = this->spec.channels;
340 
341  /* Try for a closest match on audio format */
342  format = 0;
343  for (test_format = SDL_FirstAudioFormat(this->spec.format);
344  !format && test_format;) {
345 #ifdef DEBUG_AUDIO
346  fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
347 #endif
348  switch (test_format) {
349  case AUDIO_U8:
350  bytes_per_sample = 1;
351  paud_init.bits_per_sample = 8;
352  paud_init.flags = TWOS_COMPLEMENT | FIXED;
353  format = 1;
354  break;
355  case AUDIO_S8:
356  bytes_per_sample = 1;
357  paud_init.bits_per_sample = 8;
358  paud_init.flags = SIGNED | TWOS_COMPLEMENT | FIXED;
359  format = 1;
360  break;
361  case AUDIO_S16LSB:
362  bytes_per_sample = 2;
363  paud_init.bits_per_sample = 16;
364  paud_init.flags = SIGNED | TWOS_COMPLEMENT | FIXED;
365  format = 1;
366  break;
367  case AUDIO_S16MSB:
368  bytes_per_sample = 2;
369  paud_init.bits_per_sample = 16;
370  paud_init.flags = BIG_ENDIAN | SIGNED | TWOS_COMPLEMENT | FIXED;
371  format = 1;
372  break;
373  case AUDIO_U16LSB:
374  bytes_per_sample = 2;
375  paud_init.bits_per_sample = 16;
376  paud_init.flags = TWOS_COMPLEMENT | FIXED;
377  format = 1;
378  break;
379  case AUDIO_U16MSB:
380  bytes_per_sample = 2;
381  paud_init.bits_per_sample = 16;
382  paud_init.flags = BIG_ENDIAN | TWOS_COMPLEMENT | FIXED;
383  format = 1;
384  break;
385  default:
386  break;
387  }
388  if (!format) {
389  test_format = SDL_NextAudioFormat();
390  }
391  }
392  if (format == 0) {
393 #ifdef DEBUG_AUDIO
394  fprintf(stderr, "Couldn't find any hardware audio formats\n");
395 #endif
396  PAUDIO_CloseDevice(this);
397  return SDL_SetError("Couldn't find any hardware audio formats");
398  }
399  this->spec.format = test_format;
400 
401  /*
402  * We know the buffer size and the max number of subsequent writes
403  * that can be pending. If more than one can pend, allow the application
404  * to do something like double buffering between our write buffer and
405  * the device's own buffer that we are filling with write() anyway.
406  *
407  * We calculate this->spec.samples like this because
408  * SDL_CalculateAudioSpec() will give put paud_bufinfo.write_buf_cap
409  * (or paud_bufinfo.write_buf_cap/2) into this->spec.size in return.
410  */
411  if (paud_bufinfo.request_buf_cap == 1) {
412  this->spec.samples = paud_bufinfo.write_buf_cap
413  / bytes_per_sample / this->spec.channels;
414  } else {
415  this->spec.samples = paud_bufinfo.write_buf_cap
416  / bytes_per_sample / this->spec.channels / 2;
417  }
418  paud_init.bsize = bytes_per_sample * this->spec.channels;
419 
420  SDL_CalculateAudioSpec(&this->spec);
421 
422  /*
423  * The AIX paud device init can't modify the values of the audio_init
424  * structure that we pass to it. So we don't need any recalculation
425  * of this stuff and no reinit call as in linux dsp code.
426  *
427  * /dev/paud supports all of the encoding formats, so we don't need
428  * to do anything like reopening the device, either.
429  */
430  if (ioctl(fd, AUDIO_INIT, &paud_init) < 0) {
431  switch (paud_init.rc) {
432  case 1:
433  err = "Couldn't set audio format: DSP can't do play requests";
434  break;
435  case 2:
436  err = "Couldn't set audio format: DSP can't do record requests";
437  break;
438  case 4:
439  err = "Couldn't set audio format: request was invalid";
440  break;
441  case 5:
442  err = "Couldn't set audio format: conflict with open's flags";
443  break;
444  case 6:
445  err = "Couldn't set audio format: out of DSP MIPS or memory";
446  break;
447  default:
448  err = "Couldn't set audio format: not documented in sys/audio.h";
449  break;
450  }
451  }
452 
453  if (err != NULL) {
454  PAUDIO_CloseDevice(this);
455  return SDL_SetError("Paudio: %s", err);
456  }
457 
458  /* Allocate mixing buffer */
459  this->hidden->mixlen = this->spec.size;
460  this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
461  if (this->hidden->mixbuf == NULL) {
462  PAUDIO_CloseDevice(this);
463  return SDL_OutOfMemory();
464  }
465  SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
466 
467  /*
468  * Set some paramters: full volume, first speaker that we can find.
469  * Ignore the other settings for now.
470  */
471  paud_change.input = AUDIO_IGNORE; /* the new input source */
472  paud_change.output = OUTPUT_1; /* EXTERNAL_SPEAKER,INTERNAL_SPEAKER,OUTPUT_1 */
473  paud_change.monitor = AUDIO_IGNORE; /* the new monitor state */
474  paud_change.volume = 0x7fffffff; /* volume level [0-0x7fffffff] */
475  paud_change.volume_delay = AUDIO_IGNORE; /* the new volume delay */
476  paud_change.balance = 0x3fffffff; /* the new balance */
477  paud_change.balance_delay = AUDIO_IGNORE; /* the new balance delay */
478  paud_change.treble = AUDIO_IGNORE; /* the new treble state */
479  paud_change.bass = AUDIO_IGNORE; /* the new bass state */
480  paud_change.pitch = AUDIO_IGNORE; /* the new pitch state */
481 
482  paud_control.ioctl_request = AUDIO_CHANGE;
483  paud_control.request_info = (char *) &paud_change;
484  if (ioctl(fd, AUDIO_CONTROL, &paud_control) < 0) {
485 #ifdef DEBUG_AUDIO
486  fprintf(stderr, "Can't change audio display settings\n");
487 #endif
488  }
489 
490  /*
491  * Tell the device to expect data. Actual start will wait for
492  * the first write() call.
493  */
494  paud_control.ioctl_request = AUDIO_START;
495  paud_control.position = 0;
496  if (ioctl(fd, AUDIO_CONTROL, &paud_control) < 0) {
497  PAUDIO_CloseDevice(this);
498 #ifdef DEBUG_AUDIO
499  fprintf(stderr, "Can't start audio play\n");
500 #endif
501  return SDL_SetError("Can't start audio play");
502  }
503 
504  /* Check to see if we need to use select() workaround */
505  if (workaround != NULL) {
506  this->hidden->frame_ticks = (float) (this->spec.samples * 1000) /
507  this->spec.freq;
508  this->hidden->next_frame = SDL_GetTicks() + this->hidden->frame_ticks;
509  }
510 
511  /* We're ready to rock and roll. :-) */
512  return 0;
513 }
514 
515 static int
516 PAUDIO_Init(SDL_AudioDriverImpl * impl)
517 {
518  /* !!! FIXME: not right for device enum? */
519  int fd = OpenAudioPath(NULL, 0, OPEN_FLAGS, 0);
520  if (fd < 0) {
521  SDL_SetError("PAUDIO: Couldn't open audio device");
522  return 0;
523  }
524  close(fd);
525 
526  /* Set the function pointers */
527  impl->OpenDevice = DSP_OpenDevice;
528  impl->PlayDevice = DSP_PlayDevice;
529  impl->PlayDevice = DSP_WaitDevice;
530  impl->GetDeviceBuf = DSP_GetDeviceBuf;
531  impl->CloseDevice = DSP_CloseDevice;
532  impl->OnlyHasDefaultOutputDevice = 1; /* !!! FIXME: add device enum! */
533 
534  return 1; /* this audio target is available. */
535 }
536 
538  "paud", "AIX Paudio", PAUDIO_Init, 0
539 };
540 
541 #endif /* SDL_AUDIO_DRIVER_PAUDIO */
542 
543 /* vi: set ts=4 sw=4 expandtab: */
void(* CloseDevice)(_THIS)
Definition: SDL_sysaudio.h:45
SDL_AudioFormat SDL_FirstAudioFormat(SDL_AudioFormat format)
Definition: SDL_audio.c:1226
int32_t Sint32
A signed 32-bit integer type.
Definition: SDL_stdinc.h:141
GLsizei GLenum GLuint GLuint GLsizei GLchar * message
Definition: glew.h:2540
#define NULL
Definition: ftobjs.h:61
DECLSPEC int SDLCALL SDL_snprintf(char *text, size_t maxlen, const char *fmt,...)
Definition: SDL_string.c:1277
#define AUDIO_U16LSB
Definition: SDL_audio.h:91
GLenum GLsizei const GLuint GLboolean enabled
Definition: glew.h:2538
Uint8 *(* GetDeviceBuf)(_THIS)
Definition: SDL_sysaudio.h:43
#define SDL_FreeAudioMem
Definition: SDL_audiomem.h:24
DECLSPEC void SDLCALL SDL_free(void *mem)
Uint16 SDL_AudioFormat
Audio format flags.
Definition: SDL_audio.h:64
int(* OpenDevice)(_THIS, const char *devname, int iscapture)
Definition: SDL_sysaudio.h:39
GLsizei const GLchar *const * path
Definition: glew.h:5828
SDL_AudioFormat SDL_NextAudioFormat(void)
Definition: SDL_audio.c:1238
void(* PlayDevice)(_THIS)
Definition: SDL_sysaudio.h:42
DECLSPEC Uint32 SDLCALL SDL_GetTicks(void)
Get the number of milliseconds since the SDL library initialization.
Definition: SDL_systimer.c:44
#define AUDIO_U8
Definition: SDL_audio.h:89
#define _THIS
AudioBootStrap PAUDIO_bootstrap
DECLSPEC size_t SDLCALL SDL_strlcpy(char *dst, const char *src, size_t maxlen)
Definition: SDL_string.c:448
DECLSPEC void SDLCALL SDL_Delay(Uint32 ms)
Wait a specified number of milliseconds before returning.
Definition: SDL_systimer.c:70
DECLSPEC void *SDLCALL SDL_memset(void *dst, int c, size_t len)
Definition: SDL_string.c:261
GLint GLenum GLsizei GLsizei GLsizei GLint GLenum format
Definition: gl2ext.h:845
DECLSPEC int SDLCALL SDL_SetError(const char *fmt,...)
Definition: SDL_error.c:53
DECLSPEC void *SDLCALL SDL_malloc(size_t size)
void SDL_CalculateAudioSpec(SDL_AudioSpec *spec)
Definition: SDL_audio.c:1247
#define SDL_AllocAudioMem
Definition: SDL_audiomem.h:23
GLbitfield GLuint64 timeout
Definition: glew.h:5938
#define FUDGE_TICKS
Definition: SDL_artsaudio.h:49
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
#define SDL_arraysize(array)
Definition: SDL_stdinc.h:83
GLenum GLsizei GLsizei GLsizei GLsizei GLbitfield flags
Definition: glew.h:2767
#define AUDIO_S16MSB
Definition: SDL_audio.h:94
uint8_t Uint8
An unsigned 8-bit integer type.
Definition: SDL_stdinc.h:129
#define AUDIO_S16LSB
Definition: SDL_audio.h:92
DECLSPEC char *SDLCALL SDL_getenv(const char *name)
Definition: SDL_getenv.c:179
#define AUDIO_S8
Definition: SDL_audio.h:90
#define AUDIO_U16MSB
Definition: SDL_audio.h:93