src/audio/nto/SDL_nto_audio.c
changeset 0 74212992fb08
child 252 e8157fcb3114
equal deleted inserted replaced
-1:000000000000 0:74212992fb08
       
     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 
       
    24 
       
    25 /* Allow access to a raw mixing buffer */
       
    26 
       
    27 #include <stdlib.h>
       
    28 #include <stdio.h>
       
    29 #include <string.h>
       
    30 #include <errno.h>
       
    31 #include <unistd.h>
       
    32 #include <fcntl.h>
       
    33 #include <signal.h>
       
    34 #include <sys/types.h>
       
    35 #include <sys/time.h>
       
    36 #include <sched.h>
       
    37 #include <sys/asoundlib.h>
       
    38 
       
    39 #include "SDL_audio.h"
       
    40 #include "SDL_error.h"
       
    41 #include "SDL_audiomem.h"
       
    42 #include "SDL_audio_c.h"
       
    43 #include "SDL_timer.h"
       
    44 #include "SDL_nto_audio.h"
       
    45 
       
    46 /* The tag name used by NTO audio */
       
    47 #define DRIVER_NAME         "nto"
       
    48 
       
    49 /* default card and device numbers as listed in dev/snd */
       
    50 static int card_no = 0;
       
    51 static int device_no = 0;
       
    52 
       
    53 /* default channel communication parameters */
       
    54 #define DEFAULT_CPARAMS_RATE 22050
       
    55 #define DEFAULT_CPARAMS_VOICES 1
       
    56 #define DEFAULT_CPARAMS_FRAG_SIZE 4096  //was 512
       
    57 #define DEFAULT_CPARAMS_FRAGS_MIN 1
       
    58 #define DEFAULT_CPARAMS_FRAGS_MAX -1
       
    59 
       
    60 /* Open the audio device for playback, and don't block if busy */
       
    61 #define OPEN_FLAGS	SND_PCM_OPEN_PLAYBACK
       
    62 
       
    63 /* Audio driver functions */
       
    64 static int NTO_OpenAudio(_THIS, SDL_AudioSpec *spec);
       
    65 static void NTO_WaitAudio(_THIS);
       
    66 static void NTO_PlayAudio(_THIS);
       
    67 static Uint8 *NTO_GetAudioBuf(_THIS);
       
    68 static void NTO_CloseAudio(_THIS);
       
    69 
       
    70 static snd_pcm_channel_status_t cstatus;
       
    71 static	snd_pcm_channel_params_t cparams;
       
    72 static	snd_pcm_channel_setup_t  csetup;
       
    73 
       
    74 /* PCM transfer channel parameters initialize function */
       
    75 static void init_pcm_cparams(snd_pcm_channel_params_t* cparams)
       
    76 {
       
    77 	memset(cparams,0,sizeof(snd_pcm_channel_params_t));
       
    78 
       
    79 	cparams->channel = SND_PCM_CHANNEL_PLAYBACK;
       
    80 	cparams->mode = SND_PCM_MODE_BLOCK;
       
    81 	cparams->start_mode = SND_PCM_START_DATA; //_FULL
       
    82 	cparams->stop_mode  = SND_PCM_STOP_STOP;
       
    83 	cparams->format.format = SND_PCM_SFMT_S16_LE;
       
    84 	cparams->format.interleave = 1;
       
    85 	cparams->format.rate = DEFAULT_CPARAMS_RATE;
       
    86 	cparams->format.voices = DEFAULT_CPARAMS_VOICES;
       
    87 	cparams->buf.block.frag_size = DEFAULT_CPARAMS_FRAG_SIZE;
       
    88 	cparams->buf.block.frags_min = DEFAULT_CPARAMS_FRAGS_MIN;
       
    89 	cparams->buf.block.frags_max = DEFAULT_CPARAMS_FRAGS_MAX;
       
    90 }
       
    91 
       
    92 /* Audio driver bootstrap functions */
       
    93 
       
    94 static int Audio_Available(void)
       
    95 /*
       
    96 	See if we can open a nonblocking channel.
       
    97 	Return value '1' means we can.
       
    98 	Return value '0' means we cannot.
       
    99 */
       
   100 {
       
   101 	int available;
       
   102 	int rval;
       
   103 	snd_pcm_t *handle;
       
   104 
       
   105 	available = 0;
       
   106 	handle = NULL;
       
   107 	
       
   108 	//JB modified to take advantage of software mixer
       
   109 	rval = snd_pcm_open_preferred(&handle, &card_no, &device_no, OPEN_FLAGS);
       
   110 
       
   111 	if (rval >= 0)
       
   112 	{
       
   113 			available = 1;
       
   114 
       
   115         if ((rval = snd_pcm_close(handle)) < 0)
       
   116         {
       
   117             SDL_SetError("snd_pcm_close failed: %s\n",snd_strerror(rval));
       
   118 			available = 0;
       
   119         }
       
   120 	}
       
   121 	else
       
   122 	{
       
   123 	
       
   124        SDL_SetError("snd_pcm_open failed: %s\n", snd_strerror(rval));
       
   125 	}
       
   126 	
       
   127 #ifdef DEBUG_AUDIO
       
   128 	fprintf(stderr,"AudioAvailable rtns %d\n", available);
       
   129 #endif
       
   130 	return(available);
       
   131 }
       
   132 
       
   133 static void Audio_DeleteDevice(SDL_AudioDevice *device)
       
   134 {
       
   135 #ifdef DEBUG_AUDIO
       
   136 	fprintf(stderr,"Audio_DeleteDevice\n");
       
   137 #endif
       
   138 
       
   139 	
       
   140 	free(device->hidden);
       
   141 	free(device);
       
   142 }
       
   143 
       
   144 static SDL_AudioDevice *Audio_CreateDevice(int devindex)
       
   145 {
       
   146 	SDL_AudioDevice *this;
       
   147 #ifdef DEBUG_AUDIO
       
   148 	fprintf(stderr,"Audio_CreateDevice\n");
       
   149 #endif
       
   150 	/* Initialize all variables that we clean on shutdown */
       
   151 	this = (SDL_AudioDevice *)malloc(sizeof(SDL_AudioDevice));
       
   152 	if ( this ) {
       
   153 		memset(this, 0, (sizeof *this));
       
   154 		this->hidden = (struct SDL_PrivateAudioData *)
       
   155 				malloc((sizeof *this->hidden));
       
   156 	}
       
   157 	if ( (this == NULL) || (this->hidden == NULL) ) {
       
   158 		SDL_OutOfMemory();
       
   159 		if ( this ) {
       
   160 			free(this);
       
   161 		}
       
   162 		return(0);
       
   163 	}
       
   164 	memset(this->hidden, 0, (sizeof *this->hidden));
       
   165 	audio_handle = NULL;
       
   166 
       
   167 	/* Set the function pointers */
       
   168 	this->OpenAudio = NTO_OpenAudio;
       
   169 	this->WaitAudio = NTO_WaitAudio;
       
   170 	this->PlayAudio = NTO_PlayAudio;
       
   171 	this->GetAudioBuf = NTO_GetAudioBuf;
       
   172 	this->CloseAudio = NTO_CloseAudio;
       
   173 
       
   174 	this->free = Audio_DeleteDevice;
       
   175 
       
   176 	return this;
       
   177 }
       
   178 
       
   179 /* Don't change the name from "ALSA_bootstrap" - that's how it's called */
       
   180 AudioBootStrap ALSA_bootstrap = {
       
   181 	DRIVER_NAME, "Neutrino PCM audio",
       
   182 	Audio_Available, Audio_CreateDevice
       
   183 };
       
   184 
       
   185 /* This function waits until it is possible to write a full sound buffer */
       
   186 static void NTO_WaitAudio(_THIS)
       
   187 {
       
   188 	int rval;
       
   189 	int totalbytes,roomavail;
       
   190 	/*we consider a full sound buffer to be of size pcm_len bytes */
       
   191 	
       
   192 #ifdef DEBUG_AUDIO
       
   193 	fprintf(stderr,"NTO_WaitAudio\n");
       
   194 #endif
       
   195 
       
   196 	while(1)
       
   197 	{
       
   198 	memset(&cstatus, 0, sizeof(cstatus));
       
   199 	 if( (rval = snd_pcm_plugin_status(audio_handle, &cstatus)) < 0 )
       
   200     {
       
   201 		 SDL_SetError("snd_pcm_plugin_status failed: %s\n", snd_strerror(rval));
       
   202    	    return;
       
   203 	}	
       
   204 	
       
   205 	totalbytes = csetup.buf.block.frag_size *csetup.buf.block.frags;
       
   206 	roomavail = totalbytes - cstatus.count;
       
   207 
       
   208 #ifdef DEBUG_AUDIO
       
   209 	fprintf(stderr,"NTO_WaitAudio roomavail %d pcm_len %d\n",roomavail,pcm_len);
       
   210 #endif
       
   211 	
       
   212 	if ((roomavail >= pcm_len) || (roomavail < 0))
       
   213 		return;
       
   214 		
       
   215 		SDL_Delay(10);	
       
   216 	}	
       
   217       
       
   218 }
       
   219 
       
   220 
       
   221 
       
   222 static void NTO_PlayAudio(_THIS)
       
   223 {
       
   224     int written, rval;
       
   225     int towrite;
       
   226 
       
   227 #ifdef DEBUG_AUDIO
       
   228 		fprintf(stderr, "NTO_PlayAudio\n");
       
   229 #endif
       
   230 
       
   231 	if( !this->enabled)
       
   232 	   return;
       
   233 
       
   234 	towrite = pcm_len;
       
   235 	
       
   236 
       
   237     /* Write the audio data, checking for EAGAIN (buffer full) and underrun */
       
   238     do {
       
   239 		written = snd_pcm_plugin_write(audio_handle, pcm_buf, towrite);
       
   240 #ifdef DEBUG_AUDIO
       
   241 		fprintf(stderr, "NTO_PlayAudio: written = %d towrite = %d\n",written,towrite);
       
   242 #endif
       
   243 		if (written != towrite)
       
   244 		{
       
   245 	        if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
       
   246 			{
       
   247             	SDL_Delay(1);   /* Let a little CPU time go by and try to write again */
       
   248 #ifdef DEBUG_AUDIO
       
   249 				fprintf(stderr, "errno == EAGAIN written %d\n", written);
       
   250 				towrite -= written; //we wrote some data
       
   251 #endif
       
   252 				continue;
       
   253         	}		
       
   254 			else if((errno == EINVAL) || (errno == EIO))
       
   255 			{
       
   256 #ifdef DEBUG_AUDIO
       
   257 					if(errno == EIO)
       
   258 						fprintf(stderr,"snd_pcm_plugin_write failed EIO: %s\n", snd_strerror(written));	
       
   259 					if(errno == EINVAL)
       
   260 						fprintf(stderr,"snd_pcm_plugin_write failed EINVAL: %s\n", snd_strerror(written));	
       
   261 								
       
   262 #endif			
       
   263 			
       
   264 				  memset(&cstatus, 0, sizeof(cstatus));			
       
   265 		         if( (rval = snd_pcm_plugin_status(audio_handle, &cstatus)) < 0 )
       
   266         		 {
       
   267 #ifdef DEBUG_AUDIO
       
   268 					fprintf(stderr, "snd_pcm_plugin_status failed %s\n",snd_strerror(rval));
       
   269 #endif
       
   270 		            SDL_SetError("snd_pcm_plugin_status failed: %s\n", snd_strerror(rval));
       
   271         		   return;
       
   272 		        }	
       
   273 		        
       
   274 				if ( (cstatus.status == SND_PCM_STATUS_UNDERRUN) ||
       
   275 					(cstatus.status == SND_PCM_STATUS_READY) )
       
   276 				{
       
   277 #ifdef DEBUG_AUDIO
       
   278 					fprintf(stderr, "buffer underrun\n");
       
   279 #endif
       
   280 					if ( (rval = snd_pcm_plugin_prepare (audio_handle,SND_PCM_CHANNEL_PLAYBACK)) < 0 )
       
   281 					{
       
   282 #ifdef DEBUG_AUDIO
       
   283 						fprintf(stderr, "NTO_PlayAudio: prepare failed %s\n",snd_strerror(rval));
       
   284 #endif
       
   285 						SDL_SetError("snd_pcm_plugin_prepare failed: %s\n",snd_strerror(rval) );
       
   286 						return;
       
   287 					}
       
   288 					
       
   289 				}		        		
       
   290  				continue;
       
   291 			}
       
   292 			else
       
   293 			{
       
   294 #ifdef DEBUG_AUDIO
       
   295 						fprintf(stderr, "NTO_PlayAudio: snd_pcm_plugin_write failed unknown errno %d %s\n",errno, snd_strerror(rval));
       
   296 #endif
       
   297 				return;
       
   298 			
       
   299 			}
       
   300 			
       
   301 		}
       
   302 		else
       
   303 		{
       
   304 			towrite -= written; //we wrote all remaining data
       
   305 		}
       
   306     } while ( (towrite > 0)  && (this->enabled) );
       
   307 
       
   308     /* If we couldn't write, assume fatal error for now */
       
   309     if ( towrite != 0 ) {
       
   310         this->enabled = 0;
       
   311     }
       
   312 	return;
       
   313 }
       
   314 
       
   315 static Uint8 *NTO_GetAudioBuf(_THIS)
       
   316 {
       
   317  #ifdef DEBUG_AUDIO
       
   318 		fprintf(stderr, "NTO_GetAudioBuf: pcm_buf %X\n",(Uint8 *)pcm_buf);
       
   319 #endif
       
   320 	return(pcm_buf);
       
   321 }
       
   322 
       
   323 static void NTO_CloseAudio(_THIS)
       
   324 {
       
   325 	int rval;
       
   326 	
       
   327 #ifdef DEBUG_AUDIO
       
   328 		fprintf(stderr, "NTO_CloseAudio\n");
       
   329 #endif
       
   330 
       
   331 	 this->enabled = 0;
       
   332 
       
   333 	if ( audio_handle != NULL ) {
       
   334 		if ((rval = snd_pcm_plugin_flush(audio_handle,SND_PCM_CHANNEL_PLAYBACK)) < 0)
       
   335 		{
       
   336         	SDL_SetError("snd_pcm_plugin_flush failed: %s\n",snd_strerror(rval));
       
   337 			return;
       
   338 		}
       
   339 		if ((rval = snd_pcm_close(audio_handle)) < 0)
       
   340 		{
       
   341 			SDL_SetError("snd_pcm_close failed: %s\n",snd_strerror(rval));
       
   342 			return;
       
   343 		}
       
   344 		audio_handle = NULL;
       
   345 	}
       
   346 }
       
   347 
       
   348 static int NTO_OpenAudio(_THIS, SDL_AudioSpec *spec)
       
   349 {
       
   350 	int rval;
       
   351 	int format;
       
   352 	Uint16 test_format;
       
   353 	int twidth;
       
   354 	int found;
       
   355 
       
   356 #ifdef DEBUG_AUDIO
       
   357 		fprintf(stderr, "NTO_OpenAudio\n");
       
   358 #endif
       
   359 	
       
   360 	audio_handle = NULL;
       
   361 	 this->enabled = 0;
       
   362 
       
   363 	if ( pcm_buf != NULL ) {
       
   364 		free((Uint8 *)pcm_buf); 
       
   365 		pcm_buf = NULL;
       
   366 	}
       
   367 	 
       
   368 	/* initialize channel transfer parameters to default */
       
   369 	init_pcm_cparams(&cparams);
       
   370 
       
   371 	/* Open the audio device */
       
   372 	
       
   373 	rval = snd_pcm_open_preferred(&audio_handle, &card_no, &device_no, OPEN_FLAGS);
       
   374 	if ( rval < 0 ) {
       
   375 		SDL_SetError("snd_pcm_open failed: %s\n", snd_strerror(rval));
       
   376 		return(-1);
       
   377 	}
       
   378 
       
   379     /* set to nonblocking mode */
       
   380     if ((rval = snd_pcm_nonblock_mode(audio_handle, 1))<0) //I assume 1 means on
       
   381     {
       
   382         SDL_SetError("snd_pcm_nonblock_mode failed: %s\n", snd_strerror(rval));
       
   383         return(-1);
       
   384     }
       
   385 
       
   386     /* enable count status parameter */
       
   387     if ((rval = snd_plugin_set_disable(audio_handle, PLUGIN_DISABLE_MMAP))<0)
       
   388     {
       
   389         SDL_SetError("snd_plugin_set_disable failed: %s\n", snd_strerror(rval));
       
   390         return(-1);
       
   391     }
       
   392 
       
   393 
       
   394 	/* Try for a closest match on audio format */
       
   395 	format = 0;
       
   396  	found = 0; // can't use format as SND_PCM_SFMT_U8 = 0 in nto
       
   397 	for ( test_format = SDL_FirstAudioFormat(spec->format);	!found ; ) 
       
   398 
       
   399 	{
       
   400 #ifdef DEBUG_AUDIO
       
   401 		fprintf(stderr, "Trying format 0x%4.4x spec->samples %d\n", test_format,spec->samples);
       
   402 #endif
       
   403 			/* if match found set format to equivalent ALSA format */
       
   404         switch ( test_format ) {
       
   405 			case AUDIO_U8:
       
   406 				format = SND_PCM_SFMT_U8;
       
   407 				cparams.buf.block.frag_size = spec->samples * spec->channels;
       
   408 				found = 1;
       
   409 				break;
       
   410 			case AUDIO_S8:
       
   411 				format = SND_PCM_SFMT_S8;
       
   412 				cparams.buf.block.frag_size = spec->samples * spec->channels;
       
   413 				found = 1;
       
   414 				break;
       
   415 			case AUDIO_S16LSB:
       
   416 				format = SND_PCM_SFMT_S16_LE;
       
   417 				cparams.buf.block.frag_size = spec->samples*2 * spec->channels;
       
   418 				found = 1;
       
   419 				break;
       
   420 			case AUDIO_S16MSB:
       
   421 				format = SND_PCM_SFMT_S16_BE;
       
   422 				cparams.buf.block.frag_size = spec->samples*2 * spec->channels;
       
   423 				found = 1;
       
   424 				break;
       
   425 			case AUDIO_U16LSB:
       
   426 				format = SND_PCM_SFMT_U16_LE;
       
   427 				cparams.buf.block.frag_size = spec->samples*2 * spec->channels;
       
   428 				found = 1;
       
   429 				break;
       
   430 			case AUDIO_U16MSB:
       
   431 				format = SND_PCM_SFMT_U16_BE;
       
   432 				cparams.buf.block.frag_size = spec->samples*2 * spec->channels;
       
   433 				found = 1;
       
   434 				break;
       
   435 			default:
       
   436 				break;
       
   437 		}
       
   438 		if ( ! found ) {
       
   439 			test_format = SDL_NextAudioFormat();
       
   440 		}
       
   441 	}
       
   442 	
       
   443 	/* assumes test_format not 0 on success */
       
   444 	if ( test_format == 0 ) {
       
   445 		SDL_SetError("Couldn't find any hardware audio formats");
       
   446 		return(-1);
       
   447 	}
       
   448 	
       
   449 	spec->format = test_format;
       
   450 
       
   451 	/* Set the audio format */
       
   452 	cparams.format.format = format;
       
   453 
       
   454 	/* Set mono or stereo audio (currently only two channels supported) */
       
   455 	cparams.format.voices = spec->channels;
       
   456 	
       
   457 #ifdef DEBUG_AUDIO
       
   458 	fprintf(stderr,"intializing channels %d\n", cparams.format.voices);
       
   459 #endif
       
   460 
       
   461 	
       
   462 	/* Set rate */
       
   463 	cparams.format.rate = spec->freq ;
       
   464 
       
   465 	/* Setup the transfer parameters according to cparams */
       
   466 	rval = snd_pcm_plugin_params(audio_handle, &cparams);
       
   467 	if (rval < 0) {
       
   468 		SDL_SetError("snd_pcm_channel_params failed: %s\n", snd_strerror (rval));
       
   469 		return(-1);
       
   470 	}
       
   471 
       
   472     /*  Make sure channel is setup right one last time */
       
   473     memset( &csetup, 0, sizeof( csetup ) );
       
   474     csetup.channel = SND_PCM_CHANNEL_PLAYBACK;
       
   475     if ( snd_pcm_plugin_setup( audio_handle, &csetup ) < 0 )
       
   476     {
       
   477         SDL_SetError("Unable to setup playback channel\n" );
       
   478         return(-1);
       
   479     }
       
   480     else
       
   481     {
       
   482 #ifdef DEBUG_AUDIO
       
   483         fprintf(stderr,"requested format: %d\n",cparams.format.format);
       
   484         fprintf(stderr,"requested frag size: %d\n",cparams.buf.block.frag_size);
       
   485         fprintf(stderr,"requested max frags: %d\n\n",cparams.buf.block.frags_max);
       
   486 
       
   487         fprintf(stderr,"real format: %d\n", csetup.format.format );
       
   488         fprintf(stderr,"real frag size : %d\n", csetup.buf.block.frag_size );
       
   489 		fprintf(stderr,"real max frags : %d\n", csetup.buf.block.frags_max );
       
   490 #endif // DEBUG_AUDIO
       
   491     }
       
   492 
       
   493 
       
   494     /*  Allocate memory to the audio buffer and initialize with silence
       
   495         (Note that buffer size must be a multiple of fragment size, so find closest multiple)
       
   496     */
       
   497     
       
   498     twidth = snd_pcm_format_width(format);
       
   499     if (twidth < 0) {
       
   500         printf("snd_pcm_format_width failed\n");
       
   501         twidth = 0;
       
   502     }
       
   503     
       
   504 #ifdef DEBUG_AUDIO
       
   505     fprintf(stderr,"format is %d bits wide\n",twidth);
       
   506 #endif      
       
   507     
       
   508     pcm_len = spec->size ;
       
   509     
       
   510   
       
   511 #ifdef DEBUG_AUDIO    
       
   512     fprintf(stderr,"pcm_len set to %d\n", pcm_len);
       
   513 #endif
       
   514     
       
   515     if (pcm_len == 0)
       
   516     {
       
   517         pcm_len = csetup.buf.block.frag_size;
       
   518     }
       
   519     
       
   520     pcm_buf = (Uint8*)malloc(pcm_len);
       
   521     if (pcm_buf == NULL) {
       
   522         SDL_SetError("pcm_buf malloc failed\n");
       
   523         return(-1);
       
   524     }
       
   525     memset(pcm_buf,spec->silence,pcm_len);
       
   526 
       
   527 #ifdef DEBUG_AUDIO
       
   528 	fprintf(stderr,"pcm_buf malloced and silenced.\n");
       
   529 #endif
       
   530 
       
   531     /* get the file descriptor */
       
   532     if( (audio_fd = snd_pcm_file_descriptor(audio_handle, SND_PCM_CHANNEL_PLAYBACK)) < 0)
       
   533     {
       
   534        fprintf(stderr, "snd_pcm_file_descriptor failed with error code: %d\n", audio_fd);
       
   535     }
       
   536 
       
   537 	/* Trigger audio playback */
       
   538 	rval = snd_pcm_plugin_prepare( audio_handle, SND_PCM_CHANNEL_PLAYBACK);
       
   539 	if (rval < 0) {
       
   540        SDL_SetError("snd_pcm_plugin_prepare failed: %s\n", snd_strerror (rval));
       
   541        return(-1);
       
   542 	}
       
   543 	
       
   544 	 this->enabled = 1;
       
   545 	 
       
   546 	/* Get the parent process id (we're the parent of the audio thread) */
       
   547 	parent = getpid();
       
   548 
       
   549 	/* We're ready to rock and roll. :-) */
       
   550 	return(0);
       
   551 }
       
   552 
       
   553 
       
   554