sndout_oss.c 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. /* sound output via OSS */
  2. #include <stdio.h>
  3. #include <sys/types.h>
  4. #include <sys/stat.h>
  5. #include <fcntl.h>
  6. #include <sys/ioctl.h>
  7. #include <sys/soundcard.h>
  8. #include <unistd.h>
  9. #include "sndout_oss.h"
  10. static int sounddev = -1, mixerdev = -1;
  11. static int can_write_safe;
  12. #define FRAG_COUNT 4
  13. int sndout_oss_init(void)
  14. {
  15. if (mixerdev >= 0) close(mixerdev);
  16. mixerdev = open("/dev/mixer", O_RDWR);
  17. if (mixerdev == -1)
  18. {
  19. perror("open(\"/dev/mixer\")");
  20. }
  21. return 0;
  22. }
  23. void sndout_oss_stop(void)
  24. {
  25. if (sounddev < 0)
  26. return;
  27. ioctl(sounddev, SOUND_PCM_SYNC, 0);
  28. close(sounddev);
  29. sounddev = -1;
  30. }
  31. int sndout_oss_start(int rate, int stereo, int frames_in_frag)
  32. {
  33. static int s_oldrate = 0, s_oldstereo = 0;
  34. int frag, bsize, bits, ret;
  35. // GP2X: if no settings change, we don't need to do anything,
  36. // since audio is never stopped there
  37. if (sounddev >= 0 && rate == s_oldrate && s_oldstereo == stereo)
  38. return 0;
  39. sndout_oss_stop();
  40. sounddev = open("/dev/dsp", O_WRONLY);
  41. if (sounddev == -1)
  42. {
  43. perror("open(\"/dev/dsp\")");
  44. sounddev = open("/dev/dsp1", O_WRONLY);
  45. if (sounddev == -1) {
  46. perror("open(\"/dev/dsp1\")");
  47. return -1;
  48. }
  49. }
  50. // try to fit frames_in_frag frames worth of data in fragment
  51. // ignore mono because it's unlikely to be used and
  52. // both GP2X and Wiz mixes mono to stereo anyway.
  53. bsize = (frames_in_frag * rate / 50) * 4;
  54. for (frag = 0; bsize; bsize >>= 1, frag++)
  55. ;
  56. frag |= FRAG_COUNT << 16; // fragment count
  57. ret = ioctl(sounddev, SNDCTL_DSP_SETFRAGMENT, &frag);
  58. if (ret < 0)
  59. perror("SNDCTL_DSP_SETFRAGMENT failed");
  60. bits = 16;
  61. ret = ioctl(sounddev, SNDCTL_DSP_STEREO, &stereo);
  62. if (ret == 0)
  63. ret = ioctl(sounddev, SNDCTL_DSP_SETFMT, &bits);
  64. if (ret == 0)
  65. ret = ioctl(sounddev, SNDCTL_DSP_SPEED, &rate);
  66. if (ret < 0)
  67. perror("failed to set audio format");
  68. #ifdef __GP2X__
  69. // not sure if this is still needed (avoiding driver bugs?)
  70. usleep(192*1024);
  71. #endif
  72. printf("sndout_oss_start: %d/%dbit/%s, %d buffers of %i bytes\n",
  73. rate, bits, stereo ? "stereo" : "mono", frag >> 16, 1 << (frag & 0xffff));
  74. s_oldrate = rate; s_oldstereo = stereo;
  75. can_write_safe = 0;
  76. return 0;
  77. }
  78. int sndout_oss_write(const void *buff, int len)
  79. {
  80. return write(sounddev, buff, len);
  81. }
  82. #include "../common/plat.h"
  83. /* not really non-blocking, just detects if blocking occurs
  84. * and starts skipping writes in case it does. */
  85. int sndout_oss_write_nb(const void *buff, int len)
  86. {
  87. static int lag_counter, skip_counter;
  88. unsigned int t;
  89. int ret;
  90. if (lag_counter > 2) {
  91. // skip writes if audio starts blocking
  92. lag_counter = 0;
  93. skip_counter = FRAG_COUNT;
  94. }
  95. if (skip_counter > 0) {
  96. skip_counter--;
  97. return len;
  98. }
  99. t = plat_get_ticks_ms();
  100. ret = sndout_oss_write(buff, len);
  101. t = plat_get_ticks_ms() - t;
  102. if (t > 1) {
  103. // this shouldn't really happen, most likely audio is out of sync
  104. lag_counter++;
  105. if (lag_counter > 2)
  106. printf("audio lag %u\n", t);
  107. }
  108. else
  109. lag_counter = 0;
  110. return ret;
  111. }
  112. int sndout_oss_can_write(int bytes)
  113. {
  114. audio_buf_info bi;
  115. int ret;
  116. #ifdef __GP2X__
  117. // note: SNDCTL_DSP_GETOSPACE crashes F100 kernel for some reason
  118. // if called too early, so we work around here
  119. if (can_write_safe++ < 8)
  120. return 1;
  121. #endif
  122. ret = ioctl(sounddev, SNDCTL_DSP_GETOSPACE, &bi);
  123. if (ret < 0)
  124. return 1;
  125. // have enough bytes to write + 1 frag
  126. return bi.bytes - bi.fragsize >= bytes ? 1 : 0;
  127. }
  128. void sndout_oss_sync(void)
  129. {
  130. ioctl(sounddev, SOUND_PCM_SYNC, 0);
  131. }
  132. void sndout_oss_setvol(int l, int r)
  133. {
  134. if (mixerdev < 0) return;
  135. l=l<0?0:l; l=l>255?255:l; r=r<0?0:r; r=r>255?255:r;
  136. l<<=8; l|=r;
  137. ioctl(mixerdev, SOUND_MIXER_WRITE_PCM, &l); /*SOUND_MIXER_WRITE_VOLUME*/
  138. }
  139. void sndout_oss_exit(void)
  140. {
  141. if (sounddev >= 0) close(sounddev); sounddev = -1;
  142. if (mixerdev >= 0) close(mixerdev); mixerdev = -1;
  143. }