Blob


1 /*
2 * Copyright (c) 2023 Omar Polo <op@omarpolo.com>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
17 #include "config.h"
19 #include <alsa/asoundlib.h>
21 #include <limits.h>
23 #include "amused.h"
24 #include "log.h"
26 static snd_pcm_t *pcm;
27 static size_t bpf;
28 static void (*onmove_cb)(void *, int);
30 int
31 audio_open(void (*cb)(void *, int))
32 {
33 const char *device = "default";
34 int err;
36 err = snd_pcm_open(&pcm, device, SND_PCM_STREAM_PLAYBACK,
37 SND_PCM_NONBLOCK);
38 if (err < 0) {
39 log_warnx("playback open error: %s", snd_strerror(err));
40 return -1;
41 }
43 onmove_cb = cb;
44 return 0;
45 }
47 int
48 audio_setup(unsigned int bits, unsigned int rate, unsigned int channels,
49 struct pollfd *pfds)
50 {
51 int err;
52 snd_pcm_format_t fmt;
54 if (bits == 8) {
55 fmt = SND_PCM_FORMAT_S8;
56 bpf = 1;
57 } else if (bits == 16) {
58 fmt = SND_PCM_FORMAT_S16;
59 bpf = 2;
60 } else if (bits == 24) {
61 fmt = SND_PCM_FORMAT_S24;
62 bpf = 4;
63 } else if (bits == 32) {
64 fmt = SND_PCM_FORMAT_S32;
65 bpf = 4;
66 } else {
67 log_warnx("can't handle %d bits", bits);
68 return -1;
69 }
71 bpf *= channels;
73 err = snd_pcm_set_params(pcm, fmt, SND_PCM_ACCESS_RW_INTERLEAVED,
74 channels, rate, 1, 500000 /* 0.5s */);
75 if (err < 0) {
76 log_warnx("invalid params: %s", snd_strerror(err));
77 return -1;
78 }
80 err = snd_pcm_prepare(pcm);
81 if (err < 0) {
82 log_warnx("snd_pcm_prepare failed: %s", snd_strerror(err));
83 return -1;
84 }
86 return 0;
87 }
89 int
90 audio_nfds(void)
91 {
92 return snd_pcm_poll_descriptors_count(pcm);
93 }
95 int
96 audio_pollfd(struct pollfd *pfds, int events)
97 {
98 return snd_pcm_poll_descriptors(pcm, pfds, audio_nfds());
99 }
101 int
102 audio_revents(struct pollfd *pfds)
104 int err;
105 unsigned short revents;
107 err = snd_pcm_poll_descriptors_revents(pcm, pfds, audio_nfds(),
108 &revents);
109 if (err < 0) {
110 log_warnx("snd revents failure: %s", snd_strerror(err));
111 return 0;
114 return revents;
117 size_t
118 audio_write(const void *buf, size_t len)
120 snd_pcm_sframes_t avail, ret;
122 /*
123 * snd_pcm_writei works in terms of FRAMES, not BYTES!
124 */
125 len /= bpf;
127 avail = snd_pcm_avail_update(pcm);
128 if (avail < 0) {
129 if (avail == -EPIPE) {
130 log_debug("alsa xrun occurred");
131 snd_pcm_recover(pcm, -EPIPE, 1);
132 return 0;
134 log_warnx("snd_pcm_avail_update failure: %s",
135 snd_strerror(avail));
136 return 0;
139 if (len > avail)
140 len = avail;
142 ret = snd_pcm_writei(pcm, buf, len);
143 if (ret < 0) {
144 log_warnx("snd_pcm_writei failed: %s", snd_strerror(ret));
145 return 0;
147 if (onmove_cb)
148 onmove_cb(NULL, ret);
149 return ret * bpf;
152 int
153 audio_flush(void)
155 int err;
157 err = snd_pcm_drop(pcm);
158 if (err < 0) {
159 log_warnx("snd_pcm_drop: %s", snd_strerror(err));
160 return -1;
163 return 0;
166 int
167 audio_stop(void)
169 int err;
171 err = snd_pcm_drain(pcm);
172 if (err < 0) {
173 log_warnx("snd_pcm_drain: %s", snd_strerror(err));
174 return -1;
177 return 0;