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