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

流媒体/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. #ifdef SAVE_RCSID
  19. static char rcsid =
  20.  "@(#) $Id: SDL_dmaaudio.c,v 1.4 2002/04/22 21:38:02 wmay Exp $";
  21. #endif
  22. /* Allow access to a raw mixing buffer */
  23. #include <stdlib.h>
  24. #include <stdio.h>
  25. #include <string.h>
  26. #include <errno.h>
  27. #include <unistd.h>
  28. #include <fcntl.h>
  29. #include <signal.h>
  30. #include <sys/types.h>
  31. #include <sys/time.h>
  32. #include <sys/ioctl.h>
  33. #include <sys/stat.h>
  34. #include <sys/mman.h>
  35. #ifdef OSS_USE_SOUNDCARD_H
  36. /* This is installed on some systems */
  37. #include <soundcard.h>
  38. #else
  39. /* This is recommended by OSS */
  40. #include <sys/soundcard.h>
  41. #endif
  42. #ifndef MAP_FAILED
  43. #define MAP_FAILED ((Uint8 *)-1)
  44. #endif
  45. #include "SDL_audio.h"
  46. #include "SDL_error.h"
  47. #include "SDL_audiomem.h"
  48. #include "SDL_audio_c.h"
  49. #include "SDL_timer.h"
  50. #include "SDL_audiodev_c.h"
  51. #include "SDL_dmaaudio.h"
  52. /* The tag name used by DMA audio */
  53. #define DMA_DRIVER_NAME         "dma"
  54. /* Open the audio device for playback, and don't block if busy */
  55. #define OPEN_FLAGS (O_RDWR|O_NONBLOCK)
  56. /* Audio driver functions */
  57. static int DMA_OpenAudio(_THIS, SDL_AudioSpec *spec);
  58. static void DMA_WaitAudio(_THIS);
  59. static void DMA_PlayAudio(_THIS);
  60. static Uint8 *DMA_GetAudioBuf(_THIS);
  61. static void DMA_CloseAudio(_THIS);
  62. /* Audio driver bootstrap functions */
  63. static int Audio_Available(void)
  64. {
  65. int available;
  66. int fd;
  67. available = 0;
  68. fd = SDL_OpenAudioPath(NULL, 0, OPEN_FLAGS, 0);
  69. if ( fd >= 0 ) {
  70. int caps;
  71. struct audio_buf_info info;
  72. if ( (ioctl(fd, SNDCTL_DSP_GETCAPS, &caps) == 0) &&
  73.              (caps & DSP_CAP_TRIGGER) && (caps & DSP_CAP_MMAP) &&
  74.      (ioctl(fd, SNDCTL_DSP_GETOSPACE, &info) == 0) ) {
  75. available = 1;
  76. }
  77. close(fd);
  78. }
  79. return(available);
  80. }
  81. static void Audio_DeleteDevice(SDL_AudioDevice *device)
  82. {
  83. free(device->hidden);
  84. free(device);
  85. }
  86. static SDL_AudioDevice *Audio_CreateDevice(int devindex)
  87. {
  88. SDL_AudioDevice *this;
  89. /* Initialize all variables that we clean on shutdown */
  90. this = (SDL_AudioDevice *)malloc(sizeof(SDL_AudioDevice));
  91. if ( this ) {
  92. memset(this, 0, (sizeof *this));
  93. this->hidden = (struct SDL_PrivateAudioData *)
  94. malloc((sizeof *this->hidden));
  95. }
  96. if ( (this == NULL) || (this->hidden == NULL) ) {
  97. SDL_OutOfMemory();
  98. if ( this ) {
  99. free(this);
  100. }
  101. return(0);
  102. }
  103. memset(this->hidden, 0, (sizeof *this->hidden));
  104. audio_fd = -1;
  105. /* Set the function pointers */
  106. this->OpenAudio = DMA_OpenAudio;
  107. this->WaitAudio = DMA_WaitAudio;
  108. this->PlayAudio = DMA_PlayAudio;
  109. this->GetAudioBuf = DMA_GetAudioBuf;
  110. this->CloseAudio = DMA_CloseAudio;
  111. this->free = Audio_DeleteDevice;
  112. return this;
  113. }
  114. AudioBootStrap DMA_bootstrap = {
  115. DMA_DRIVER_NAME, "OSS /dev/dsp DMA audio",
  116. Audio_Available, Audio_CreateDevice
  117. };
  118. /* This function waits until it is possible to write a full sound buffer */
  119. static void DMA_WaitAudio(_THIS)
  120. {
  121. fd_set fdset;
  122. /* Check to see if the thread-parent process is still alive */
  123. { static int cnt = 0;
  124. /* Note that this only works with thread implementations 
  125.    that use a different process id for each thread.
  126. */
  127. if (parent && (((++cnt)%10) == 0)) { /* Check every 10 loops */
  128. if ( kill(parent, 0) < 0 ) {
  129. this->enabled = 0;
  130. }
  131. }
  132. }
  133. /* See if we need to use timed audio synchronization */
  134. if ( frame_ticks ) {
  135. /* Use timer for general audio synchronization */
  136. Sint32 ticks;
  137. ticks = ((Sint32)(next_frame - SDL_GetTicks()))-FUDGE_TICKS;
  138. if ( ticks > 0 ) {
  139. SDL_Delay(ticks);
  140. }
  141. } else {
  142. /* Use select() for audio synchronization */
  143. struct timeval timeout;
  144. FD_ZERO(&fdset);
  145. FD_SET(audio_fd, &fdset);
  146. timeout.tv_sec = 10;
  147. timeout.tv_usec = 0;
  148. #ifdef DEBUG_AUDIO
  149. fprintf(stderr, "Waiting for audio to get readyn");
  150. #endif
  151. if ( select(audio_fd+1, NULL, &fdset, NULL, &timeout) <= 0 ) {
  152. const char *message =
  153. #ifdef AUDIO_OSPACE_HACK
  154. "Audio timeout - buggy audio driver? (trying ospace)";
  155. #else
  156. "Audio timeout - buggy audio driver? (disabled)";
  157. #endif
  158. /* In general we should never print to the screen,
  159.    but in this case we have no other way of letting
  160.    the user know what happened.
  161. */
  162. fprintf(stderr, "SDL: %sn", message);
  163. #ifdef AUDIO_OSPACE_HACK
  164. /* We may be able to use GET_OSPACE trick */
  165. frame_ticks = (float)(this->spec->samples*1000) /
  166.                       this->spec->freq;
  167. next_frame = SDL_GetTicks()+frame_ticks;
  168. #else
  169. this->enabled = 0;
  170. /* Don't try to close - may hang */
  171. audio_fd = -1;
  172. #ifdef DEBUG_AUDIO
  173. fprintf(stderr, "Done disabling audion");
  174. #endif
  175. #endif /* AUDIO_OSPACE_HACK */
  176. }
  177. #ifdef DEBUG_AUDIO
  178. fprintf(stderr, "Ready!n");
  179. #endif
  180. }
  181. }
  182. static void DMA_PlayAudio(_THIS)
  183. {
  184. /* If timer synchronization is enabled, set the next write frame */
  185. if ( frame_ticks ) {
  186. next_frame += frame_ticks;
  187. }
  188. return;
  189. }
  190. static Uint8 *DMA_GetAudioBuf(_THIS)
  191. {
  192. count_info info;
  193. int playing;
  194. int filling;
  195. /* Get number of blocks, looping if we're not using select() */
  196. do {
  197. if ( ioctl(audio_fd, SNDCTL_DSP_GETOPTR, &info) < 0 ) {
  198. /* Uh oh... */
  199. this->enabled = 0;
  200. return(NULL);
  201. }
  202. } while ( frame_ticks && (info.blocks < 1) );
  203. #ifdef DEBUG_AUDIO
  204. if ( info.blocks > 1 ) {
  205. printf("Warning: audio underflow (%d frags)n", info.blocks-1);
  206. }
  207. #endif
  208. playing = info.ptr / this->spec.size;
  209. filling = (playing + 1)%num_buffers;
  210. return (dma_buf + (filling * this->spec.size));
  211. }
  212. static void DMA_CloseAudio(_THIS)
  213. {
  214. if ( dma_buf != NULL ) {
  215. munmap(dma_buf, dma_len);
  216. dma_buf = NULL;
  217. }
  218. if ( audio_fd >= 0 ) {
  219. close(audio_fd);
  220. audio_fd = -1;
  221. }
  222. }
  223. static int DMA_ReopenAudio(_THIS, const char *audiodev, int format, int stereo,
  224. SDL_AudioSpec *spec)
  225. {
  226. int frag_spec;
  227. int value;
  228. /* Close and then reopen the audio device */
  229. close(audio_fd);
  230. audio_fd = open(audiodev, O_RDWR, 0);
  231. if ( audio_fd < 0 ) {
  232. SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno));
  233. return(-1);
  234. }
  235. /* Calculate the final parameters for this audio specification */
  236. SDL_CalculateAudioSpec(spec);
  237. /* Determine the power of two of the fragment size */
  238. for ( frag_spec = 0; (0x01<<frag_spec) < spec->size; ++frag_spec );
  239. if ( (0x01<<frag_spec) != spec->size ) {
  240. SDL_SetError("Fragment size must be a power of two");
  241. return(-1);
  242. }
  243. /* Set the audio buffering parameters */
  244. if ( ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &frag_spec) < 0 ) {
  245. SDL_SetError("Couldn't set audio fragment spec");
  246. return(-1);
  247. }
  248. /* Set the audio format */
  249. value = format;
  250. if ( (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) ||
  251. (value != format) ) {
  252. SDL_SetError("Couldn't set audio format");
  253. return(-1);
  254. }
  255. /* Set mono or stereo audio */
  256. value = (spec->channels > 1);
  257. if ( (ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo) < 0) ||
  258. (value != stereo) ) {
  259. SDL_SetError("Couldn't set audio channels");
  260. return(-1);
  261. }
  262. /* Set the DSP frequency */
  263. value = spec->freq;
  264. if ( ioctl(audio_fd, SOUND_PCM_WRITE_RATE, &value) < 0 ) {
  265. SDL_SetError("Couldn't set audio frequency");
  266. return(-1);
  267. }
  268. spec->freq = value;
  269. /* We successfully re-opened the audio */
  270. return(0);
  271. }
  272. static int DMA_OpenAudio(_THIS, SDL_AudioSpec *spec)
  273. {
  274. char audiodev[1024];
  275. int format;
  276. int stereo;
  277. int value;
  278. Uint16 test_format;
  279. struct audio_buf_info info;
  280. /* Reset the timer synchronization flag */
  281. frame_ticks = 0.0;
  282. /* Open the audio device */
  283. audio_fd = SDL_OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 0);
  284. if ( audio_fd < 0 ) {
  285. SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno));
  286. return(-1);
  287. }
  288. dma_buf = NULL;
  289. ioctl(audio_fd, SNDCTL_DSP_RESET, 0);
  290. /* Get a list of supported hardware formats */
  291. if ( ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &value) < 0 ) {
  292. SDL_SetError("Couldn't get audio format list");
  293. return(-1);
  294. }
  295. /* Try for a closest match on audio format */
  296. format = 0;
  297. for ( test_format = SDL_FirstAudioFormat(spec->format);
  298. ! format && test_format; ) {
  299. #ifdef DEBUG_AUDIO
  300. fprintf(stderr, "Trying format 0x%4.4xn", test_format);
  301. #endif
  302. switch ( test_format ) {
  303. case AUDIO_U8:
  304. if ( value & AFMT_U8 ) {
  305. format = AFMT_U8;
  306. }
  307. break;
  308. case AUDIO_S8:
  309. if ( value & AFMT_S8 ) {
  310. format = AFMT_S8;
  311. }
  312. break;
  313. case AUDIO_S16LSB:
  314. if ( value & AFMT_S16_LE ) {
  315. format = AFMT_S16_LE;
  316. }
  317. break;
  318. case AUDIO_S16MSB:
  319. if ( value & AFMT_S16_BE ) {
  320. format = AFMT_S16_BE;
  321. }
  322. break;
  323. case AUDIO_U16LSB:
  324. if ( value & AFMT_U16_LE ) {
  325. format = AFMT_U16_LE;
  326. }
  327. break;
  328. case AUDIO_U16MSB:
  329. if ( value & AFMT_U16_BE ) {
  330. format = AFMT_U16_BE;
  331. }
  332. break;
  333. default:
  334. break;
  335. }
  336. if ( ! format ) {
  337. test_format = SDL_NextAudioFormat();
  338. }
  339. }
  340. if ( format == 0 ) {
  341. SDL_SetError("Couldn't find any hardware audio formats");
  342. return(-1);
  343. }
  344. spec->format = test_format;
  345. /* Set the audio format */
  346. value = format;
  347. if ( (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) ||
  348. (value != format) ) {
  349. SDL_SetError("Couldn't set audio format");
  350. return(-1);
  351. }
  352. /* Set mono or stereo audio (currently only two channels supported) */
  353. stereo = (spec->channels > 1);
  354. ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo);
  355. if ( stereo ) {
  356. spec->channels = 2;
  357. } else {
  358. spec->channels = 1;
  359. }
  360. /* Because some drivers don't allow setting the buffer size
  361.    after setting the format, we must re-open the audio device
  362.    once we know what format and channels are supported
  363.  */
  364. if ( DMA_ReopenAudio(this, audiodev, format, stereo, spec) < 0 ) {
  365. /* Error is set by DMA_ReopenAudio() */
  366. return(-1);
  367. }
  368. /* Memory map the audio buffer */
  369. if ( ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &info) < 0 ) {
  370. SDL_SetError("Couldn't get OSPACE parameters");
  371. return(-1);
  372. }
  373. spec->size = info.fragsize;
  374. spec->samples = spec->size / ((spec->format & 0xFF) / 8);
  375. spec->samples /= spec->channels;
  376. num_buffers = info.fragstotal;
  377. dma_len = num_buffers*spec->size;
  378. dma_buf = (Uint8 *)mmap(NULL, dma_len, PROT_WRITE, MAP_SHARED,
  379. audio_fd, 0);
  380. if ( dma_buf == MAP_FAILED ) {
  381. SDL_SetError("DMA memory map failed");
  382. dma_buf = NULL;
  383. return(-1);
  384. }
  385. memset(dma_buf, spec->silence, dma_len);
  386. /* Check to see if we need to use select() workaround */
  387. { char *workaround;
  388. workaround = getenv("SDL_DSP_NOSELECT");
  389. if ( workaround ) {
  390. frame_ticks = (float)(spec->samples*1000)/spec->freq;
  391. next_frame = SDL_GetTicks()+frame_ticks;
  392. }
  393. }
  394. /* Trigger audio playback */
  395. value = 0;
  396. ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &value);
  397. value = PCM_ENABLE_OUTPUT;
  398. if ( ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &value) < 0 ) {
  399. SDL_SetError("Couldn't trigger audio output");
  400. return(-1);
  401. }
  402. /* Get the parent process id (we're the parent of the audio thread) */
  403. parent = getpid();
  404. /* We're ready to rock and roll. :-) */
  405. return(0);
  406. }