0
+ − 1 /*
+ − 2 SDL - Simple DirectMedia Layer
+ − 3 Copyright (C) 1997, 1998, 1999, 2000, 2001 Sam Lantinga
+ − 4
+ − 5 This library is free software; you can redistribute it and/or
+ − 6 modify it under the terms of the GNU Library General Public
+ − 7 License as published by the Free Software Foundation; either
+ − 8 version 2 of the License, or (at your option) any later version.
+ − 9
+ − 10 This library is distributed in the hope that it will be useful,
+ − 11 but WITHOUT ANY WARRANTY; without even the implied warranty of
+ − 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ − 13 Library General Public License for more details.
+ − 14
+ − 15 You should have received a copy of the GNU Library General Public
+ − 16 License along with this library; if not, write to the Free
+ − 17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ − 18
+ − 19 Sam Lantinga
+ − 20 slouken@devolution.com
+ − 21 */
+ − 22
+ − 23 #ifdef SAVE_RCSID
+ − 24 static char rcsid =
+ − 25 "@(#) $Id$";
+ − 26 #endif
+ − 27
+ − 28 /* Allow access to an ESD network stream mixing buffer */
+ − 29
+ − 30 #ifdef ESD_SUPPORT
+ − 31
+ − 32 #include <stdlib.h>
+ − 33 #include <stdio.h>
+ − 34 #include <string.h>
+ − 35 #include <errno.h>
+ − 36 #include <signal.h>
+ − 37 #include <unistd.h>
+ − 38
+ − 39 #include <esd.h>
+ − 40
+ − 41 #include "SDL_audio.h"
+ − 42 #include "SDL_error.h"
+ − 43 #include "SDL_audiomem.h"
+ − 44 #include "SDL_audio_c.h"
+ − 45 #include "SDL_timer.h"
+ − 46 #include "SDL_audiodev_c.h"
+ − 47 #include "SDL_esdaudio.h"
+ − 48
+ − 49 /* The tag name used by ESD audio */
+ − 50 #define ESD_DRIVER_NAME "esd"
+ − 51
+ − 52 /* Audio driver functions */
+ − 53 static int ESD_OpenAudio(_THIS, SDL_AudioSpec *spec);
+ − 54 static void ESD_WaitAudio(_THIS);
+ − 55 static void ESD_PlayAudio(_THIS);
+ − 56 static Uint8 *ESD_GetAudioBuf(_THIS);
+ − 57 static void ESD_CloseAudio(_THIS);
+ − 58
+ − 59 /* Audio driver bootstrap functions */
+ − 60
+ − 61 static int Audio_Available(void)
+ − 62 {
+ − 63 int connection;
+ − 64 int available;
+ − 65
+ − 66 available = 0;
+ − 67 connection = esd_open_sound(NULL);
+ − 68 if ( connection >= 0 ) {
+ − 69 available = 1;
+ − 70 esd_close(connection);
+ − 71 }
+ − 72 return(available);
+ − 73 }
+ − 74
+ − 75 static void Audio_DeleteDevice(SDL_AudioDevice *device)
+ − 76 {
+ − 77 free(device->hidden);
+ − 78 free(device);
+ − 79 }
+ − 80
+ − 81 static SDL_AudioDevice *Audio_CreateDevice(int devindex)
+ − 82 {
+ − 83 SDL_AudioDevice *this;
+ − 84
+ − 85 /* Initialize all variables that we clean on shutdown */
+ − 86 this = (SDL_AudioDevice *)malloc(sizeof(SDL_AudioDevice));
+ − 87 if ( this ) {
+ − 88 memset(this, 0, (sizeof *this));
+ − 89 this->hidden = (struct SDL_PrivateAudioData *)
+ − 90 malloc((sizeof *this->hidden));
+ − 91 }
+ − 92 if ( (this == NULL) || (this->hidden == NULL) ) {
+ − 93 SDL_OutOfMemory();
+ − 94 if ( this ) {
+ − 95 free(this);
+ − 96 }
+ − 97 return(0);
+ − 98 }
+ − 99 memset(this->hidden, 0, (sizeof *this->hidden));
+ − 100 audio_fd = -1;
+ − 101
+ − 102 /* Set the function pointers */
+ − 103 this->OpenAudio = ESD_OpenAudio;
+ − 104 this->WaitAudio = ESD_WaitAudio;
+ − 105 this->PlayAudio = ESD_PlayAudio;
+ − 106 this->GetAudioBuf = ESD_GetAudioBuf;
+ − 107 this->CloseAudio = ESD_CloseAudio;
+ − 108
+ − 109 this->free = Audio_DeleteDevice;
+ − 110
+ − 111 return this;
+ − 112 }
+ − 113
+ − 114 AudioBootStrap ESD_bootstrap = {
+ − 115 ESD_DRIVER_NAME, "Enlightened Sound Daemon",
+ − 116 Audio_Available, Audio_CreateDevice
+ − 117 };
+ − 118
+ − 119 /* This function waits until it is possible to write a full sound buffer */
+ − 120 static void ESD_WaitAudio(_THIS)
+ − 121 {
+ − 122 Sint32 ticks;
+ − 123
+ − 124 /* Check to see if the thread-parent process is still alive */
+ − 125 { static int cnt = 0;
+ − 126 /* Note that this only works with thread implementations
+ − 127 that use a different process id for each thread.
+ − 128 */
+ − 129 if (parent && (((++cnt)%10) == 0)) { /* Check every 10 loops */
+ − 130 if ( kill(parent, 0) < 0 ) {
+ − 131 this->enabled = 0;
+ − 132 }
+ − 133 }
+ − 134 }
+ − 135
+ − 136 /* Use timer for general audio synchronization */
+ − 137 ticks = ((Sint32)(next_frame - SDL_GetTicks()))-FUDGE_TICKS;
+ − 138 if ( ticks > 0 ) {
+ − 139 SDL_Delay(ticks);
+ − 140 }
+ − 141 }
+ − 142
+ − 143 static void ESD_PlayAudio(_THIS)
+ − 144 {
+ − 145 int written;
+ − 146
+ − 147 /* Write the audio data, checking for EAGAIN on broken audio drivers */
+ − 148 do {
+ − 149 written = write(audio_fd, mixbuf, mixlen);
+ − 150 if ( (written < 0) && ((errno == 0) || (errno == EAGAIN)) ) {
+ − 151 SDL_Delay(1); /* Let a little CPU time go by */
+ − 152 }
+ − 153 } while ( (written < 0) &&
+ − 154 ((errno == 0) || (errno == EAGAIN) || (errno == EINTR)) );
+ − 155
+ − 156 /* Set the next write frame */
+ − 157 next_frame += frame_ticks;
+ − 158
+ − 159 /* If we couldn't write, assume fatal error for now */
+ − 160 if ( written < 0 ) {
+ − 161 this->enabled = 0;
+ − 162 }
+ − 163 }
+ − 164
+ − 165 static Uint8 *ESD_GetAudioBuf(_THIS)
+ − 166 {
+ − 167 return(mixbuf);
+ − 168 }
+ − 169
+ − 170 static void ESD_CloseAudio(_THIS)
+ − 171 {
+ − 172 if ( mixbuf != NULL ) {
+ − 173 SDL_FreeAudioMem(mixbuf);
+ − 174 mixbuf = NULL;
+ − 175 }
+ − 176 if ( audio_fd >= 0 ) {
+ − 177 close(audio_fd);
+ − 178 audio_fd = -1;
+ − 179 }
+ − 180 }
+ − 181
+ − 182 /* Try to get the name of the program */
+ − 183 static char *get_progname(void)
+ − 184 {
+ − 185 char *progname = NULL;
+ − 186 #ifdef linux
+ − 187 FILE *fp;
+ − 188 static char temp[BUFSIZ];
+ − 189
+ − 190 sprintf(temp, "/proc/%d/cmdline", getpid());
+ − 191 fp = fopen(temp, "r");
+ − 192 if ( fp != NULL ) {
+ − 193 if ( fgets(temp, sizeof(temp)-1, fp) ) {
+ − 194 progname = strrchr(temp, '/');
+ − 195 if ( progname == NULL ) {
+ − 196 progname = temp;
+ − 197 } else {
+ − 198 progname = progname+1;
+ − 199 }
+ − 200 }
+ − 201 fclose(fp);
+ − 202 }
+ − 203 #endif
+ − 204 return(progname);
+ − 205 }
+ − 206
+ − 207 static int ESD_OpenAudio(_THIS, SDL_AudioSpec *spec)
+ − 208 {
+ − 209 esd_format_t format;
+ − 210
+ − 211 /* Convert audio spec to the ESD audio format */
+ − 212 format = (ESD_STREAM | ESD_PLAY);
+ − 213 switch ( spec->format & 0xFF ) {
+ − 214 case 8:
+ − 215 format |= ESD_BITS8;
+ − 216 break;
+ − 217 case 16:
+ − 218 format |= ESD_BITS16;
+ − 219 break;
+ − 220 default:
+ − 221 SDL_SetError("Unsupported ESD audio format");
+ − 222 return(-1);
+ − 223 }
+ − 224 if ( spec->channels == 1 ) {
+ − 225 format |= ESD_MONO;
+ − 226 } else {
+ − 227 format |= ESD_STEREO;
+ − 228 }
+ − 229 #if 0
+ − 230 spec->samples = ESD_BUF_SIZE; /* Darn, no way to change this yet */
+ − 231 #endif
+ − 232
+ − 233 /* Open a connection to the ESD audio server */
+ − 234 audio_fd = esd_play_stream(format, spec->freq, NULL, get_progname());
+ − 235 if ( audio_fd < 0 ) {
+ − 236 SDL_SetError("Couldn't open ESD connection");
+ − 237 return(-1);
+ − 238 }
+ − 239
+ − 240 /* Calculate the final parameters for this audio specification */
+ − 241 SDL_CalculateAudioSpec(spec);
+ − 242 frame_ticks = (float)(spec->samples*1000)/spec->freq;
+ − 243 next_frame = SDL_GetTicks()+frame_ticks;
+ − 244
+ − 245 /* Allocate mixing buffer */
+ − 246 mixlen = spec->size;
+ − 247 mixbuf = (Uint8 *)SDL_AllocAudioMem(mixlen);
+ − 248 if ( mixbuf == NULL ) {
+ − 249 return(-1);
+ − 250 }
+ − 251 memset(mixbuf, spec->silence, spec->size);
+ − 252
+ − 253 /* Get the parent process id (we're the parent of the audio thread) */
+ − 254 parent = getpid();
+ − 255
+ − 256 /* We're ready to rock and roll. :-) */
+ − 257 return(0);
+ − 258 }
+ − 259
+ − 260 #endif /* ESD_SUPPORT */