src/audio/sndio/SDL_sndioaudio.c
author Ozkan Sezer <sezeroz@gmail.com>
Sat, 24 Mar 2018 22:41:17 +0300
branchSDL-1.2
changeset 11972 0b5ac2b90ab1
parent 6353 dfcbd0d9209c
child 13925 d3f74efa82e7
permissions -rw-r--r--
backfort fix for bug #3739: handle %lu, %li and %ld in SDL_SetError.
     1 /*
     2  * Copyright (c) 2008 Jacob Meuser <jakemsr@sdf.lonestar.org>
     3  *
     4  * Permission to use, copy, modify, and distribute this software for any
     5  * purpose with or without fee is hereby granted, provided that the above
     6  * copyright notice and this permission notice appear in all copies.
     7  *
     8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
    10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
    11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
    12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
    13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
    14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
    15  */
    16 
    17 #include "SDL_config.h"
    18 
    19 /* Allow access to a raw mixing buffer */
    20 
    21 #ifdef HAVE_SIGNAL_H
    22 #include <signal.h>
    23 #endif
    24 #include <unistd.h>
    25 
    26 #include "SDL_timer.h"
    27 #include "SDL_audio.h"
    28 #include "../SDL_audiomem.h"
    29 #include "../SDL_audio_c.h"
    30 #include "../SDL_audiodev_c.h"
    31 #include "SDL_sndioaudio.h"
    32 
    33 /* The tag name used by sndio audio */
    34 #define SNDIO_DRIVER_NAME         "sndio"
    35 
    36 /* Audio driver functions */
    37 static int SNDIO_OpenAudio(_THIS, SDL_AudioSpec *spec);
    38 static void SNDIO_WaitAudio(_THIS);
    39 static void SNDIO_PlayAudio(_THIS);
    40 static Uint8 *SNDIO_GetAudioBuf(_THIS);
    41 static void SNDIO_CloseAudio(_THIS);
    42 
    43 /* Audio driver bootstrap functions */
    44 
    45 static int Audio_Available(void)
    46 {
    47 	struct sio_hdl *this_hdl;
    48 	int available = 0;
    49 
    50 	if ( (this_hdl = sio_open(NULL, SIO_PLAY, 0)) != NULL ) {
    51 		sio_close(this_hdl);
    52 		available = 1;
    53 	}
    54 
    55 	return available;
    56 }
    57 
    58 static void Audio_DeleteDevice(SDL_AudioDevice *device)
    59 {
    60 	SDL_free(device->hidden);
    61 	SDL_free(device);
    62 }
    63 
    64 static SDL_AudioDevice *Audio_CreateDevice(int devindex)
    65 {
    66 	SDL_AudioDevice *this;
    67 
    68 	/* Initialize all variables that we clean on shutdown */
    69 	this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice));
    70 	if ( this ) {
    71 		SDL_memset(this, 0, (sizeof *this));
    72 		this->hidden = (struct SDL_PrivateAudioData *)
    73 				SDL_malloc((sizeof *this->hidden));
    74 	}
    75 	if ( (this == NULL) || (this->hidden == NULL) ) {
    76 		SDL_OutOfMemory();
    77 		if ( this ) {
    78 			SDL_free(this);
    79 		}
    80 		return(0);
    81 	}
    82 	SDL_memset(this->hidden, 0, (sizeof *this->hidden));
    83 
    84 	/* Set the function pointers */
    85 	this->OpenAudio = SNDIO_OpenAudio;
    86 	this->WaitAudio = SNDIO_WaitAudio;
    87 	this->PlayAudio = SNDIO_PlayAudio;
    88 	this->GetAudioBuf = SNDIO_GetAudioBuf;
    89 	this->CloseAudio = SNDIO_CloseAudio;
    90 
    91 	this->free = Audio_DeleteDevice;
    92 
    93 	hdl = NULL;
    94 
    95 	return this;
    96 }
    97 
    98 AudioBootStrap SNDIO_bootstrap = {
    99 	SNDIO_DRIVER_NAME, "sndio",
   100 	Audio_Available, Audio_CreateDevice
   101 };
   102 
   103 
   104 
   105 /* This function waits until it is possible to write a full sound buffer */
   106 static void SNDIO_WaitAudio(_THIS)
   107 {
   108 	/* Check to see if the thread-parent process is still alive */
   109 	{ static int cnt = 0;
   110 		/* Note that this only works with thread implementations 
   111 		   that use a different process id for each thread.
   112 		*/
   113 		if (parent && (((++cnt)%10) == 0)) { /* Check every 10 loops */
   114 			if ( kill(parent, 0) < 0 ) {
   115 				this->enabled = 0;
   116 			}
   117 		}
   118 	}
   119 }
   120 
   121 static void SNDIO_PlayAudio(_THIS)
   122 {
   123 	int written;
   124 
   125 	/* Write the audio data */
   126 	written = sio_write(hdl, mixbuf, mixlen);
   127 	
   128 	/* If we couldn't write, assume fatal error for now */
   129 	if ( written == 0 ) {
   130 		this->enabled = 0;
   131 	}
   132 #ifdef DEBUG_AUDIO
   133 	fprintf(stderr, "Wrote %d bytes of audio data\n", written);
   134 #endif
   135 }
   136 
   137 static Uint8 *SNDIO_GetAudioBuf(_THIS)
   138 {
   139 	return(mixbuf);
   140 }
   141 
   142 static void SNDIO_CloseAudio(_THIS)
   143 {
   144 	if ( mixbuf != NULL ) {
   145 		SDL_FreeAudioMem(mixbuf);
   146 		mixbuf = NULL;
   147 	}
   148 	if ( hdl != NULL ) {
   149 		sio_close(hdl);
   150 		hdl = NULL;
   151 	}
   152 }
   153 
   154 static int SNDIO_OpenAudio(_THIS, SDL_AudioSpec *spec)
   155 {
   156 	struct sio_par par, reqpar;
   157 	int newrate;
   158 
   159 	mixbuf = NULL;
   160 
   161 	if ((hdl = sio_open(NULL, SIO_PLAY, 0)) == NULL) {
   162 		SDL_SetError("sio_open() failed");
   163 		return(-1);
   164 	}
   165 
   166 	sio_initpar(&par);
   167 
   168 	switch (spec->format) {
   169 	case AUDIO_S16LSB:
   170 		par.bits = 16;
   171 		par.sig = 1;
   172 		par.le = 1;
   173 		break;
   174 	case AUDIO_S16MSB:
   175 		par.bits = 16;
   176 		par.sig = 1;
   177 		par.le = 0;
   178 		break;
   179 	case AUDIO_S8:
   180 		par.bits = 8;
   181 		par.sig = 1;
   182 		break;
   183 	case AUDIO_U16LSB:
   184 		par.bits = 16;
   185 		par.sig = 0;
   186 		par.le = 1;
   187 		break;
   188 	case AUDIO_U16MSB:
   189 		par.bits = 16;
   190 		par.sig = 0;
   191 		par.le = 0;
   192 		break;
   193 	case AUDIO_U8:
   194 		par.bits = 8;
   195 		par.sig = 0;
   196 		break;
   197 	default:
   198 		SDL_SetError("SNDIO unknown format");
   199 		return(-1);
   200 	}
   201 
   202 	par.rate = spec->freq;
   203 	par.pchan = spec->channels;
   204 	par.round = spec->samples;
   205 	par.appbufsz = par.round * 2;
   206 
   207 	reqpar = par;
   208 
   209 	if (sio_setpar(hdl, &par) == 0) {
   210 		SDL_SetError("sio_setpar() failed");
   211 		return(-1);
   212 	}
   213 
   214 	if (sio_getpar(hdl, &par) == 0) {
   215 		SDL_SetError("sio_getpar() failed");
   216 		return(-1);
   217 	}
   218 
   219 	/* if wanted rate not found, find a multiple/factor */
   220 	if (par.rate != spec->freq) {
   221 		newrate = par.rate;
   222 		if ((newrate > spec->freq && newrate % spec->freq != 0) ||
   223 		     (newrate < spec->freq && spec->freq % newrate != 0)) {
   224 			if ((spec->freq < 44100 && 44100 % spec->freq == 0) ||
   225 			     (spec->freq > 44100 && spec->freq % 44100 == 0)) {
   226 				newrate = 44100;
   227 			}
   228 		}
   229 		/* only change sample rate */
   230 		par = reqpar;
   231 		par.rate = newrate;
   232 		/* keep same latency */
   233 		par.round = spec->samples * par.rate / reqpar.rate;
   234 		par.appbufsz = par.round * 2;
   235 		if (sio_setpar(hdl, &par) == 0) {
   236 			SDL_SetError("sio_setpar() failed");
   237 			return(-1);
   238 		}
   239 	}
   240 
   241 	if (sio_getpar(hdl, &par) == 0) {
   242 		SDL_SetError("sio_getpar() failed");
   243 		return(-1);
   244 	}
   245 
   246 	if (par.bits == 16) {
   247 		if (par.sig && par.le) {
   248 			spec->format = AUDIO_S16LSB;
   249 		} else if (par.sig && !par.le) {
   250 			spec->format = AUDIO_S16MSB;
   251 		} else if (!par.sig && par.le) {
   252 			spec->format = AUDIO_U16LSB;
   253 		} else 
   254 			spec->format = AUDIO_U16MSB;
   255 	} else if (par.bits == 8) {
   256 		spec->format = par.sig ? AUDIO_S8 : AUDIO_U8;
   257 	} else {
   258 		SDL_SetError("SNDIO couldn't configure a suitable format");
   259 		return(-1);
   260 	}
   261 
   262 	spec->freq = par.rate;
   263 	spec->channels = par.pchan;
   264 	spec->samples = par.round;
   265 
   266 	SDL_CalculateAudioSpec(spec);
   267 
   268 	/* Allocate mixing buffer */
   269 	mixlen = spec->size;
   270 	mixbuf = (Uint8 *)SDL_AllocAudioMem(mixlen);
   271 	if ( mixbuf == NULL ) {
   272 		return(-1);
   273 	}
   274 	SDL_memset(mixbuf, spec->silence, spec->size);
   275 
   276 	/* Get the parent process id (we're the parent of the audio thread) */
   277 	parent = getpid();
   278 
   279 	if ( sio_start(hdl) == 0 ) {
   280 		SDL_SetError("sio_start() failed");
   281 		return(-1);
   282 	}
   283 
   284 	/* We're ready to rock and roll. :-) */
   285 	return(0);
   286 }