nanoalsa.h 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  1. /*
  2. * nanoalsa.h
  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 Simple and suckless PCM ALSA player library (replaces that buggy bloated asoundlib)
  27. * https://gitlab.com/bztsrc/nanoalsa
  28. */
  29. #ifndef _NANOALSA_H
  30. #define _NANOALSA_H
  31. #ifdef __cplusplus
  32. extern "C" {
  33. #endif
  34. /* we don't need asoundlib, we just use its header for the enums and structs, that's all */
  35. #include <sound/asound.h>
  36. /* error codes */
  37. enum {
  38. ALSA_SUCCESS,
  39. ALSA_ERR_INP,
  40. ALSA_ERR_DEV,
  41. ALSA_ERR_HWPAR,
  42. ALSA_ERR_SWPAR,
  43. ALSA_ERR_MMAP,
  44. ALSA_ERR_PREP,
  45. ALSA_ERR_THREAD
  46. };
  47. /* callback prototype */
  48. typedef void (*alsa_callback_t)(void *buf, int samplesize, int channels, int frames, void *data);
  49. /* main ALSA context */
  50. typedef struct {
  51. int fd, fmt, freq, chan;
  52. #ifndef ALSA_NORESAMPLE
  53. unsigned int reslen, needresample;
  54. short *resample;
  55. #endif
  56. unsigned int period_size, period_count, sample_size, boundary;
  57. struct snd_pcm_mmap_status *mmap_status;
  58. struct snd_pcm_mmap_control *mmap_control;
  59. #ifdef _PTHREAD_H
  60. alsa_callback_t cb;
  61. pthread_t th;
  62. void *abuf, *data;
  63. #endif
  64. } alsa_t;
  65. /* public API */
  66. int alsa_open(alsa_t *ctx, int card, int device, int fmt, int freq, int chan);
  67. int alsa_write(alsa_t *ctx, void *buf, unsigned int numframes);
  68. void alsa_close(alsa_t *ctx);
  69. #ifdef _PTHREAD_H
  70. int alsa_start(alsa_t *ctx, alsa_callback_t callback, void *data);
  71. int alsa_stop(alsa_t *ctx);
  72. #endif
  73. #ifdef ALSA_IMPLEMENTATION
  74. #ifndef ALSA_BUFFER_SIZE
  75. #define ALSA_BUFFER_SIZE 4096
  76. #endif
  77. #ifndef ALSA_PERIOD_SIZE
  78. #define ALSA_PERIOD_SIZE 1024
  79. #endif
  80. #include <stdio.h>
  81. #include <string.h>
  82. #include <fcntl.h>
  83. #include <unistd.h>
  84. #include <endian.h>
  85. #include <sys/mman.h>
  86. #include <sys/ioctl.h>
  87. #include <linux/ioctl.h>
  88. /* get host native signed 16 bit */
  89. #ifndef SNDRV_PCM_FORMAT_S16
  90. # if __BYTE_ORDER == __LITTLE_ENDIAN
  91. # define SNDRV_PCM_FORMAT_S16 SNDRV_PCM_FORMAT_S16_LE
  92. # else
  93. # define SNDRV_PCM_FORMAT_S16 SNDRV_PCM_FORMAT_S16_BE
  94. # endif
  95. #endif
  96. /* helpers for hw_params */
  97. static struct snd_interval *param_to_interval(struct snd_pcm_hw_params *p, int n)
  98. {
  99. return &(p->intervals[n - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]);
  100. }
  101. static struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p, int n)
  102. {
  103. return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]);
  104. }
  105. static void param_set_mask(struct snd_pcm_hw_params *p, int n, unsigned int bit)
  106. {
  107. struct snd_mask *m = param_to_mask(p, n);
  108. if (bit >= SNDRV_MASK_MAX) return;
  109. m->bits[0] = 0; m->bits[1] = 0; m->bits[bit >> 5] |= (1 << (bit & 31));
  110. }
  111. static void param_set_min(struct snd_pcm_hw_params *p, int n, unsigned int val)
  112. {
  113. struct snd_interval *i = param_to_interval(p, n);
  114. i->min = val;
  115. }
  116. static void param_set_int(struct snd_pcm_hw_params *p, int n, unsigned int val)
  117. {
  118. struct snd_interval *i = param_to_interval(p, n);
  119. i->min = val; i->max = val; i->integer = 1;
  120. }
  121. static unsigned int param_get_int(struct snd_pcm_hw_params *p, int n)
  122. {
  123. struct snd_interval *i = param_to_interval(p, n);
  124. return (i->integer) ? i->max : 0;
  125. }
  126. static void param_init(struct snd_pcm_hw_params *p)
  127. {
  128. struct snd_mask *m;
  129. struct snd_interval *i;
  130. int n;
  131. memset(p, 0, sizeof(struct snd_pcm_hw_params));
  132. for (n = SNDRV_PCM_HW_PARAM_FIRST_MASK; n <= SNDRV_PCM_HW_PARAM_LAST_MASK; n++) {
  133. m = param_to_mask(p, n); m->bits[0] = ~0; m->bits[1] = ~0;
  134. }
  135. for (n = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; n <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; n++) {
  136. i = param_to_interval(p, n); i->min = 0; i->max = ~0;
  137. }
  138. p->rmask = ~0U; p->cmask = 0; p->info = ~0U;
  139. }
  140. /**
  141. * Open the ALSA context
  142. */
  143. int alsa_open(alsa_t *ctx, int card, int device, int fmt, int freq, int chan)
  144. {
  145. char dev[64];
  146. struct snd_pcm_hw_params params;
  147. struct snd_pcm_sw_params spar;
  148. if(!ctx || card < 0 || device < 0 || freq < 1 || chan < 1) return ALSA_ERR_INP;
  149. memset(ctx, 0, sizeof(alsa_t));
  150. sprintf(dev, "/dev/snd/pcmC%uD%up", card, device);
  151. ctx->fd = open(dev, O_RDWR);
  152. if(ctx->fd < 1) { ctx->fd = -1; return ALSA_ERR_DEV; }
  153. ctx->fmt = fmt; ctx->chan = chan; ctx->freq = freq;
  154. param_init(&params);
  155. param_set_mask(&params, SNDRV_PCM_HW_PARAM_ACCESS, SNDRV_PCM_ACCESS_RW_INTERLEAVED);
  156. param_set_mask(&params, SNDRV_PCM_HW_PARAM_FORMAT, fmt);
  157. param_set_min(&params, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, ALSA_BUFFER_SIZE);
  158. param_set_min(&params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, ALSA_PERIOD_SIZE);
  159. param_set_int(&params, SNDRV_PCM_HW_PARAM_PERIODS, ALSA_BUFFER_SIZE / ALSA_PERIOD_SIZE);
  160. param_set_int(&params, SNDRV_PCM_HW_PARAM_RATE, freq);
  161. param_set_int(&params, SNDRV_PCM_HW_PARAM_CHANNELS, chan);
  162. switch(fmt) {
  163. case SNDRV_PCM_FORMAT_S8: case SNDRV_PCM_FORMAT_U8: case SNDRV_PCM_FORMAT_DSD_U8:
  164. case SNDRV_PCM_FORMAT_MU_LAW: case SNDRV_PCM_FORMAT_A_LAW: ctx->sample_size = 1; break;
  165. case SNDRV_PCM_FORMAT_S16_LE: case SNDRV_PCM_FORMAT_S16_BE:
  166. case SNDRV_PCM_FORMAT_U16_LE: case SNDRV_PCM_FORMAT_U16_BE:
  167. case SNDRV_PCM_FORMAT_DSD_U16_LE: case SNDRV_PCM_FORMAT_DSD_U16_BE: ctx->sample_size = 2; break;
  168. case SNDRV_PCM_FORMAT_S18_3LE: case SNDRV_PCM_FORMAT_S18_3BE:
  169. case SNDRV_PCM_FORMAT_U18_3LE: case SNDRV_PCM_FORMAT_U18_3BE:
  170. case SNDRV_PCM_FORMAT_S20_3LE: case SNDRV_PCM_FORMAT_S20_3BE:
  171. case SNDRV_PCM_FORMAT_U20_3LE: case SNDRV_PCM_FORMAT_U20_3BE:
  172. case SNDRV_PCM_FORMAT_S24_3LE: case SNDRV_PCM_FORMAT_S24_3BE:
  173. case SNDRV_PCM_FORMAT_U24_3LE: case SNDRV_PCM_FORMAT_U24_3BE: ctx->sample_size = 3; break;
  174. case SNDRV_PCM_FORMAT_S20_LE: case SNDRV_PCM_FORMAT_S20_BE:
  175. case SNDRV_PCM_FORMAT_U20_LE: case SNDRV_PCM_FORMAT_U20_BE:
  176. case SNDRV_PCM_FORMAT_S24_LE: case SNDRV_PCM_FORMAT_S24_BE:
  177. case SNDRV_PCM_FORMAT_U24_LE: case SNDRV_PCM_FORMAT_U24_BE:
  178. case SNDRV_PCM_FORMAT_S32_LE: case SNDRV_PCM_FORMAT_S32_BE:
  179. case SNDRV_PCM_FORMAT_U32_LE: case SNDRV_PCM_FORMAT_U32_BE:
  180. case SNDRV_PCM_FORMAT_FLOAT_LE: case SNDRV_PCM_FORMAT_FLOAT_BE:
  181. case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE: case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE:
  182. case SNDRV_PCM_FORMAT_DSD_U32_LE: case SNDRV_PCM_FORMAT_DSD_U32_BE: ctx->sample_size = 4; break;
  183. case SNDRV_PCM_FORMAT_FLOAT64_LE: case SNDRV_PCM_FORMAT_FLOAT64_BE: ctx->sample_size = 8; break;
  184. }
  185. if(!ctx->sample_size) { close(ctx->fd); ctx->fd = -1; return ALSA_ERR_HWPAR; }
  186. /* let's see if the sound card supports the requested format */
  187. if(ioctl(ctx->fd, SNDRV_PCM_IOCTL_HW_PARAMS, &params)) {
  188. #ifndef ALSA_NORESAMPLE
  189. /* if not, fallback to int16_t stereo (most commonly supported by hardware) and convert samples from software */
  190. if(fmt != SNDRV_PCM_FORMAT_S16 || chan != 2) {
  191. fmt = SNDRV_PCM_FORMAT_S16; param_set_mask(&params, SNDRV_PCM_HW_PARAM_FORMAT, fmt);
  192. chan = 2; param_set_int(&params, SNDRV_PCM_HW_PARAM_CHANNELS, chan);
  193. if(!ioctl(ctx->fd, SNDRV_PCM_IOCTL_HW_PARAMS, &params)) { ctx->needresample = 1; goto ok; }
  194. }
  195. #endif
  196. close(ctx->fd); ctx->fd = -1; return ALSA_ERR_HWPAR;
  197. }
  198. #ifndef ALSA_NORESAMPLE
  199. ok:
  200. #endif
  201. ctx->period_size = param_get_int(&params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
  202. ctx->period_count = param_get_int(&params, SNDRV_PCM_HW_PARAM_PERIODS);
  203. memset(&spar, 0, sizeof(spar));
  204. spar.tstamp_mode = SNDRV_PCM_TSTAMP_ENABLE;
  205. spar.period_step = 1;
  206. spar.avail_min = ctx->period_size;
  207. spar.start_threshold = ALSA_BUFFER_SIZE - ctx->period_size;
  208. spar.stop_threshold = ALSA_BUFFER_SIZE;
  209. spar.xfer_align = ctx->period_size / 2; /* for old kernels */
  210. if(ioctl(ctx->fd, SNDRV_PCM_IOCTL_SW_PARAMS, &spar)) { close(ctx->fd); ctx->fd = -1; return ALSA_ERR_SWPAR; }
  211. ctx->boundary = spar.boundary;
  212. ctx->mmap_status = mmap(NULL, 4096, PROT_READ, MAP_SHARED, ctx->fd, SNDRV_PCM_MMAP_OFFSET_STATUS);
  213. if(!ctx->mmap_status || ctx->mmap_status == MAP_FAILED) {
  214. ctx->mmap_status = NULL; close(ctx->fd); ctx->fd = -1; return ALSA_ERR_MMAP; }
  215. ctx->mmap_control = mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, ctx->fd, SNDRV_PCM_MMAP_OFFSET_CONTROL);
  216. if(!ctx->mmap_control || ctx->mmap_control == MAP_FAILED) {
  217. munmap(ctx->mmap_status, 4096); ctx->mmap_status = NULL; ctx->mmap_control = NULL; close(ctx->fd); ctx->fd = -1; return ALSA_ERR_MMAP; }
  218. if(ioctl(ctx->fd, SNDRV_PCM_IOCTL_PREPARE) < 0) {
  219. munmap(ctx->mmap_status, 4096); munmap(ctx->mmap_control, 4096); ctx->mmap_status = NULL; ctx->mmap_control = NULL;
  220. close(ctx->fd); ctx->fd = -1; return ALSA_ERR_PREP;
  221. }
  222. #ifdef _PTHREAD_H
  223. ctx->abuf = (void*)malloc(ctx->period_size * ctx->sample_size * ctx->chan);
  224. if(!ctx->abuf) {
  225. munmap(ctx->mmap_status, 4096); munmap(ctx->mmap_control, 4096); ctx->mmap_status = NULL; ctx->mmap_control = NULL;
  226. close(ctx->fd); ctx->fd = -1; return ALSA_ERR_THREAD;
  227. }
  228. #endif
  229. return ALSA_SUCCESS;
  230. }
  231. #ifndef ALSA_NORESAMPLE
  232. /**
  233. * Convert a sample in specified format to int16_t
  234. */
  235. static int alsa_conv(int fmt, unsigned char *src)
  236. {
  237. float f;
  238. int *i = (int*)&f;
  239. /* TODO: not all formats are handled yet */
  240. switch(fmt) {
  241. case SNDRV_PCM_FORMAT_S8: return (int)(char)src[0] * 256;
  242. case SNDRV_PCM_FORMAT_U8: return ((int)src[0] - 127) * 256;
  243. case SNDRV_PCM_FORMAT_S16_LE: return ((int)(char)src[1] << 8) | (int)src[0];
  244. case SNDRV_PCM_FORMAT_S16_BE: return ((int)(char)src[0] << 8) | (int)src[1];
  245. case SNDRV_PCM_FORMAT_U16_LE: return (((int)src[1] << 8) | (int)src[0]) - 32768;
  246. case SNDRV_PCM_FORMAT_U16_BE: return (((int)src[0] << 8) | (int)src[1]) - 32768;
  247. case SNDRV_PCM_FORMAT_S24_LE: return ((int)(char)src[2] << 8) | (int)src[1];
  248. case SNDRV_PCM_FORMAT_S24_BE: return ((int)(char)src[0] << 8) | (int)src[1];
  249. case SNDRV_PCM_FORMAT_U24_LE: return ((((int)src[2] << 16) | ((int)src[1] << 8) | (int)src[0]) - 8388608) / 256;
  250. case SNDRV_PCM_FORMAT_U24_BE: return ((((int)src[2] << 16) | ((int)src[1] << 8) | (int)src[0]) - 8388608) / 256;
  251. case SNDRV_PCM_FORMAT_S32_LE: return ((int)(char)src[3] << 8) | (int)src[2];
  252. case SNDRV_PCM_FORMAT_S32_BE: return ((int)(char)src[0] << 8) | (int)src[1];
  253. case SNDRV_PCM_FORMAT_U32_LE: return ((((int)src[3] << 24) | ((int)src[2] << 16) | ((int)src[1] << 8) | (int)src[0]) - 2147483648) / 32768;
  254. case SNDRV_PCM_FORMAT_U32_BE: return ((((int)src[0] << 24) | ((int)src[1] << 16) | ((int)src[2] << 8) | (int)src[3]) - 2147483648) / 32768;
  255. #if __BYTE_ORDER == __LITTLE_ENDIAN
  256. case SNDRV_PCM_FORMAT_FLOAT_LE: *i = (*(int*)src); return (int)((f < -1.0f ? -1.0f : (f > 1.0f ? 1.0f : f)) * 32767.0f);
  257. case SNDRV_PCM_FORMAT_FLOAT_BE: *i = (((int)src[0] << 24) | ((int)src[1] << 16) | ((int)src[2] << 8) | (int)src[3]);
  258. return (int)((f < -1.0f ? -1.0f : (f > 1.0f ? 1.0f : f)) * 32767.0f);
  259. #else
  260. case SNDRV_PCM_FORMAT_FLOAT_LE: *i = (((int)src[0] << 24) | ((int)src[1] << 16) | ((int)src[2] << 8) | (int)src[3]);
  261. return (int)((f < -1.0f ? -1.0f : (f > 1.0f ? 1.0f : f)) * 32767.0f);
  262. case SNDRV_PCM_FORMAT_FLOAT_BE: *i = (*(int*)src); return (int)((f < -1.0f ? -1.0f : (f > 1.0f ? 1.0f : f)) * 32767.0f);
  263. #endif
  264. }
  265. return 0;
  266. }
  267. #endif
  268. /**
  269. * Send PCM data directly to channel
  270. */
  271. int alsa_write(alsa_t *ctx, void *buf, unsigned int numframes)
  272. {
  273. unsigned char *data = (unsigned char*)buf;
  274. struct snd_xferi xfer = { 0 };
  275. int ret, avail, s = ctx->sample_size * ctx->chan;
  276. #ifndef ALSA_NORESAMPLE
  277. unsigned int i;
  278. int j, k;
  279. short *smp;
  280. #endif
  281. if(!ctx || !data || numframes < 1) return ALSA_ERR_INP;
  282. #ifndef ALSA_NORESAMPLE
  283. if(ctx->needresample) {
  284. if(!ctx->resample || ctx->reslen < numframes) {
  285. ctx->reslen = numframes;
  286. ctx->resample = (short*)realloc(ctx->resample, numframes * 4);
  287. if(!ctx->resample) { ctx->reslen = 0; return ALSA_ERR_INP; }
  288. }
  289. for(smp = ctx->resample, i = 0; i < numframes; i++, data += s, smp += 2)
  290. switch(ctx->chan) {
  291. case 1: /* mono -> stereo */
  292. smp[0] = smp[1] = alsa_conv(ctx->fmt, data);
  293. break;
  294. case 2: /* stereo -> stereo */
  295. smp[0] = alsa_conv(ctx->fmt, data);
  296. smp[1] = alsa_conv(ctx->fmt, data + ctx->sample_size);
  297. break;
  298. case 6: /* dolby 5.1 -> stereo */
  299. smp[0] = (alsa_conv(ctx->fmt, data) + alsa_conv(ctx->fmt, data + 2 * ctx->sample_size) +
  300. + alsa_conv(ctx->fmt, data + 3 * ctx->sample_size) + alsa_conv(ctx->fmt, data + 5 * ctx->sample_size)) / 4;
  301. smp[1] = (alsa_conv(ctx->fmt, data + ctx->sample_size) + alsa_conv(ctx->fmt, data + 2 * ctx->sample_size) +
  302. + alsa_conv(ctx->fmt, data + 4 * ctx->sample_size) + alsa_conv(ctx->fmt, data + 5 * ctx->sample_size)) / 4;
  303. break;
  304. default: /* anything else -> mono -> stereo */
  305. for(k = j = 0; j < ctx->chan; j++)
  306. k += alsa_conv(ctx->fmt, data + j * ctx->sample_size);
  307. smp[0] = smp[1] = k / ctx->chan;
  308. break;
  309. }
  310. s = 4; data = (unsigned char*)ctx->resample;
  311. }
  312. #endif
  313. do {
  314. xfer.buf = data;
  315. xfer.frames = numframes > ctx->period_size ? ctx->period_size : numframes;
  316. xfer.result = 0;
  317. if(!(ret = ioctl(ctx->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &xfer))) {
  318. avail = ctx->mmap_status->hw_ptr + ALSA_BUFFER_SIZE - ctx->mmap_control->appl_ptr;
  319. if(avail < 0) avail += ctx->boundary; else
  320. if((unsigned int)avail >= ctx->boundary) avail -= ctx->boundary;
  321. numframes -= xfer.result;
  322. data += xfer.result * s;
  323. } else return ret;
  324. } while(numframes > 0);
  325. return ALSA_SUCCESS;
  326. }
  327. /**
  328. * Close the ALSA context
  329. */
  330. void alsa_close(alsa_t *ctx)
  331. {
  332. if(!ctx) return;
  333. #ifdef _PTHREAD_H
  334. alsa_stop(ctx);
  335. if(ctx->abuf) { free(ctx->abuf); ctx->abuf = NULL; }
  336. #endif
  337. #ifndef ALSA_NORESAMPLE
  338. if(ctx->resample) { free(ctx->resample); ctx->resample = NULL; }
  339. #endif
  340. if(ctx->mmap_status) munmap(ctx->mmap_status, 4096);
  341. if(ctx->mmap_control) munmap(ctx->mmap_control, 4096);
  342. if(ctx->fd > 0) {
  343. ioctl(ctx->fd, SNDRV_PCM_IOCTL_DRAIN);
  344. close(ctx->fd);
  345. }
  346. memset(ctx, 0, sizeof(alsa_t));
  347. }
  348. #ifdef _PTHREAD_H
  349. /**
  350. * Worker thread
  351. */
  352. static void *alsa_worker(void *data)
  353. {
  354. alsa_t *ctx = (alsa_t*)data;
  355. while(ctx->period_size) {
  356. (*ctx->cb)(ctx->abuf, ctx->sample_size, ctx->chan, ctx->period_size, ctx->data);
  357. alsa_write(ctx, ctx->abuf, ctx->period_size);
  358. }
  359. return NULL;
  360. }
  361. /**
  362. * Start channel
  363. */
  364. int alsa_start(alsa_t *ctx, alsa_callback_t callback, void *data)
  365. {
  366. if(!ctx || ctx->fd < 1 || !ctx->abuf || ctx->th || !callback) return ALSA_ERR_INP;
  367. ctx->cb = callback; ctx->data = data;
  368. return pthread_create(&ctx->th, NULL, alsa_worker, ctx) ? ALSA_ERR_THREAD : ALSA_SUCCESS;
  369. }
  370. /**
  371. * Stop channel
  372. */
  373. int alsa_stop(alsa_t *ctx)
  374. {
  375. if(!ctx) return ALSA_ERR_INP;
  376. ctx->period_size = 0;
  377. if(ctx->th) {
  378. pthread_cancel(ctx->th);
  379. pthread_join(ctx->th, NULL);
  380. ctx->th = 0;
  381. }
  382. if(ctx->fd > 0) ioctl(ctx->fd, SNDRV_PCM_IOCTL_DRAIN);
  383. return ALSA_SUCCESS;
  384. }
  385. #endif
  386. #endif /* ALSA_IMPLEMENTATION */
  387. #ifdef __cplusplus
  388. }
  389. #endif
  390. #endif /* _NANOALSA_H */