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