test_async.c 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. /*
  2. * nanoalsa/test_async.c
  3. *
  4. * Copyright (C) 2023 bzt (bztsrc@gitlab) MIT license
  5. *
  6. * Permission is hereby granted, free of charge, to any person
  7. * obtaining a copy of this software and associated documentation
  8. * files (the "Software"), to deal in the Software without
  9. * restriction, including without limitation the rights to use, copy,
  10. * modify, merge, publish, distribute, sublicense, and/or sell copies
  11. * of the Software, and to permit persons to whom the Software is
  12. * furnished to do so, subject to the following conditions:
  13. *
  14. * The above copyright notice and this permission notice shall be
  15. * included in all copies or substantial portions of the Software.
  16. *
  17. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  18. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  19. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  20. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  21. * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  22. * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  23. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  24. * DEALINGS IN THE SOFTWARE.
  25. *
  26. * @brief Test playing PCM in the background
  27. * https://gitlab.com/bztsrc/nanoalsa
  28. */
  29. #define _POSIX_C_SOURCE 199309L /* needed for timespec */
  30. #include <stdio.h>
  31. /* all you need to do to enable async playback is to include pthread header */
  32. #include <pthread.h>
  33. #define ALSA_IMPLEMENTATION
  34. #include "nanoalsa.h"
  35. /* riff wave header */
  36. typedef struct {
  37. char str_riff[4]; /* "RIFF" */
  38. int wav_size; /* (file size) - 8 */
  39. char str_wave[4]; /* "WAVE" */
  40. /* Format Header */
  41. char str_fmt[4]; /* "fmt " */
  42. int fmt_chunk_size; /* Should be 16 for PCM */
  43. short audio_format; /* Should be 1 for PCM. 3 for IEEE Float */
  44. short channels; /* number of channels */
  45. int sample_rate; /* eg: 8000, 44100, 48000 */
  46. int byte_rate; /* Number of bytes per second. sample_rate * channels * Bytes Per Sample */
  47. short frame_size; /* channels * Bytes Per Sample */
  48. short bit_depth; /* bits per sample, eg: 8, 16, 24 */
  49. /* Data chunk */
  50. char str_data[4]; /* "data" */
  51. int data_bytes; /* (file size) - 44 */
  52. } wav_header_t;
  53. wav_header_t *wav;
  54. unsigned char *data, *end, *playback;
  55. /* helper to read file into memory */
  56. unsigned char* readfileall(char *file, int *size)
  57. {
  58. unsigned char *data = NULL;
  59. FILE *f;
  60. *size = 0;
  61. f = fopen(file, "rb");
  62. if(f){
  63. fseek(f, 0L, SEEK_END);
  64. *size = ftell(f);
  65. fseek(f, 0L, SEEK_SET);
  66. data = (unsigned char*)malloc(*size);
  67. if(!data) { fprintf(stderr, "unable to allocate memory\n"); exit(1); }
  68. fread(data, 1, *size, f);
  69. fclose(f);
  70. }
  71. return data;
  72. }
  73. /* textual error messages. nanoalsa just returns error codes */
  74. const char *alsa_err[] = {
  75. "ALSA_SUCCESS - ok",
  76. "ALSA_ERR_INP - bad function input arguments",
  77. "ALSA_ERR_DEV - no such device",
  78. "ALSA_ERR_HWPAR - ioctl hw parameter not supported",
  79. "ALSA_ERR_SWPAR - ioctl sw parameter not supported",
  80. "ALSA_ERR_MMAP - failed to mmap status or control registers",
  81. "ALSA_ERR_PREP - failed to prepare channel",
  82. "ALSA_ERR_THREAD - failed to create worker thread"
  83. };
  84. /**
  85. * The callback function. It's only job is to fill in the audio buffer
  86. */
  87. void callback(void *buf, int samplesize, int channels, int frames, void *priv)
  88. {
  89. int len = samplesize * channels * frames;
  90. /* we don't need private data. We could have placed the `playback` and `end` pointers there, but we just use globals */
  91. (void)priv;
  92. /* let's see if we have enough remaining samples. If not, then start over */
  93. if(playback + len >= end) len = end - playback;
  94. if(len < 1) { playback = data + sizeof(wav_header_t); len = samplesize * channels * frames; }
  95. /* fill in the buffer with PCM data and move pointer to the next batch */
  96. memcpy(buf, playback, len);
  97. playback += len;
  98. }
  99. /**
  100. * Main procedure, entry point
  101. */
  102. int main(int argc, char **argv)
  103. {
  104. alsa_t ctx;
  105. int ret, len = 0, fmt = SNDRV_PCM_FORMAT_LAST;
  106. /*** read in wav file ***/
  107. if(argc < 2) { fprintf(stderr, "./test_async <wav>\n"); exit(1); }
  108. data = readfileall(argv[1], &len);
  109. if(!data || len < (int)sizeof(wav_header_t)) { fprintf(stderr, "unable to read wav\n"); exit(1); }
  110. wav = (wav_header_t*)data;
  111. switch(wav->audio_format) {
  112. case 1:
  113. switch(wav->bit_depth) {
  114. case 8: fmt = SNDRV_PCM_FORMAT_U8; break;
  115. case 16: fmt = SNDRV_PCM_FORMAT_S16_LE; break;
  116. case 32: fmt = SNDRV_PCM_FORMAT_S32_LE; break;
  117. }
  118. break;
  119. case 3:
  120. switch(wav->bit_depth) {
  121. case 32: fmt = SNDRV_PCM_FORMAT_FLOAT_LE; break;
  122. case 64: fmt = SNDRV_PCM_FORMAT_FLOAT64_LE; break;
  123. }
  124. break;
  125. }
  126. printf("wav file: %s %u bits, %u channels, length %u frames\n",
  127. wav->audio_format == 3 ? "float" : "int", wav->bit_depth, wav->channels,
  128. wav->data_bytes / wav->frame_size);
  129. /*** open the PCM context ***/
  130. if((ret = alsa_open(&ctx, 0, 0, fmt, wav->sample_rate, wav->channels))) {
  131. fprintf(stderr, "unable to open sound card device: %s\n", alsa_err[ret]);
  132. exit(0);
  133. }
  134. /*** play PCM data ***/
  135. /* set pointers for out callback */
  136. playback = data + sizeof(wav_header_t); end = data + wav->data_bytes;
  137. /* specify the callback function and start playing in the background */
  138. alsa_start(&ctx, callback, NULL);
  139. /* do something in the foreground... */
  140. sleep(3);
  141. /* stop the background playback */
  142. alsa_stop(&ctx);
  143. /*** free resources ***/
  144. alsa_close(&ctx);
  145. free(data);
  146. return 0;
  147. }