linux_audio.c 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. #ifdef IRON_A2
  2. #include <iron_audio.h>
  3. #include <alsa/asoundlib.h>
  4. #include <errno.h>
  5. #include <poll.h>
  6. #include <pthread.h>
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. // apt-get install libasound2-dev
  10. iron_a2_buffer_t a2_buffer;
  11. pthread_t threadid;
  12. bool audioRunning = false;
  13. snd_pcm_t *playback_handle;
  14. short buf[4096 * 4];
  15. static unsigned int samples_per_second = 44100;
  16. uint32_t iron_a2_samples_per_second(void) {
  17. return samples_per_second;
  18. }
  19. void copySample(void *buffer) {
  20. float left_value = *(float *)&a2_buffer.channels[0][a2_buffer.read_location];
  21. float right_value = *(float *)&a2_buffer.channels[1][a2_buffer.read_location];
  22. a2_buffer.read_location += 1;
  23. if (a2_buffer.read_location >= a2_buffer.data_size) {
  24. a2_buffer.read_location = 0;
  25. }
  26. ((int16_t *)buffer)[0] = (int16_t)(left_value * 32767);
  27. ((int16_t *)buffer)[1] = (int16_t)(right_value * 32767);
  28. }
  29. int playback_callback(snd_pcm_sframes_t nframes) {
  30. int err = 0;
  31. if (iron_a2_internal_callback(&a2_buffer, nframes)) {
  32. int ni = 0;
  33. while (ni < nframes) {
  34. int i = 0;
  35. for (; ni < nframes && i < 4096 * 2; ++i, ++ni) {
  36. copySample(&buf[i * 2]);
  37. }
  38. int err2;
  39. if ((err2 = snd_pcm_writei(playback_handle, buf, i)) < 0) {
  40. fprintf(stderr, "write failed (%s)\n", snd_strerror(err2));
  41. }
  42. err += err2;
  43. }
  44. }
  45. return err;
  46. }
  47. bool tryToRecover(snd_pcm_t *handle, int errorCode) {
  48. switch (-errorCode) {
  49. case EINTR:
  50. case EPIPE:
  51. case ESPIPE:
  52. case ESTRPIPE:
  53. {
  54. int recovered = snd_pcm_recover(playback_handle, errorCode, 1);
  55. if (recovered != 0) {
  56. fprintf(stderr, "unable to recover from ALSA error code=%i\n", errorCode);
  57. return false;
  58. }
  59. else {
  60. fprintf(stdout, "recovered from ALSA error code=%i\n", errorCode);
  61. return true;
  62. }
  63. }
  64. default:
  65. fprintf(stderr, "unhandled ALSA error code=%i\n", errorCode);
  66. return false;
  67. }
  68. }
  69. void *doAudio(void *arg) {
  70. snd_pcm_hw_params_t *hw_params;
  71. snd_pcm_sw_params_t *sw_params;
  72. snd_pcm_sframes_t frames_to_deliver;
  73. int err;
  74. if ((err = snd_pcm_open(&playback_handle, "default", SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
  75. fprintf(stderr, "cannot open audio device default (%s)\n", snd_strerror(err));
  76. return NULL;
  77. }
  78. if ((err = snd_pcm_hw_params_malloc(&hw_params)) < 0) {
  79. fprintf(stderr, "cannot allocate hardware parameter structure (%s)\n", snd_strerror(err));
  80. return NULL;
  81. }
  82. if ((err = snd_pcm_hw_params_any(playback_handle, hw_params)) < 0) {
  83. fprintf(stderr, "cannot initialize hardware parameter structure (%s)\n", snd_strerror(err));
  84. return NULL;
  85. }
  86. if ((err = snd_pcm_hw_params_set_access(playback_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
  87. fprintf(stderr, "cannot set access type (%s)\n", snd_strerror(err));
  88. return NULL;
  89. }
  90. if ((err = snd_pcm_hw_params_set_format(playback_handle, hw_params, SND_PCM_FORMAT_S16_LE)) < 0) {
  91. fprintf(stderr, "cannot set sample format (%s)\n", snd_strerror(err));
  92. return NULL;
  93. }
  94. int dir = 0;
  95. if ((err = snd_pcm_hw_params_set_rate_near(playback_handle, hw_params, &samples_per_second, &dir)) < 0) {
  96. fprintf(stderr, "cannot set sample rate (%s)\n", snd_strerror(err));
  97. return NULL;
  98. }
  99. if ((err = snd_pcm_hw_params_set_channels(playback_handle, hw_params, 2)) < 0) {
  100. fprintf(stderr, "cannot set channel count (%s)\n", snd_strerror(err));
  101. return NULL;
  102. }
  103. snd_pcm_uframes_t bufferSize = samples_per_second / 8;
  104. if (((err = snd_pcm_hw_params_set_buffer_size(playback_handle, hw_params, bufferSize)) < 0 &&
  105. (snd_pcm_hw_params_set_buffer_size_near(playback_handle, hw_params, &bufferSize)) < 0)) {
  106. fprintf(stderr, "cannot set buffer size (%s)\n", snd_strerror(err));
  107. return NULL;
  108. }
  109. if ((err = snd_pcm_hw_params(playback_handle, hw_params)) < 0) {
  110. fprintf(stderr, "cannot set parameters (%s)\n", snd_strerror(err));
  111. return NULL;
  112. }
  113. snd_pcm_hw_params_free(hw_params);
  114. /* tell ALSA to wake us up whenever 4096 or more frames
  115. of playback data can be delivered. Also, tell
  116. ALSA that we'll start the device ourselves.
  117. */
  118. if ((err = snd_pcm_sw_params_malloc(&sw_params)) < 0) {
  119. fprintf(stderr, "cannot allocate software parameters structure (%s)\n", snd_strerror(err));
  120. return NULL;
  121. }
  122. if ((err = snd_pcm_sw_params_current(playback_handle, sw_params)) < 0) {
  123. fprintf(stderr, "cannot initialize software parameters structure (%s)\n", snd_strerror(err));
  124. return NULL;
  125. }
  126. if ((err = snd_pcm_sw_params_set_avail_min(playback_handle, sw_params, 4096)) < 0) {
  127. fprintf(stderr, "cannot set minimum available count (%s)\n", snd_strerror(err));
  128. return NULL;
  129. }
  130. if ((err = snd_pcm_sw_params_set_start_threshold(playback_handle, sw_params, 0U)) < 0) {
  131. fprintf(stderr, "cannot set start mode (%s)\n", snd_strerror(err));
  132. return NULL;
  133. }
  134. if ((err = snd_pcm_sw_params(playback_handle, sw_params)) < 0) {
  135. fprintf(stderr, "cannot set software parameters (%s)\n", snd_strerror(err));
  136. return NULL;
  137. }
  138. /* the interface will interrupt the kernel every 4096 frames, and ALSA
  139. will wake up this program very soon after that.
  140. */
  141. if ((err = snd_pcm_prepare(playback_handle)) < 0) {
  142. fprintf(stderr, "cannot prepare audio interface for use (%s)\n", snd_strerror(err));
  143. return NULL;
  144. }
  145. while (audioRunning) {
  146. /* wait till the interface is ready for data, or 1 second
  147. has elapsed.
  148. */
  149. if ((err = snd_pcm_wait(playback_handle, 1000)) < 0) {
  150. fprintf(stderr, "poll failed (%s)\n", strerror(errno));
  151. break;
  152. }
  153. /* find out how much space is available for playback data */
  154. if ((frames_to_deliver = snd_pcm_avail_update(playback_handle)) < 0) {
  155. if (!tryToRecover(playback_handle, frames_to_deliver)) {
  156. // break;
  157. }
  158. }
  159. else {
  160. // frames_to_deliver = frames_to_deliver > 4096 ? 4096 : frames_to_deliver;
  161. /* deliver the data */
  162. int delivered = playback_callback(frames_to_deliver);
  163. if (delivered != frames_to_deliver) {
  164. fprintf(stderr, "playback callback failed (delivered %i / %ld frames)\n", delivered, frames_to_deliver);
  165. // break;
  166. }
  167. }
  168. }
  169. snd_pcm_close(playback_handle);
  170. return NULL;
  171. }
  172. static bool initialized = false;
  173. void iron_a2_init() {
  174. if (initialized) {
  175. return;
  176. }
  177. iron_a2_internal_init();
  178. initialized = true;
  179. a2_buffer.read_location = 0;
  180. a2_buffer.write_location = 0;
  181. a2_buffer.data_size = 128 * 1024;
  182. a2_buffer.channel_count = 2;
  183. a2_buffer.channels[0] = (float *)malloc(a2_buffer.data_size * sizeof(float));
  184. a2_buffer.channels[1] = (float *)malloc(a2_buffer.data_size * sizeof(float));
  185. audioRunning = true;
  186. pthread_create(&threadid, NULL, &doAudio, NULL);
  187. }
  188. void iron_a2_update() {}
  189. void iron_a2_shutdown() {
  190. audioRunning = false;
  191. }
  192. #endif