zenilib  0.5.3.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
SDL_sunaudio.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_SUNAUDIO
24 
25 /* Allow access to a raw mixing buffer */
26 
27 #include <fcntl.h>
28 #include <errno.h>
29 #ifdef __NETBSD__
30 #include <sys/ioctl.h>
31 #include <sys/audioio.h>
32 #endif
33 #ifdef __SVR4
34 #include <sys/audioio.h>
35 #else
36 #include <sys/time.h>
37 #include <sys/types.h>
38 #endif
39 #include <unistd.h>
40 
41 #include "SDL_timer.h"
42 #include "SDL_audio.h"
43 #include "../SDL_audiomem.h"
44 #include "../SDL_audio_c.h"
45 #include "../SDL_audiodev_c.h"
46 #include "SDL_sunaudio.h"
47 
48 /* Open the audio device for playback, and don't block if busy */
49 
50 #if defined(AUDIO_GETINFO) && !defined(AUDIO_GETBUFINFO)
51 #define AUDIO_GETBUFINFO AUDIO_GETINFO
52 #endif
53 
54 /* Audio driver functions */
55 static Uint8 snd2au(int sample);
56 
57 /* Audio driver bootstrap functions */
58 static void
59 SUNAUDIO_DetectDevices(int iscapture, SDL_AddAudioDevice addfn)
60 {
61  SDL_EnumUnixAudioDevices(iscapture, 1, (int (*)(int fd)) NULL, addfn);
62 }
63 
64 #ifdef DEBUG_AUDIO
65 void
66 CheckUnderflow(_THIS)
67 {
68 #ifdef AUDIO_GETBUFINFO
69  audio_info_t info;
70  int left;
71 
72  ioctl(this->hidden->audio_fd, AUDIO_GETBUFINFO, &info);
73  left = (this->hidden->written - info.play.samples);
74  if (this->hidden->written && (left == 0)) {
75  fprintf(stderr, "audio underflow!\n");
76  }
77 #endif
78 }
79 #endif
80 
81 static void
82 SUNAUDIO_WaitDevice(_THIS)
83 {
84 #ifdef AUDIO_GETBUFINFO
85 #define SLEEP_FUDGE 10 /* 10 ms scheduling fudge factor */
86  audio_info_t info;
87  Sint32 left;
88 
89  ioctl(this->hidden->audio_fd, AUDIO_GETBUFINFO, &info);
90  left = (this->hidden->written - info.play.samples);
91  if (left > this->hidden->fragsize) {
92  Sint32 sleepy;
93 
94  sleepy = ((left - this->hidden->fragsize) / this->hidden->frequency);
95  sleepy -= SLEEP_FUDGE;
96  if (sleepy > 0) {
97  SDL_Delay(sleepy);
98  }
99  }
100 #else
101  fd_set fdset;
102 
103  FD_ZERO(&fdset);
104  FD_SET(this->hidden->audio_fd, &fdset);
105  select(this->hidden->audio_fd + 1, NULL, &fdset, NULL, NULL);
106 #endif
107 }
108 
109 static void
110 SUNAUDIO_PlayDevice(_THIS)
111 {
112  /* Write the audio data */
113  if (this->hidden->ulaw_only) {
114  /* Assuming that this->spec.freq >= 8000 Hz */
115  int accum, incr, pos;
116  Uint8 *aubuf;
117 
118  accum = 0;
119  incr = this->spec.freq / 8;
120  aubuf = this->hidden->ulaw_buf;
121  switch (this->hidden->audio_fmt & 0xFF) {
122  case 8:
123  {
124  Uint8 *sndbuf;
125 
126  sndbuf = this->hidden->mixbuf;
127  for (pos = 0; pos < this->hidden->fragsize; ++pos) {
128  *aubuf = snd2au((0x80 - *sndbuf) * 64);
129  accum += incr;
130  while (accum > 0) {
131  accum -= 1000;
132  sndbuf += 1;
133  }
134  aubuf += 1;
135  }
136  }
137  break;
138  case 16:
139  {
140  Sint16 *sndbuf;
141 
142  sndbuf = (Sint16 *) this->hidden->mixbuf;
143  for (pos = 0; pos < this->hidden->fragsize; ++pos) {
144  *aubuf = snd2au(*sndbuf / 4);
145  accum += incr;
146  while (accum > 0) {
147  accum -= 1000;
148  sndbuf += 1;
149  }
150  aubuf += 1;
151  }
152  }
153  break;
154  }
155 #ifdef DEBUG_AUDIO
156  CheckUnderflow(this);
157 #endif
158  if (write(this->hidden->audio_fd, this->hidden->ulaw_buf,
159  this->hidden->fragsize) < 0) {
160  /* Assume fatal error, for now */
161  this->enabled = 0;
162  }
163  this->hidden->written += this->hidden->fragsize;
164  } else {
165 #ifdef DEBUG_AUDIO
166  CheckUnderflow(this);
167 #endif
168  if (write(this->hidden->audio_fd, this->hidden->mixbuf,
169  this->spec.size) < 0) {
170  /* Assume fatal error, for now */
171  this->enabled = 0;
172  }
173  this->hidden->written += this->hidden->fragsize;
174  }
175 }
176 
177 static Uint8 *
178 SUNAUDIO_GetDeviceBuf(_THIS)
179 {
180  return (this->hidden->mixbuf);
181 }
182 
183 static void
184 SUNAUDIO_CloseDevice(_THIS)
185 {
186  if (this->hidden != NULL) {
187  SDL_FreeAudioMem(this->hidden->mixbuf);
188  this->hidden->mixbuf = NULL;
189  SDL_free(this->hidden->ulaw_buf);
190  this->hidden->ulaw_buf = NULL;
191  if (this->hidden->audio_fd >= 0) {
192  close(this->hidden->audio_fd);
193  this->hidden->audio_fd = -1;
194  }
195  SDL_free(this->hidden);
196  this->hidden = NULL;
197  }
198 }
199 
200 static int
201 SUNAUDIO_OpenDevice(_THIS, const char *devname, int iscapture)
202 {
203  const int flags = ((iscapture) ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT);
205  audio_info_t info;
206 
207  /* We don't care what the devname is...we'll try to open anything. */
208  /* ...but default to first name in the list... */
209  if (devname == NULL) {
210  devname = SDL_GetAudioDeviceName(0, iscapture);
211  if (devname == NULL) {
212  return SDL_SetError("No such audio device");
213  }
214  }
215 
216  /* Initialize all variables that we clean on shutdown */
217  this->hidden = (struct SDL_PrivateAudioData *)
218  SDL_malloc((sizeof *this->hidden));
219  if (this->hidden == NULL) {
220  return SDL_OutOfMemory();
221  }
222  SDL_memset(this->hidden, 0, (sizeof *this->hidden));
223 
224  /* Open the audio device */
225  this->hidden->audio_fd = open(devname, flags, 0);
226  if (this->hidden->audio_fd < 0) {
227  return SDL_SetError("Couldn't open %s: %s", devname, strerror(errno));
228  }
229 
230 #ifdef AUDIO_SETINFO
231  int enc;
232 #endif
233  int desired_freq = this->spec.freq;
234 
235  /* Determine the audio parameters from the AudioSpec */
236  switch (SDL_AUDIO_BITSIZE(this->spec.format)) {
237 
238  case 8:
239  { /* Unsigned 8 bit audio data */
240  this->spec.format = AUDIO_U8;
241 #ifdef AUDIO_SETINFO
242  enc = AUDIO_ENCODING_LINEAR8;
243 #endif
244  }
245  break;
246 
247  case 16:
248  { /* Signed 16 bit audio data */
249  this->spec.format = AUDIO_S16SYS;
250 #ifdef AUDIO_SETINFO
251  enc = AUDIO_ENCODING_LINEAR;
252 #endif
253  }
254  break;
255 
256  default:
257  {
258  /* !!! FIXME: fallback to conversion on unsupported types! */
259  return SDL_SetError("Unsupported audio format");
260  }
261  }
262  this->hidden->audio_fmt = this->spec.format;
263 
264  this->hidden->ulaw_only = 0; /* modern Suns do support linear audio */
265 #ifdef AUDIO_SETINFO
266  for (;;) {
267  audio_info_t info;
268  AUDIO_INITINFO(&info); /* init all fields to "no change" */
269 
270  /* Try to set the requested settings */
271  info.play.sample_rate = this->spec.freq;
272  info.play.channels = this->spec.channels;
273  info.play.precision = (enc == AUDIO_ENCODING_ULAW)
274  ? 8 : this->spec.format & 0xff;
275  info.play.encoding = enc;
276  if (ioctl(this->hidden->audio_fd, AUDIO_SETINFO, &info) == 0) {
277 
278  /* Check to be sure we got what we wanted */
279  if (ioctl(this->hidden->audio_fd, AUDIO_GETINFO, &info) < 0) {
280  return SDL_SetError("Error getting audio parameters: %s",
281  strerror(errno));
282  }
283  if (info.play.encoding == enc
284  && info.play.precision == (this->spec.format & 0xff)
285  && info.play.channels == this->spec.channels) {
286  /* Yow! All seems to be well! */
287  this->spec.freq = info.play.sample_rate;
288  break;
289  }
290  }
291 
292  switch (enc) {
293  case AUDIO_ENCODING_LINEAR8:
294  /* unsigned 8bit apparently not supported here */
295  enc = AUDIO_ENCODING_LINEAR;
296  this->spec.format = AUDIO_S16SYS;
297  break; /* try again */
298 
299  case AUDIO_ENCODING_LINEAR:
300  /* linear 16bit didn't work either, resort to -law */
301  enc = AUDIO_ENCODING_ULAW;
302  this->spec.channels = 1;
303  this->spec.freq = 8000;
304  this->spec.format = AUDIO_U8;
305  this->hidden->ulaw_only = 1;
306  break;
307 
308  default:
309  /* oh well... */
310  return SDL_SetError("Error setting audio parameters: %s",
311  strerror(errno));
312  }
313  }
314 #endif /* AUDIO_SETINFO */
315  this->hidden->written = 0;
316 
317  /* We can actually convert on-the-fly to U-Law */
318  if (this->hidden->ulaw_only) {
319  this->spec.freq = desired_freq;
320  this->hidden->fragsize = (this->spec.samples * 1000) /
321  (this->spec.freq / 8);
322  this->hidden->frequency = 8;
323  this->hidden->ulaw_buf = (Uint8 *) SDL_malloc(this->hidden->fragsize);
324  if (this->hidden->ulaw_buf == NULL) {
325  return SDL_OutOfMemory();
326  }
327  this->spec.channels = 1;
328  } else {
329  this->hidden->fragsize = this->spec.samples;
330  this->hidden->frequency = this->spec.freq / 1000;
331  }
332 #ifdef DEBUG_AUDIO
333  fprintf(stderr, "Audio device %s U-Law only\n",
334  this->hidden->ulaw_only ? "is" : "is not");
335  fprintf(stderr, "format=0x%x chan=%d freq=%d\n",
336  this->spec.format, this->spec.channels, this->spec.freq);
337 #endif
338 
339  /* Update the fragment size as size in bytes */
340  SDL_CalculateAudioSpec(&this->spec);
341 
342  /* Allocate mixing buffer */
343  this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->spec.size);
344  if (this->hidden->mixbuf == NULL) {
345  return SDL_OutOfMemory();
346  }
347  SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
348 
349  /* We're ready to rock and roll. :-) */
350  return 0;
351 }
352 
353 /************************************************************************/
354 /* This function (snd2au()) copyrighted: */
355 /************************************************************************/
356 /* Copyright 1989 by Rich Gopstein and Harris Corporation */
357 /* */
358 /* Permission to use, copy, modify, and distribute this software */
359 /* and its documentation for any purpose and without fee is */
360 /* hereby granted, provided that the above copyright notice */
361 /* appears in all copies and that both that copyright notice and */
362 /* this permission notice appear in supporting documentation, and */
363 /* that the name of Rich Gopstein and Harris Corporation not be */
364 /* used in advertising or publicity pertaining to distribution */
365 /* of the software without specific, written prior permission. */
366 /* Rich Gopstein and Harris Corporation make no representations */
367 /* about the suitability of this software for any purpose. It */
368 /* provided "as is" without express or implied warranty. */
369 /************************************************************************/
370 
371 static Uint8
372 snd2au(int sample)
373 {
374 
375  int mask;
376 
377  if (sample < 0) {
378  sample = -sample;
379  mask = 0x7f;
380  } else {
381  mask = 0xff;
382  }
383 
384  if (sample < 32) {
385  sample = 0xF0 | (15 - sample / 2);
386  } else if (sample < 96) {
387  sample = 0xE0 | (15 - (sample - 32) / 4);
388  } else if (sample < 224) {
389  sample = 0xD0 | (15 - (sample - 96) / 8);
390  } else if (sample < 480) {
391  sample = 0xC0 | (15 - (sample - 224) / 16);
392  } else if (sample < 992) {
393  sample = 0xB0 | (15 - (sample - 480) / 32);
394  } else if (sample < 2016) {
395  sample = 0xA0 | (15 - (sample - 992) / 64);
396  } else if (sample < 4064) {
397  sample = 0x90 | (15 - (sample - 2016) / 128);
398  } else if (sample < 8160) {
399  sample = 0x80 | (15 - (sample - 4064) / 256);
400  } else {
401  sample = 0x80;
402  }
403  return (mask & sample);
404 }
405 
406 static int
407 SUNAUDIO_Init(SDL_AudioDriverImpl * impl)
408 {
409  /* Set the function pointers */
410  impl->DetectDevices = SUNAUDIO_DetectDevices;
411  impl->OpenDevice = SUNAUDIO_OpenDevice;
412  impl->PlayDevice = SUNAUDIO_PlayDevice;
413  impl->WaitDevice = SUNAUDIO_WaitDevice;
414  impl->GetDeviceBuf = SUNAUDIO_GetDeviceBuf;
415  impl->CloseDevice = SUNAUDIO_CloseDevice;
416 
417  return 1; /* this audio target is available. */
418 }
419 
421  "audio", "UNIX /dev/audio interface", SUNAUDIO_Init, 0
422 };
423 
424 #endif /* SDL_AUDIO_DRIVER_SUNAUDIO */
425 
426 /* vi: set ts=4 sw=4 expandtab: */
void(* CloseDevice)(_THIS)
Definition: SDL_sysaudio.h:45
#define OPEN_FLAGS_INPUT
AudioBootStrap SUNAUDIO_bootstrap
int32_t Sint32
A signed 32-bit integer type.
Definition: SDL_stdinc.h:141
void(* SDL_AddAudioDevice)(const char *name)
Definition: SDL_sysaudio.h:34
GLint left
Definition: glew.h:7291
void(* WaitDevice)(_THIS)
Definition: SDL_sysaudio.h:41
void SDL_EnumUnixAudioDevices(int iscapture, int classic, int(*test)(int fd), SDL_AddAudioDevice addfn)
#define NULL
Definition: ftobjs.h:61
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
#define AUDIO_S16SYS
Definition: SDL_audio.h:123
if(!yyg->yy_init)
int(* OpenDevice)(_THIS, const char *devname, int iscapture)
Definition: SDL_sysaudio.h:39
void(* PlayDevice)(_THIS)
Definition: SDL_sysaudio.h:42
#define AUDIO_U8
Definition: SDL_audio.h:89
void(* DetectDevices)(int iscapture, SDL_AddAudioDevice addfn)
Definition: SDL_sysaudio.h:38
DECLSPEC const char *SDLCALL SDL_GetAudioDeviceName(int index, int iscapture)
Definition: SDL_audio.c:708
for(;;)
#define OPEN_FLAGS_OUTPUT
#define _THIS
#define SDL_AUDIO_BITSIZE(x)
Definition: SDL_audio.h:75
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
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
GLenum GLsizei GLsizei GLsizei GLsizei GLbitfield flags
Definition: glew.h:2767
uint8_t Uint8
An unsigned 8-bit integer type.
Definition: SDL_stdinc.h:129
GLint GLint GLint GLint GLint GLint GLint GLbitfield mask
Definition: gl2ext.h:961
int16_t Sint16
A signed 16-bit integer type.
Definition: SDL_stdinc.h:133