src/audio/sun/SDL_sunaudio.c
author Sam Lantinga
Mon, 06 Feb 2006 08:28:51 +0000
changeset 1330 450721ad5436
parent 1312 c9b51268668f
child 1336 3692456e7b0f
permissions -rw-r--r--
It's now possible to build SDL without any C runtime at all on Windows,
using Visual C++ 2005
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2006 Sam Lantinga
     4 
     5     This library is free software; you can redistribute it and/or
     6     modify it under the terms of the GNU Lesser General Public
     7     License as published by the Free Software Foundation; either
     8     version 2.1 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     Lesser General Public License for more details.
    14 
    15     You should have received a copy of the GNU Lesser General Public
    16     License along with this library; if not, write to the Free Software
    17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    18 
    19     Sam Lantinga
    20     slouken@libsdl.org
    21 */
    22 
    23 /* Allow access to a raw mixing buffer */
    24 
    25 #include <stdlib.h>
    26 #include <stdio.h>
    27 #include <fcntl.h>
    28 #include <errno.h>
    29 #include <string.h>
    30 #ifdef __NetBSD__
    31 #include <sys/ioctl.h>
    32 #include <sys/audioio.h>
    33 #endif
    34 #ifdef __SVR4
    35 #include <sys/audioio.h>
    36 #else
    37 #include <sys/time.h>
    38 #include <sys/types.h>
    39 #endif
    40 #include <unistd.h>
    41 
    42 #include "SDL_endian.h"
    43 #include "SDL_audio.h"
    44 #include "SDL_audiomem.h"
    45 #include "SDL_audiodev_c.h"
    46 #include "SDL_sunaudio.h"
    47 #include "SDL_audio_c.h"
    48 #include "SDL_timer.h"
    49 
    50 /* Open the audio device for playback, and don't block if busy */
    51 #define OPEN_FLAGS	(O_WRONLY|O_NONBLOCK)
    52 
    53 /* Audio driver functions */
    54 static int DSP_OpenAudio(_THIS, SDL_AudioSpec *spec);
    55 static void DSP_WaitAudio(_THIS);
    56 static void DSP_PlayAudio(_THIS);
    57 static Uint8 *DSP_GetAudioBuf(_THIS);
    58 static void DSP_CloseAudio(_THIS);
    59 
    60 /* Audio driver bootstrap functions */
    61 
    62 static int Audio_Available(void)
    63 {
    64 	int fd;
    65 	int available;
    66 
    67 	available = 0;
    68 	fd = SDL_OpenAudioPath(NULL, 0, OPEN_FLAGS, 1);
    69 	if ( fd >= 0 ) {
    70 		available = 1;
    71 		close(fd);
    72 	}
    73 	return(available);
    74 }
    75 
    76 static void Audio_DeleteDevice(SDL_AudioDevice *device)
    77 {
    78 	free(device->hidden);
    79 	free(device);
    80 }
    81 
    82 static SDL_AudioDevice *Audio_CreateDevice(int devindex)
    83 {
    84 	SDL_AudioDevice *this;
    85 
    86 	/* Initialize all variables that we clean on shutdown */
    87 	this = (SDL_AudioDevice *)malloc(sizeof(SDL_AudioDevice));
    88 	if ( this ) {
    89 		memset(this, 0, (sizeof *this));
    90 		this->hidden = (struct SDL_PrivateAudioData *)
    91 				malloc((sizeof *this->hidden));
    92 	}
    93 	if ( (this == NULL) || (this->hidden == NULL) ) {
    94 		SDL_OutOfMemory();
    95 		if ( this ) {
    96 			free(this);
    97 		}
    98 		return(0);
    99 	}
   100 	memset(this->hidden, 0, (sizeof *this->hidden));
   101 	audio_fd = -1;
   102 
   103 	/* Set the function pointers */
   104 	this->OpenAudio = DSP_OpenAudio;
   105 	this->WaitAudio = DSP_WaitAudio;
   106 	this->PlayAudio = DSP_PlayAudio;
   107 	this->GetAudioBuf = DSP_GetAudioBuf;
   108 	this->CloseAudio = DSP_CloseAudio;
   109 
   110 	this->free = Audio_DeleteDevice;
   111 
   112 	return this;
   113 }
   114 
   115 AudioBootStrap SUNAUDIO_bootstrap = {
   116 	"audio", "UNIX /dev/audio interface",
   117 	Audio_Available, Audio_CreateDevice
   118 };
   119 
   120 #ifdef DEBUG_AUDIO
   121 void CheckUnderflow(_THIS)
   122 {
   123 #ifdef AUDIO_GETINFO
   124 	audio_info_t info;
   125 	int left;
   126 
   127 	ioctl(audio_fd, AUDIO_GETINFO, &info);
   128 	left = (written - info.play.samples);
   129 	if ( written && (left == 0) ) {
   130 		fprintf(stderr, "audio underflow!\n");
   131 	}
   132 #endif
   133 }
   134 #endif
   135 
   136 void DSP_WaitAudio(_THIS)
   137 {
   138 #ifdef AUDIO_GETINFO
   139 #define SLEEP_FUDGE	10		/* 10 ms scheduling fudge factor */
   140 	audio_info_t info;
   141 	Sint32 left;
   142 
   143 	ioctl(audio_fd, AUDIO_GETINFO, &info);
   144 	left = (written - info.play.samples);
   145 	if ( left > fragsize ) {
   146 		Sint32 sleepy;
   147 
   148 		sleepy = ((left - fragsize)/frequency);
   149 		sleepy -= SLEEP_FUDGE;
   150 		if ( sleepy > 0 ) {
   151 			SDL_Delay(sleepy);
   152 		}
   153 	}
   154 #else
   155 	fd_set fdset;
   156 
   157 	FD_ZERO(&fdset);
   158 	FD_SET(audio_fd, &fdset);
   159 	select(audio_fd+1, NULL, &fdset, NULL, NULL);
   160 #endif
   161 }
   162 
   163 static Uint8 snd2au(int sample);
   164 void DSP_PlayAudio(_THIS)
   165 {
   166 	/* Write the audio data */
   167 	if ( ulaw_only ) {
   168 		/* Assuming that this->spec.freq >= 8000 Hz */
   169 		int accum, incr, pos;
   170 		Uint8 *aubuf;
   171 
   172 		accum = 0;
   173 		incr  = this->spec.freq/8;
   174 		aubuf = ulaw_buf;
   175 		switch (audio_fmt & 0xFF) {
   176 			case 8: {
   177 				Uint8 *sndbuf;
   178 
   179 				sndbuf = mixbuf;
   180 				for ( pos=0; pos < fragsize; ++pos ) {
   181 					*aubuf = snd2au((0x80-*sndbuf)*64);
   182 					accum += incr;
   183 					while ( accum > 0 ) {
   184 						accum -= 1000;
   185 						sndbuf += 1;
   186 					}
   187 					aubuf += 1;
   188 				}
   189 			}
   190 			break;
   191 			case 16: {
   192 				Sint16 *sndbuf;
   193 
   194 				sndbuf = (Sint16 *)mixbuf;
   195 				for ( pos=0; pos < fragsize; ++pos ) {
   196 					*aubuf = snd2au(*sndbuf/4);
   197 					accum += incr;
   198 					while ( accum > 0 ) {
   199 						accum -= 1000;
   200 						sndbuf += 1;
   201 					}
   202 					aubuf += 1;
   203 				}
   204 			}
   205 			break;
   206 		}
   207 #ifdef DEBUG_AUDIO
   208 		CheckUnderflow(this);
   209 #endif
   210 		if ( write(audio_fd, ulaw_buf, fragsize) < 0 ) {
   211 			/* Assume fatal error, for now */
   212 			this->enabled = 0;
   213 		}
   214 		written += fragsize;
   215 	} else {
   216 #ifdef DEBUG_AUDIO
   217 		CheckUnderflow(this);
   218 #endif
   219 		if ( write(audio_fd, mixbuf, this->spec.size) < 0 ) {
   220 			/* Assume fatal error, for now */
   221 			this->enabled = 0;
   222 		}
   223 		written += fragsize;
   224 	}
   225 }
   226 
   227 Uint8 *DSP_GetAudioBuf(_THIS)
   228 {
   229 	return(mixbuf);
   230 }
   231 
   232 void DSP_CloseAudio(_THIS)
   233 {
   234 	if ( mixbuf != NULL ) {
   235 		SDL_FreeAudioMem(mixbuf);
   236 		mixbuf = NULL;
   237 	}
   238 	if ( ulaw_buf != NULL ) {
   239 		free(ulaw_buf);
   240 		ulaw_buf = NULL;
   241 	}
   242 	close(audio_fd);
   243 }
   244 
   245 int DSP_OpenAudio(_THIS, SDL_AudioSpec *spec)
   246 {
   247 	char audiodev[1024];
   248 #ifdef AUDIO_SETINFO
   249 	int enc;
   250 #endif
   251 	int desired_freq = spec->freq;
   252 
   253 	/* Initialize our freeable variables, in case we fail*/
   254 	audio_fd = -1;
   255 	mixbuf = NULL;
   256 	ulaw_buf = NULL;
   257 
   258 	/* Determine the audio parameters from the AudioSpec */
   259 	switch ( spec->format & 0xFF ) {
   260 
   261 		case 8: { /* Unsigned 8 bit audio data */
   262 			spec->format = AUDIO_U8;
   263 #ifdef AUDIO_SETINFO
   264 			enc = AUDIO_ENCODING_LINEAR8;
   265 #endif
   266 		}
   267 		break;
   268 
   269 		case 16: { /* Signed 16 bit audio data */
   270 		        spec->format = AUDIO_S16SYS;
   271 #ifdef AUDIO_SETINFO
   272 			enc = AUDIO_ENCODING_LINEAR;
   273 #endif
   274 		}
   275 		break;
   276 
   277 		default: {
   278 			SDL_SetError("Unsupported audio format");
   279 			return(-1);
   280 		}
   281 	}
   282 	audio_fmt = spec->format;
   283 
   284 	/* Open the audio device */
   285 	audio_fd = SDL_OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 1);
   286 	if ( audio_fd < 0 ) {
   287 		SDL_SetError("Couldn't open %s: %s", audiodev,
   288 			     strerror(errno));
   289 		return(-1);
   290 	}
   291 
   292 	ulaw_only = 0;		/* modern Suns do support linear audio */
   293 #ifdef AUDIO_SETINFO
   294 	for(;;) {
   295 	    audio_info_t info;
   296 	    AUDIO_INITINFO(&info); /* init all fields to "no change" */
   297 
   298 	    /* Try to set the requested settings */
   299 	    info.play.sample_rate = spec->freq;
   300 	    info.play.channels = spec->channels;
   301 	    info.play.precision = (enc == AUDIO_ENCODING_ULAW)
   302 		                  ? 8 : spec->format & 0xff;
   303 	    info.play.encoding = enc;
   304 	    if( ioctl(audio_fd, AUDIO_SETINFO, &info) == 0 ) {
   305 
   306 		/* Check to be sure we got what we wanted */
   307 		if(ioctl(audio_fd, AUDIO_GETINFO, &info) < 0) {
   308 		    SDL_SetError("Error getting audio parameters: %s",
   309 				 strerror(errno));
   310 		    return -1;
   311 		}
   312 		if(info.play.encoding == enc
   313 		   && info.play.precision == (spec->format & 0xff)
   314 		   && info.play.channels == spec->channels) {
   315 		    /* Yow! All seems to be well! */
   316 		    spec->freq = info.play.sample_rate;
   317 		    break;
   318 		}
   319 	    }
   320 
   321 	    switch(enc) {
   322 	    case AUDIO_ENCODING_LINEAR8:
   323 		/* unsigned 8bit apparently not supported here */
   324 		enc = AUDIO_ENCODING_LINEAR;
   325 		spec->format = AUDIO_S16SYS;
   326 		break;	/* try again */
   327 
   328 	    case AUDIO_ENCODING_LINEAR:
   329 		/* linear 16bit didn't work either, resort to -law */
   330 		enc = AUDIO_ENCODING_ULAW;
   331 		spec->channels = 1;
   332 		spec->freq = 8000;
   333 		spec->format = AUDIO_U8;
   334 		ulaw_only = 1;
   335 		break;
   336 
   337 	    default:
   338 		/* oh well... */
   339 		SDL_SetError("Error setting audio parameters: %s",
   340 			     strerror(errno));
   341 		return -1;
   342 	    }
   343 	}
   344 #endif /* AUDIO_SETINFO */
   345 	written = 0;
   346 
   347 	/* We can actually convert on-the-fly to U-Law */
   348 	if ( ulaw_only ) {
   349 	        spec->freq = desired_freq;
   350 		fragsize = (spec->samples*1000)/(spec->freq/8);
   351 		frequency = 8;
   352 		ulaw_buf = (Uint8 *)malloc(fragsize);
   353 		if ( ulaw_buf == NULL ) {
   354 			SDL_OutOfMemory();
   355 			return(-1);
   356 		}
   357 		spec->channels = 1;
   358 	} else {
   359 		fragsize = spec->samples;
   360 		frequency = spec->freq/1000;
   361 	}
   362 #ifdef DEBUG_AUDIO
   363 	fprintf(stderr, "Audio device %s U-Law only\n", 
   364 				ulaw_only ? "is" : "is not");
   365 	fprintf(stderr, "format=0x%x chan=%d freq=%d\n",
   366 		spec->format, spec->channels, spec->freq);
   367 #endif
   368 
   369 	/* Update the fragment size as size in bytes */
   370 	SDL_CalculateAudioSpec(spec);
   371 
   372 	/* Allocate mixing buffer */
   373 	mixbuf = (Uint8 *)SDL_AllocAudioMem(spec->size);
   374 	if ( mixbuf == NULL ) {
   375 		SDL_OutOfMemory();
   376 		return(-1);
   377 	}
   378 	memset(mixbuf, spec->silence, spec->size);
   379 
   380 	/* We're ready to rock and roll. :-) */
   381 	return(0);
   382 }
   383 
   384 /************************************************************************/
   385 /* This function (snd2au()) copyrighted:                                */
   386 /************************************************************************/
   387 /*      Copyright 1989 by Rich Gopstein and Harris Corporation          */
   388 /*                                                                      */
   389 /*      Permission to use, copy, modify, and distribute this software   */
   390 /*      and its documentation for any purpose and without fee is        */
   391 /*      hereby granted, provided that the above copyright notice        */
   392 /*      appears in all copies and that both that copyright notice and   */
   393 /*      this permission notice appear in supporting documentation, and  */
   394 /*      that the name of Rich Gopstein and Harris Corporation not be    */
   395 /*      used in advertising or publicity pertaining to distribution     */
   396 /*      of the software without specific, written prior permission.     */
   397 /*      Rich Gopstein and Harris Corporation make no representations    */
   398 /*      about the suitability of this software for any purpose.  It     */
   399 /*      provided "as is" without express or implied warranty.           */
   400 /************************************************************************/
   401 
   402 static Uint8 snd2au(int sample)
   403 {
   404 
   405 	int mask;
   406 
   407 	if (sample < 0) {
   408 		sample = -sample;
   409 		mask = 0x7f;
   410 	} else {
   411 		mask = 0xff;
   412 	}
   413 
   414 	if (sample < 32) {
   415 		sample = 0xF0 | (15 - sample / 2);
   416 	} else if (sample < 96) {
   417 		sample = 0xE0 | (15 - (sample - 32) / 4);
   418 	} else if (sample < 224) {
   419 		sample = 0xD0 | (15 - (sample - 96) / 8);
   420 	} else if (sample < 480) {
   421 		sample = 0xC0 | (15 - (sample - 224) / 16);
   422 	} else if (sample < 992) {
   423 		sample = 0xB0 | (15 - (sample - 480) / 32);
   424 	} else if (sample < 2016) {
   425 		sample = 0xA0 | (15 - (sample - 992) / 64);
   426 	} else if (sample < 4064) {
   427 		sample = 0x90 | (15 - (sample - 2016) / 128);
   428 	} else if (sample < 8160) {
   429 		sample = 0x80 | (15 - (sample - 4064) /  256);
   430 	} else {
   431 		sample = 0x80;
   432 	}
   433 	return (mask & sample);
   434 }