src/audio/nto/SDL_nto_audio.c

/* [<][>]
[^][v][top][bottom][index][help] */

FUNCTIONS

This source file includes following functions.
  1. init_pcm_cparams
  2. Audio_Available
  3. Audio_DeleteDevice
  4. Audio_CreateDevice
  5. NTO_WaitAudio
  6. NTO_PlayAudio
  7. NTO_GetAudioBuf
  8. NTO_CloseAudio
  9. NTO_OpenAudio

   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)
     /* [<][>][^][v][top][bottom][index][help] */
  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)
     /* [<][>][^][v][top][bottom][index][help] */
  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)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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 

/* [<][>][^][v][top][bottom][index][help] */