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

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