SDL_alsa_audio.c
上传用户:sun1608
上传日期:2007-02-02
资源大小:6116k
文件大小:14k
源码类别:

流媒体/Mpeg4/MP4

开发平台:

Visual C++

  1. /*
  2.     SDL - Simple DirectMedia Layer
  3.     Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002  Sam Lantinga
  4.     This library is free software; you can redistribute it and/or
  5.     modify it under the terms of the GNU Library General Public
  6.     License as published by the Free Software Foundation; either
  7.     version 2 of the License, or (at your option) any later version.
  8.     This library is distributed in the hope that it will be useful,
  9.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  10.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  11.     Library General Public License for more details.
  12.     You should have received a copy of the GNU Library General Public
  13.     License along with this library; if not, write to the Free
  14.     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  15.     Sam Lantinga
  16.     slouken@libsdl.org
  17. */
  18. /* Allow access to a raw mixing buffer */
  19. #include <stdlib.h>
  20. #include <stdio.h>
  21. #include <string.h>
  22. #include <errno.h>
  23. #include <unistd.h>
  24. #include <fcntl.h>
  25. #include <signal.h>
  26. #include <sys/types.h>
  27. #include <sys/time.h>
  28. #include "SDL_audio.h"
  29. #include "SDL_error.h"
  30. #include "SDL_audiomem.h"
  31. #include "SDL_audio_c.h"
  32. #include "SDL_timer.h"
  33. #include "SDL_alsa_audio.h"
  34. /* The tag name used by ALSA audio */
  35. #define DRIVER_NAME         "alsa"
  36. /* default card and device numbers as listed in dev/snd */
  37. static int card_no = 0;
  38. static int device_no = 0;
  39. /* default channel communication parameters */
  40. #define DEFAULT_CPARAMS_RATE 22050
  41. #define DEFAULT_CPARAMS_VOICES 1
  42. #define DEFAULT_CPARAMS_FRAG_SIZE 512
  43. #define DEFAULT_CPARAMS_FRAGS_MIN 1
  44. #define DEFAULT_CPARAMS_FRAGS_MAX -1
  45. /* Open the audio device for playback, and don't block if busy */
  46. #define OPEN_FLAGS (SND_PCM_OPEN_PLAYBACK|SND_PCM_OPEN_NONBLOCK)
  47. /* Audio driver functions */
  48. static int PCM_OpenAudio(_THIS, SDL_AudioSpec *spec);
  49. static void PCM_WaitAudio(_THIS);
  50. static void PCM_PlayAudio(_THIS);
  51. static Uint8 *PCM_GetAudioBuf(_THIS);
  52. static void PCM_CloseAudio(_THIS);
  53. /* PCM transfer channel parameters initialize function */
  54. static void init_pcm_cparams(snd_pcm_channel_params_t* cparams)
  55. {
  56. memset(cparams,0,sizeof(snd_pcm_channel_params_t));
  57. cparams->channel = SND_PCM_CHANNEL_PLAYBACK;
  58. cparams->mode = SND_PCM_MODE_BLOCK;
  59. cparams->start_mode = SND_PCM_START_DATA; //_FULL
  60. cparams->stop_mode  = SND_PCM_STOP_STOP;
  61. cparams->format.format = SND_PCM_SFMT_S16_LE;
  62. cparams->format.interleave = 1;
  63. cparams->format.rate = DEFAULT_CPARAMS_RATE;
  64. cparams->format.voices = DEFAULT_CPARAMS_VOICES;
  65. cparams->buf.block.frag_size = DEFAULT_CPARAMS_FRAG_SIZE;
  66. cparams->buf.block.frags_min = DEFAULT_CPARAMS_FRAGS_MIN;
  67. cparams->buf.block.frags_max = DEFAULT_CPARAMS_FRAGS_MAX;
  68. }
  69. /* Audio driver bootstrap functions */
  70. static int Audio_Available(void)
  71. /*
  72. See if we can open a nonblocking channel.
  73. Return value '1' means we can.
  74. Return value '0' means we cannot.
  75. */
  76. {
  77. int available;
  78. int rval;
  79. snd_pcm_t *handle;
  80. snd_pcm_channel_params_t cparams;
  81. #ifdef DEBUG_AUDIO
  82. snd_pcm_channel_status_t cstatus;
  83. #endif
  84. available = 0;
  85. handle = NULL;
  86. init_pcm_cparams(&cparams);
  87. rval = snd_pcm_open(&handle, card_no, device_no, OPEN_FLAGS);
  88. if (rval >= 0)
  89. {
  90. rval = snd_pcm_plugin_params(handle, &cparams);
  91. #ifdef DEBUG_AUDIO
  92. snd_pcm_plugin_status(handle, &cstatus);
  93. printf("status after snd_pcm_plugin_params call = %dn",cstatus.status);
  94. #endif
  95. if (rval >= 0)
  96. {
  97. available = 1;
  98. }
  99. else
  100. {
  101.          SDL_SetError("snd_pcm_channel_params failed: %sn", snd_strerror (rval));
  102. }
  103.         if ((rval = snd_pcm_close(handle)) < 0)
  104.         {
  105.             SDL_SetError("snd_pcm_close failed: %sn",snd_strerror(rval));
  106. available = 0;
  107.         }
  108. }
  109. else
  110. {
  111.        SDL_SetError("snd_pcm_open failed: %sn", snd_strerror(rval));
  112. }
  113. return(available);
  114. }
  115. static void Audio_DeleteDevice(SDL_AudioDevice *device)
  116. {
  117. free(device->hidden);
  118. free(device);
  119. }
  120. static SDL_AudioDevice *Audio_CreateDevice(int devindex)
  121. {
  122. SDL_AudioDevice *this;
  123. /* Initialize all variables that we clean on shutdown */
  124. this = (SDL_AudioDevice *)malloc(sizeof(SDL_AudioDevice));
  125. if ( this ) {
  126. memset(this, 0, (sizeof *this));
  127. this->hidden = (struct SDL_PrivateAudioData *)
  128. malloc((sizeof *this->hidden));
  129. }
  130. if ( (this == NULL) || (this->hidden == NULL) ) {
  131. SDL_OutOfMemory();
  132. if ( this ) {
  133. free(this);
  134. }
  135. return(0);
  136. }
  137. memset(this->hidden, 0, (sizeof *this->hidden));
  138. audio_handle = NULL;
  139. /* Set the function pointers */
  140. this->OpenAudio = PCM_OpenAudio;
  141. this->WaitAudio = PCM_WaitAudio;
  142. this->PlayAudio = PCM_PlayAudio;
  143. this->GetAudioBuf = PCM_GetAudioBuf;
  144. this->CloseAudio = PCM_CloseAudio;
  145. this->free = Audio_DeleteDevice;
  146. return this;
  147. }
  148. AudioBootStrap ALSA_bootstrap = {
  149. DRIVER_NAME, "ALSA PCM audio",
  150. Audio_Available, Audio_CreateDevice
  151. };
  152. /* This function waits until it is possible to write a full sound buffer */
  153. static void PCM_WaitAudio(_THIS)
  154. {
  155. /* Check to see if the thread-parent process is still alive */
  156. { static int cnt = 0;
  157. /* Note that this only works with thread implementations 
  158.    that use a different process id for each thread.
  159. */
  160. if (parent && (((++cnt)%10) == 0)) { /* Check every 10 loops */
  161. if ( kill(parent, 0) < 0 ) {
  162. this->enabled = 0;
  163. }
  164. }
  165. }
  166. /* See if we need to use timed audio synchronization */
  167. if ( frame_ticks ) 
  168. {
  169. /* Use timer for general audio synchronization */
  170. Sint32 ticks;
  171. ticks = ((Sint32)(next_frame - SDL_GetTicks()))-FUDGE_TICKS;
  172. if ( ticks > 0 ) 
  173. {
  174. SDL_Delay(ticks);
  175. }
  176. }
  177.     else 
  178. {
  179.      /* Use select() for audio synchronization */
  180. fd_set fdset;
  181.     struct timeval timeout;
  182.      FD_ZERO(&fdset);
  183.     FD_SET(audio_fd, &fdset);
  184.      timeout.tv_sec = 10;
  185.     timeout.tv_usec = 0;
  186. #ifdef DEBUG_AUDIO
  187.      fprintf(stderr, "Waiting for audio to get readyn");
  188. #endif
  189.     if ( select(audio_fd+1, NULL, &fdset, NULL, &timeout) <= 0 ) 
  190. {
  191.             const char *message =
  192.             "Audio timeout - buggy audio driver? (disabled)";
  193.         /* In general we should never print to the screen,
  194.                but in this case we have no other way of letting
  195.                the user know what happened.
  196.             */
  197.             fprintf(stderr, "SDL: %sn", message);
  198.             this->enabled = 0;
  199.             /* Don't try to close - may hang */
  200.             audio_fd = -1;
  201. #ifdef DEBUG_AUDIO
  202.             fprintf(stderr, "Done disabling audion");
  203. #endif
  204.          }
  205. #ifdef DEBUG_AUDIO
  206.         fprintf(stderr, "Ready!n");
  207. #endif
  208.     }
  209. }
  210. static snd_pcm_channel_status_t cstatus;
  211. static void PCM_PlayAudio(_THIS)
  212. {
  213.     int written, rval;
  214.     /* Write the audio data, checking for EAGAIN (buffer full) and underrun */
  215.     do {
  216. written = snd_pcm_plugin_write(audio_handle, pcm_buf, pcm_len);
  217. #ifdef DEBUG_AUDIO
  218. fprintf(stderr, "written = %d pcm_len = %dn",written,pcm_len);
  219. #endif
  220. if (written != pcm_len)
  221. {
  222.         if (errno == EAGAIN) 
  223. {
  224.              SDL_Delay(1);   /* Let a little CPU time go by and try to write again */
  225. #ifdef DEBUG_AUDIO
  226. fprintf(stderr, "errno == EAGAINn");
  227. #endif
  228.          }
  229. else
  230. {
  231.         if( (rval = snd_pcm_plugin_status(audio_handle, &cstatus)) < 0 )
  232.          {
  233.             SDL_SetError("snd_pcm_plugin_status failed: %sn", snd_strerror(rval));
  234.              return;
  235.         }
  236. if ( (cstatus.status == SND_PCM_STATUS_UNDERRUN)
  237. ||(cstatus.status == SND_PCM_STATUS_READY) )
  238. {
  239. #ifdef DEBUG_AUDIO
  240. fprintf(stderr, "buffer underrunn");
  241. #endif
  242. if ( (rval = snd_pcm_plugin_prepare (audio_handle,SND_PCM_CHANNEL_PLAYBACK)) < 0 )
  243. {
  244. SDL_SetError("snd_pcm_plugin_prepare failed: %sn",snd_strerror(rval) );
  245. return;
  246. }
  247. /* if we reach here, try to write again */
  248. }
  249. }
  250. }
  251.     } while ( (written < 0) && ((errno == 0) || (errno == EAGAIN)) );
  252.     /* Set the next write frame */
  253.    if ( frame_ticks ) {
  254.     next_frame += frame_ticks;
  255. }
  256.     /* If we couldn't write, assume fatal error for now */
  257.     if ( written < 0 ) {
  258.         this->enabled = 0;
  259.     }
  260. return;
  261. }
  262. static Uint8 *PCM_GetAudioBuf(_THIS)
  263. {
  264. return(pcm_buf);
  265. }
  266. static void PCM_CloseAudio(_THIS)
  267. {
  268. int rval;
  269. if ( pcm_buf != NULL ) {
  270. free(pcm_buf);
  271. pcm_buf = NULL;
  272. }
  273. if ( audio_handle != NULL ) {
  274. if ((rval = snd_pcm_plugin_flush(audio_handle,SND_PCM_CHANNEL_PLAYBACK)) < 0)
  275. {
  276.          SDL_SetError("snd_pcm_plugin_flush failed: %sn",snd_strerror(rval));
  277. return;
  278. }
  279. if ((rval = snd_pcm_close(audio_handle)) < 0)
  280. {
  281. SDL_SetError("snd_pcm_close failed: %sn",snd_strerror(rval));
  282. return;
  283. }
  284. audio_handle = NULL;
  285. }
  286. }
  287. static int PCM_OpenAudio(_THIS, SDL_AudioSpec *spec)
  288. {
  289. int rval;
  290. snd_pcm_channel_params_t cparams;
  291. snd_pcm_channel_setup_t  csetup;
  292. int format;
  293. Uint16 test_format;
  294. int twidth;
  295. /* initialize channel transfer parameters to default */
  296. init_pcm_cparams(&cparams);
  297. /* Reset the timer synchronization flag */
  298. frame_ticks = 0.0;
  299. /* Open the audio device */
  300. rval = snd_pcm_open(&audio_handle, card_no, device_no, OPEN_FLAGS);
  301. if ( rval < 0 ) {
  302. SDL_SetError("snd_pcm_open failed: %sn", snd_strerror(rval));
  303. return(-1);
  304. }
  305. #ifdef PLUGIN_DISABLE_MMAP /* This is gone in newer versions of ALSA? */
  306.     /* disable count status parameter */
  307.     if ((rval = snd_plugin_set_disable(audio_handle, PLUGIN_DISABLE_MMAP))<0)
  308.     {
  309.         SDL_SetError("snd_plugin_set_disable failed: %sn", snd_strerror(rval));
  310.         return(-1);
  311.     }
  312. #endif
  313. pcm_buf = NULL;
  314. /* Try for a closest match on audio format */
  315. format = 0;
  316. for ( test_format = SDL_FirstAudioFormat(spec->format);
  317. ! format && test_format; ) 
  318. {
  319. #ifdef DEBUG_AUDIO
  320. fprintf(stderr, "Trying format 0x%4.4x spec->samples %dn", test_format,spec->samples);
  321. #endif
  322. /* if match found set format to equivalent ALSA format */
  323.         switch ( test_format ) {
  324. case AUDIO_U8:
  325. format = SND_PCM_SFMT_U8;
  326. cparams.buf.block.frag_size = spec->samples * spec->channels;
  327. break;
  328. case AUDIO_S8:
  329. format = SND_PCM_SFMT_S8;
  330. cparams.buf.block.frag_size = spec->samples * spec->channels;
  331. break;
  332. case AUDIO_S16LSB:
  333. format = SND_PCM_SFMT_S16_LE;
  334. cparams.buf.block.frag_size = spec->samples*2 * spec->channels;
  335. break;
  336. case AUDIO_S16MSB:
  337. format = SND_PCM_SFMT_S16_BE;
  338. cparams.buf.block.frag_size = spec->samples*2 * spec->channels;
  339. break;
  340. case AUDIO_U16LSB:
  341. format = SND_PCM_SFMT_U16_LE;
  342. cparams.buf.block.frag_size = spec->samples*2 * spec->channels;
  343. break;
  344. case AUDIO_U16MSB:
  345. format = SND_PCM_SFMT_U16_BE;
  346. cparams.buf.block.frag_size = spec->samples*2 * spec->channels;
  347. break;
  348. default:
  349. break;
  350. }
  351. if ( ! format ) {
  352. test_format = SDL_NextAudioFormat();
  353. }
  354. }
  355. if ( format == 0 ) {
  356. SDL_SetError("Couldn't find any hardware audio formats");
  357. return(-1);
  358. }
  359. spec->format = test_format;
  360. /* Set the audio format */
  361. cparams.format.format = format;
  362. /* Set mono or stereo audio (currently only two channels supported) */
  363. cparams.format.voices = spec->channels;
  364. #ifdef DEBUG_AUDIO
  365. printf("intializing channels %dn", cparams.format.voices);
  366. #endif
  367. /* Set rate */
  368. cparams.format.rate = spec->freq ;
  369. /* Setup the transfer parameters according to cparams */
  370. rval = snd_pcm_plugin_params(audio_handle, &cparams);
  371. if (rval < 0) {
  372. SDL_SetError("snd_pcm_channel_params failed: %sn", snd_strerror (rval));
  373. return(-1);
  374. }
  375.     /*  Make sure channel is setup right one last time */
  376.     memset( &csetup, 0, sizeof( csetup ) );
  377.     csetup.channel = SND_PCM_CHANNEL_PLAYBACK;
  378.     if ( snd_pcm_plugin_setup( audio_handle, &csetup ) < 0 )
  379.     {
  380.         SDL_SetError("Unable to setup playback channeln" );
  381.         return(-1);
  382.     }
  383. #ifdef DEBUG_AUDIO
  384.     else
  385.     {
  386.         fprintf(stderr,"requested format: %dn",cparams.format.format);
  387.         fprintf(stderr,"requested frag size: %dn",cparams.buf.block.frag_size);
  388.         fprintf(stderr,"requested max frags: %dnn",cparams.buf.block.frags_max);
  389.         fprintf(stderr,"real format: %dn", csetup.format.format );
  390.         fprintf(stderr,"real frag size : %dn", csetup.buf.block.frag_size );
  391. fprintf(stderr,"real max frags : %dn", csetup.buf.block.frags_max );
  392.     }
  393. #endif // DEBUG_AUDIO
  394.     /*  Allocate memory to the audio buffer and initialize with silence
  395.         (Note that buffer size must be a multiple of fragment size, so find closest multiple)
  396.     */
  397.     
  398.     twidth = snd_pcm_format_width(format);
  399.     if (twidth < 0) {
  400.         printf("snd_pcm_format_width failedn");
  401.         twidth = 0;
  402.     }
  403. #ifdef DEBUG_AUDIO
  404.     printf("format is %d bits widen",twidth);
  405. #endif      
  406.     
  407.     pcm_len = csetup.buf.block.frag_size * (twidth/8) * csetup.format.voices ;
  408.     
  409. #ifdef DEBUG_AUDIO    
  410.     printf("pcm_len set to %dn", pcm_len);
  411. #endif
  412.     
  413.     if (pcm_len == 0)
  414.     {
  415.         pcm_len = csetup.buf.block.frag_size;
  416.     }
  417.     
  418.     pcm_buf = (Uint8*)malloc(pcm_len);
  419.     if (pcm_buf == NULL) {
  420.         SDL_SetError("pcm_buf malloc failedn");
  421.         return(-1);
  422.     }
  423.     memset(pcm_buf,spec->silence,pcm_len);
  424. #ifdef DEBUG_AUDIO
  425. fprintf(stderr,"pcm_buf malloced and silenced.n");
  426. #endif
  427.     /* get the file descriptor */
  428.     if( (audio_fd = snd_pcm_file_descriptor(audio_handle, device_no)) < 0)
  429.     {
  430.        fprintf(stderr, "snd_pcm_file_descriptor failed with error code: %dn", audio_fd);
  431.     }
  432. /* Trigger audio playback */
  433. rval = snd_pcm_plugin_prepare( audio_handle, SND_PCM_CHANNEL_PLAYBACK);
  434. if (rval < 0) {
  435.        SDL_SetError("snd_pcm_plugin_prepare failed: %sn", snd_strerror (rval));
  436.        return(-1);
  437. }
  438. rval =  snd_pcm_playback_go(audio_handle);
  439.     if (rval < 0) {
  440.        SDL_SetError("snd_pcm_playback_go failed: %sn", snd_strerror (rval));
  441.        return(-1);
  442.     }
  443.     /* Check to see if we need to use select() workaround */
  444.     { char *workaround;
  445.         workaround = getenv("SDL_DSP_NOSELECT");
  446.         if ( workaround ) {
  447.             frame_ticks = (float)(spec->samples*1000)/spec->freq;
  448.             next_frame = SDL_GetTicks()+frame_ticks;
  449.         }
  450.     }
  451. /* Get the parent process id (we're the parent of the audio thread) */
  452. parent = getpid();
  453. /* We're ready to rock and roll. :-) */
  454. return(0);
  455. }