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;
29 int
30 audio_open(void (*onmove_cb)(void *, int))
31 {
32 const char *device = "default";
33 int err;
35 err = snd_pcm_open(&pcm, device, SND_PCM_STREAM_PLAYBACK,
36 SND_PCM_NONBLOCK);
37 if (err < 0) {
38 log_warnx("playback open error: %s", snd_strerror(err));
39 return -1;
40 }
42 /* TODO: set up onmove callback? */
43 return 0;
44 }
46 int
47 audio_setup(unsigned int bits, unsigned int rate, unsigned int channels,
48 struct pollfd *pfds)
49 {
50 int err;
51 snd_pcm_format_t fmt;
53 if (bits == 8) {
54 fmt = SND_PCM_FORMAT_S8;
55 bpf = 1;
56 } else if (bits == 16) {
57 fmt = SND_PCM_FORMAT_S16;
58 bpf = 2;
59 } else if (bits == 24) {
60 fmt = SND_PCM_FORMAT_S24;
61 bpf = 4;
62 } else if (bits == 32) {
63 fmt = SND_PCM_FORMAT_S32;
64 bpf = 4;
65 } else {
66 log_warnx("can't handle %d bits", bits);
67 return -1;
68 }
70 bpf *= channels;
72 err = snd_pcm_set_params(pcm, fmt, SND_PCM_ACCESS_RW_INTERLEAVED,
73 channels, rate, 1, 500000 /* 0.5s */);
74 if (err < 0) {
75 log_warnx("invalid params: %s", snd_strerror(err));
76 return -1;
77 }
79 err = snd_pcm_prepare(pcm);
80 if (err < 0) {
81 log_warnx("snd_pcm_prepare failed: %s", snd_strerror(err));
82 return -1;
83 }
85 return 0;
86 }
88 int
89 audio_nfds(void)
90 {
91 return snd_pcm_poll_descriptors_count(pcm);
92 }
94 int
95 audio_pollfd(struct pollfd *pfds, int events)
96 {
97 return snd_pcm_poll_descriptors(pcm, pfds, audio_nfds());
98 }
100 int
101 audio_revents(struct pollfd *pfds)
103 int err;
104 unsigned short revents;
106 err = snd_pcm_poll_descriptors_revents(pcm, pfds, audio_nfds(),
107 &revents);
108 if (err < 0) {
109 log_warnx("snd revents failure: %s", snd_strerror(err));
110 return 0;
113 return revents;
116 size_t
117 audio_write(const void *buf, size_t len)
119 snd_pcm_sframes_t avail, ret;
121 /*
122 * snd_pcm_writei works in terms of FRAMES, not BYTES!
123 */
124 len /= bpf;
126 avail = snd_pcm_avail_update(pcm);
127 if (avail < 0) {
128 if (avail == -EPIPE) {
129 log_debug("alsa xrun occurred");
130 snd_pcm_recover(pcm, -EPIPE, 1);
131 return 0;
133 log_warnx("snd_pcm_avail_update failure: %s",
134 snd_strerror(avail));
135 return 0;
138 if (len > avail)
139 len = avail;
141 ret = snd_pcm_writei(pcm, buf, len);
142 if (ret < 0) {
143 log_warnx("snd_pcm_writei failed: %s", snd_strerror(ret));
144 return 0;
146 return ret * bpf;
149 int
150 audio_flush(void)
152 int err;
154 err = snd_pcm_drop(pcm);
155 if (err < 0) {
156 log_warnx("snd_pcm_drop: %s", snd_strerror(err));
157 return -1;
160 return 0;
163 int
164 audio_stop(void)
166 int err;
168 err = snd_pcm_drain(pcm);
169 if (err < 0) {
170 log_warnx("snd_pcm_drain: %s", snd_strerror(err));
171 return -1;
174 return 0;