SDL  2.0
SDL_esdaudio.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_ESD
24 
25 /* Allow access to an ESD network stream mixing buffer */
26 
27 #include <sys/types.h>
28 #include <unistd.h>
29 #include <signal.h>
30 #include <errno.h>
31 #include <esd.h>
32 
33 #include "SDL_timer.h"
34 #include "SDL_audio.h"
35 #include "../SDL_audio_c.h"
36 #include "SDL_esdaudio.h"
37 
38 #ifdef SDL_AUDIO_DRIVER_ESD_DYNAMIC
39 #include "SDL_name.h"
40 #include "SDL_loadso.h"
41 #else
42 #define SDL_NAME(X) X
43 #endif
44 
45 #ifdef SDL_AUDIO_DRIVER_ESD_DYNAMIC
46 
47 static const char *esd_library = SDL_AUDIO_DRIVER_ESD_DYNAMIC;
48 static void *esd_handle = NULL;
49 
50 static int (*SDL_NAME(esd_open_sound)) (const char *host);
51 static int (*SDL_NAME(esd_close)) (int esd);
52 static int (*SDL_NAME(esd_play_stream)) (esd_format_t format, int rate,
53  const char *host, const char *name);
54 
55 #define SDL_ESD_SYM(x) { #x, (void **) (char *) &SDL_NAME(x) }
56 static struct
57 {
58  const char *name;
59  void **func;
60 } const esd_functions[] = {
61  SDL_ESD_SYM(esd_open_sound),
62  SDL_ESD_SYM(esd_close), SDL_ESD_SYM(esd_play_stream),
63 };
64 
65 #undef SDL_ESD_SYM
66 
67 static void
68 UnloadESDLibrary()
69 {
70  if (esd_handle != NULL) {
71  SDL_UnloadObject(esd_handle);
72  esd_handle = NULL;
73  }
74 }
75 
76 static int
77 LoadESDLibrary(void)
78 {
79  int i, retval = -1;
80 
81  if (esd_handle == NULL) {
82  esd_handle = SDL_LoadObject(esd_library);
83  if (esd_handle) {
84  retval = 0;
85  for (i = 0; i < SDL_arraysize(esd_functions); ++i) {
86  *esd_functions[i].func =
87  SDL_LoadFunction(esd_handle, esd_functions[i].name);
88  if (!*esd_functions[i].func) {
89  retval = -1;
90  UnloadESDLibrary();
91  break;
92  }
93  }
94  }
95  }
96  return retval;
97 }
98 
99 #else
100 
101 static void
102 UnloadESDLibrary()
103 {
104  return;
105 }
106 
107 static int
108 LoadESDLibrary(void)
109 {
110  return 0;
111 }
112 
113 #endif /* SDL_AUDIO_DRIVER_ESD_DYNAMIC */
114 
115 
116 /* This function waits until it is possible to write a full sound buffer */
117 static void
118 ESD_WaitDevice(_THIS)
119 {
120  Sint32 ticks;
121 
122  /* Check to see if the thread-parent process is still alive */
123  {
124  static int cnt = 0;
125  /* Note that this only works with thread implementations
126  that use a different process id for each thread.
127  */
128  /* Check every 10 loops */
129  if (this->hidden->parent && (((++cnt) % 10) == 0)) {
130  if (kill(this->hidden->parent, 0) < 0 && errno == ESRCH) {
132  }
133  }
134  }
135 
136  /* Use timer for general audio synchronization */
137  ticks = ((Sint32) (this->hidden->next_frame - SDL_GetTicks())) - FUDGE_TICKS;
138  if (ticks > 0) {
139  SDL_Delay(ticks);
140  }
141 }
142 
143 static void
144 ESD_PlayDevice(_THIS)
145 {
146  int written = 0;
147 
148  /* Write the audio data, checking for EAGAIN on broken audio drivers */
149  do {
150  written = write(this->hidden->audio_fd,
151  this->hidden->mixbuf, this->hidden->mixlen);
152  if ((written < 0) && ((errno == 0) || (errno == EAGAIN))) {
153  SDL_Delay(1); /* Let a little CPU time go by */
154  }
155  } while ((written < 0) &&
156  ((errno == 0) || (errno == EAGAIN) || (errno == EINTR)));
157 
158  /* Set the next write frame */
159  this->hidden->next_frame += this->hidden->frame_ticks;
160 
161  /* If we couldn't write, assume fatal error for now */
162  if (written < 0) {
164  }
165 }
166 
167 static Uint8 *
168 ESD_GetDeviceBuf(_THIS)
169 {
170  return (this->hidden->mixbuf);
171 }
172 
173 static void
174 ESD_CloseDevice(_THIS)
175 {
176  if (this->hidden->audio_fd >= 0) {
177  SDL_NAME(esd_close) (this->hidden->audio_fd);
178  }
179  SDL_free(this->hidden->mixbuf);
180  SDL_free(this->hidden);
181 }
182 
183 /* Try to get the name of the program */
184 static char *
185 get_progname(void)
186 {
187  char *progname = NULL;
188 #ifdef __LINUX__
189  FILE *fp;
190  static char temp[BUFSIZ];
191 
192  SDL_snprintf(temp, SDL_arraysize(temp), "/proc/%d/cmdline", getpid());
193  fp = fopen(temp, "r");
194  if (fp != NULL) {
195  if (fgets(temp, sizeof(temp) - 1, fp)) {
196  progname = SDL_strrchr(temp, '/');
197  if (progname == NULL) {
198  progname = temp;
199  } else {
200  progname = progname + 1;
201  }
202  }
203  fclose(fp);
204  }
205 #endif
206  return (progname);
207 }
208 
209 
210 static int
211 ESD_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
212 {
213  esd_format_t format = (ESD_STREAM | ESD_PLAY);
214  SDL_AudioFormat test_format = 0;
215  int found = 0;
216 
217  /* Initialize all variables that we clean on shutdown */
218  this->hidden = (struct SDL_PrivateAudioData *)
219  SDL_malloc((sizeof *this->hidden));
220  if (this->hidden == NULL) {
221  return SDL_OutOfMemory();
222  }
223  SDL_zerop(this->hidden);
224  this->hidden->audio_fd = -1;
225 
226  /* Convert audio spec to the ESD audio format */
227  /* Try for a closest match on audio format */
228  for (test_format = SDL_FirstAudioFormat(this->spec.format);
229  !found && test_format; test_format = SDL_NextAudioFormat()) {
230 #ifdef DEBUG_AUDIO
231  fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
232 #endif
233  found = 1;
234  switch (test_format) {
235  case AUDIO_U8:
236  format |= ESD_BITS8;
237  break;
238  case AUDIO_S16SYS:
239  format |= ESD_BITS16;
240  break;
241  default:
242  found = 0;
243  break;
244  }
245  }
246 
247  if (!found) {
248  return SDL_SetError("Couldn't find any hardware audio formats");
249  }
250 
251  if (this->spec.channels == 1) {
252  format |= ESD_MONO;
253  } else {
254  format |= ESD_STEREO;
255  }
256 #if 0
257  this->spec.samples = ESD_BUF_SIZE; /* Darn, no way to change this yet */
258 #endif
259 
260  /* Open a connection to the ESD audio server */
261  this->hidden->audio_fd =
262  SDL_NAME(esd_play_stream) (format, this->spec.freq, NULL,
263  get_progname());
264 
265  if (this->hidden->audio_fd < 0) {
266  return SDL_SetError("Couldn't open ESD connection");
267  }
268 
269  /* Calculate the final parameters for this audio specification */
271  this->hidden->frame_ticks =
272  (float) (this->spec.samples * 1000) / this->spec.freq;
273  this->hidden->next_frame = SDL_GetTicks() + this->hidden->frame_ticks;
274 
275  /* Allocate mixing buffer */
276  this->hidden->mixlen = this->spec.size;
277  this->hidden->mixbuf = (Uint8 *) SDL_malloc(this->hidden->mixlen);
278  if (this->hidden->mixbuf == NULL) {
279  return SDL_OutOfMemory();
280  }
281  SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
282 
283  /* Get the parent process id (we're the parent of the audio thread) */
284  this->hidden->parent = getpid();
285 
286  /* We're ready to rock and roll. :-) */
287  return 0;
288 }
289 
290 static void
291 ESD_Deinitialize(void)
292 {
293  UnloadESDLibrary();
294 }
295 
296 static int
297 ESD_Init(SDL_AudioDriverImpl * impl)
298 {
299  if (LoadESDLibrary() < 0) {
300  return 0;
301  } else {
302  int connection = 0;
303 
304  /* Don't start ESD if it's not running */
305  SDL_setenv("ESD_NO_SPAWN", "1", 0);
306 
307  connection = SDL_NAME(esd_open_sound) (NULL);
308  if (connection < 0) {
309  UnloadESDLibrary();
310  SDL_SetError("ESD: esd_open_sound failed (no audio server?)");
311  return 0;
312  }
313  SDL_NAME(esd_close) (connection);
314  }
315 
316  /* Set the function pointers */
317  impl->OpenDevice = ESD_OpenDevice;
318  impl->PlayDevice = ESD_PlayDevice;
319  impl->WaitDevice = ESD_WaitDevice;
320  impl->GetDeviceBuf = ESD_GetDeviceBuf;
321  impl->CloseDevice = ESD_CloseDevice;
322  impl->Deinitialize = ESD_Deinitialize;
323  impl->OnlyHasDefaultOutputDevice = 1;
324 
325  return 1; /* this audio target is available. */
326 }
327 
328 
330  "esd", "Enlightened Sound Daemon", ESD_Init, 0
331 };
332 
333 #endif /* SDL_AUDIO_DRIVER_ESD */
334 
335 /* vi: set ts=4 sw=4 expandtab: */
SDL_AudioFormat SDL_FirstAudioFormat(SDL_AudioFormat format)
Definition: SDL_audio.c:1584
static int ticks
Definition: testtimer.c:24
AudioBootStrap ESD_bootstrap
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 SDL_LoadObject
#define AUDIO_S16SYS
Definition: SDL_audio.h:123
#define SDL_UnloadObject
#define SDL_zerop(x)
Definition: SDL_stdinc.h:417
SDL_AudioFormat SDL_NextAudioFormat(void)
Definition: SDL_audio.c:1596
GLuint const GLchar * name
#define SDL_setenv
GLint GLint GLsizei GLsizei GLsizei GLint GLenum format
Definition: SDL_opengl.h:1572
SDL_AudioSpec spec
Definition: loopwave.c:31
SDL_bool retval
#define AUDIO_U8
Definition: SDL_audio.h:89
EGLImageKHR EGLint EGLint * handle
Definition: eglext.h:937
Uint32 SDL_GetTicks(void)
Get the number of milliseconds since the SDL library initialization.
Uint8 channels
Definition: SDL_audio.h:181
#define _THIS
uint8_t Uint8
Definition: SDL_stdinc.h:157
#define SDL_free
void(* Deinitialize)(void)
Definition: SDL_sysaudio.h:83
void SDL_CalculateAudioSpec(SDL_AudioSpec *spec)
Definition: SDL_audio.c:1605
int32_t Sint32
Definition: SDL_stdinc.h:175
#define FUDGE_TICKS
Definition: SDL_artsaudio.h:49
#define SDL_Delay
return Display return Display Bool Bool int int int return Display XEvent Bool(*) XPointer return Display return Display Drawable _Xconst char unsigned int unsigned int return Display Pixmap Pixmap XColor XColor unsigned int unsigned int return Display _Xconst char char int char return Display Visual unsigned int int int char unsigned int unsigned int in i)
Definition: SDL_x11sym.h:50
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_NAME(X)
Definition: SDL_name.h:29
#define SDL_SetError
void(* CloseDevice)(_THIS)
Definition: SDL_sysaudio.h:79
GLenum func
SDL_AudioFormat format
Definition: SDL_audio.h:180
Uint8 *(* GetDeviceBuf)(_THIS)
Definition: SDL_sysaudio.h:75
#define SDL_snprintf
#define SDL_arraysize(array)
Definition: SDL_stdinc.h:93
#define SDL_malloc
void * SDL_LoadFunction(void *handle, const char *name)
#define SDL_strrchr
#define SDL_memset