chiark / gitweb /
Abolish uaudio_apis[]. Instead, define UAUDIO_DEFAULT to indicate
[disorder] / lib / alsabg.c
CommitLineData
4dadf1a2
RK
1/*
2 * This file is part of DisOrder.
3 * Copyright (C) 2008 Richard Kettlewell
4 *
e7eb3a27 5 * This program is free software: you can redistribute it and/or modify
4dadf1a2 6 * it under the terms of the GNU General Public License as published by
e7eb3a27 7 * the Free Software Foundation, either version 3 of the License, or
4dadf1a2 8 * (at your option) any later version.
e7eb3a27
RK
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
4dadf1a2 15 * You should have received a copy of the GNU General Public License
e7eb3a27 16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
4dadf1a2
RK
17 */
18/** @file alsabg.c
19 * @brief Background-thread interface to ALSA
4d82d579
RK
20 *
21 * This wraps ALSA with an interface which calls back to the client from a
22 * thread. It's not intended for completely general use, just what DisOrder
23 * needs.
24 *
25 * Only builds on Linux systems.
4dadf1a2
RK
26 */
27
05b75f8d 28#include "common.h"
4dadf1a2
RK
29
30#if HAVE_ALSA_ASOUNDLIB_H
4dadf1a2
RK
31#include <alsa/asoundlib.h>
32#include <pthread.h>
33
34#include "alsabg.h"
35#include "log.h"
36
37/** @brief Output handle */
38static snd_pcm_t *pcm;
39
40/** @brief Called to get audio data */
41static alsa_bg_supply *supplyfn;
42
43static pthread_t alsa_bg_collect_tid, alsa_bg_play_tid;
44
45/** @brief Set to shut down the background threads */
46static int alsa_bg_shutdown = 0;
47
48/** @brief Number of channels (samples per frame) */
49#define CHANNELS 2
50
51/** @brief Number of bytes per samples */
52#define BYTES_PER_SAMPLE 2
53
54/** @brief Number of bytes per frame */
55#define BYTES_PER_FRAME (CHANNELS * BYTES_PER_SAMPLE)
56
57/** @brief Buffer size in bytes */
58#define BUFFER_BYTES 65536
59
60/** @brief Buffer size in frames */
61#define BUFFER_FRAMES (BUFFER_BYTES / BYTES_PER_FRAME)
62
63/** @brief Buffer size in samples */
64#define BUFFER_SAMPLES (BUFFER_BYTES / BYTES_PER_SAMPLE)
65
66/** @brief Audio buffer */
67static uint8_t alsa_bg_buffer[BUFFER_BYTES];
68
69/** @brief First playable byte in audio buffer */
70static unsigned alsa_bg_start;
71
72/** @brief Number of playable bytes in audio buffer */
73static unsigned alsa_bg_count;
74
75/** @brief Current enable status */
76static int alsa_bg_enabled;
77
78/** @brief Lock protecting audio buffer pointers */
79static pthread_mutex_t alsa_bg_lock = PTHREAD_MUTEX_INITIALIZER;
80
81/** @brief Signaled when buffer contents changes */
82static pthread_cond_t alsa_bg_cond = PTHREAD_COND_INITIALIZER;
83
84/** @brief Call a pthread_ function and fatal() on exit */
85#define ep(x) do { \
86 int prc; \
87 \
88 if((prc = (x))) \
89 fatal(prc, "%s", #x); \
90} while(0)
91
92/** @brief Data collection thread
93 *
94 * This thread collects audio data to play and stores it in the ring
95 * buffer.
96 */
97static void *alsa_bg_collect(void attribute((unused)) *arg) {
98 unsigned avail_start, avail_count;
99 int count;
100
101 ep(pthread_mutex_lock(&alsa_bg_lock));
102 for(;;) {
103 /* If we're shutting down, quit straight away */
104 if(alsa_bg_shutdown)
105 break;
106 /* While we're disabled or the buffer is full, just wait */
107 if(!alsa_bg_enabled || alsa_bg_count == BUFFER_BYTES) {
108 ep(pthread_cond_wait(&alsa_bg_cond, &alsa_bg_lock));
109 continue;
110 }
111 /* Figure out where and how big the gap we can write into is */
112 avail_start = alsa_bg_start + alsa_bg_count;
113 if(avail_start < BUFFER_BYTES)
114 avail_count = BUFFER_BYTES - avail_start;
115 else {
116 avail_start %= BUFFER_BYTES;
117 avail_count = alsa_bg_start - avail_start;
118 }
119 assert(avail_start < BUFFER_BYTES);
120 assert(avail_count <= BUFFER_BYTES);
121 assert(avail_count + alsa_bg_count <= BUFFER_BYTES);
122 ep(pthread_mutex_unlock(&alsa_bg_lock));
123 count = supplyfn(alsa_bg_buffer + avail_start,
124 avail_count / BYTES_PER_SAMPLE);
125 ep(pthread_mutex_lock(&alsa_bg_lock));
126 alsa_bg_count += count * BYTES_PER_SAMPLE;
127 assert(alsa_bg_start < BUFFER_BYTES);
128 assert(alsa_bg_count <= BUFFER_BYTES);
129 ep(pthread_cond_signal(&alsa_bg_cond));
130 }
131 ep(pthread_mutex_unlock(&alsa_bg_lock));
132 return 0;
133}
134
135/** @brief Playback thread
136 *
137 * This thread reads audio data out of the ring buffer and plays it back
138 */
139static void *alsa_bg_play(void attribute((unused)) *arg) {
140 int prepared = 1, err;
141 int start, nbytes, nframes, rframes;
142
143 ep(pthread_mutex_lock(&alsa_bg_lock));
144 for(;;) {
145 /* If we're shutting down, quit straight away */
146 if(alsa_bg_shutdown)
147 break;
148 /* Wait for some data to be available. (If we are marked disabled
149 * we keep on playing what we've got.) */
150 if(alsa_bg_count == 0) {
151 if(prepared) {
152 if((err = snd_pcm_drain(pcm)))
153 fatal(0, "snd_pcm_drain: %d", err);
154 prepared = 0;
155 }
156 ep(pthread_cond_wait(&alsa_bg_cond, &alsa_bg_lock));
157 continue;
158 }
159 /* Calculate how much we can play */
160 start = alsa_bg_start;
161 if(start + alsa_bg_count <= BUFFER_BYTES)
162 nbytes = alsa_bg_count;
163 else
164 nbytes = BUFFER_BYTES - start;
165 /* Limit how much of the buffer we play. The effect is that we return from
166 * _writei earlier, and therefore free up more buffer space to read fresh
167 * data into. /2 works fine, /4 is just conservative. /1 (i.e. abolishing
168 * the heuristic) produces noticably noisy output. */
169 if(nbytes > BUFFER_BYTES / 4)
170 nbytes = BUFFER_BYTES / 4;
171 assert((unsigned)nbytes <= alsa_bg_count);
172 nframes = nbytes / BYTES_PER_FRAME;
173 ep(pthread_mutex_unlock(&alsa_bg_lock));
174 /* Make sure the PCM is prepared */
175 if(!prepared) {
176 if((err = snd_pcm_prepare(pcm)))
177 fatal(0, "snd_pcm_prepare: %d", err);
178 prepared = 1;
179 }
180 /* Play what we can */
181 rframes = snd_pcm_writei(pcm, alsa_bg_buffer + start, nframes);
182 ep(pthread_mutex_lock(&alsa_bg_lock));
183 if(rframes < 0) {
4dadf1a2
RK
184 switch(rframes) {
185 case -EPIPE:
2956aeb4
RK
186 error(0, "underrun detected");
187 if((err = snd_pcm_prepare(pcm)))
188 fatal(0, "snd_pcm_prepare: %d", err);
4dadf1a2 189 break;
2956aeb4
RK
190 default:
191 fatal(0, "snd_pcm_writei: %d", rframes);
4dadf1a2
RK
192 }
193 } else {
194 const int rbytes = rframes * BYTES_PER_FRAME;
195 /*fprintf(stderr, "%5d -> %5d\n", nbytes, rbytes);*/
196 /* Update the buffer pointers */
197 alsa_bg_count -= rbytes;
198 alsa_bg_start += rbytes;
199 if(alsa_bg_start >= BUFFER_BYTES)
200 alsa_bg_start -= BUFFER_BYTES;
201 assert(alsa_bg_start < BUFFER_BYTES);
202 assert(alsa_bg_count <= BUFFER_BYTES);
203 /* Let the collector know we've opened up some space */
204 ep(pthread_cond_signal(&alsa_bg_cond));
205 }
206 }
207 ep(pthread_mutex_unlock(&alsa_bg_lock));
208 return 0;
209}
210
211/** @brief Enable ALSA play */
212void alsa_bg_enable(void) {
213 ep(pthread_mutex_lock(&alsa_bg_lock));
214 alsa_bg_enabled = 1;
215 ep(pthread_cond_broadcast(&alsa_bg_cond));
216 ep(pthread_mutex_unlock(&alsa_bg_lock));
217}
218
219/** @brief Disable ALSA play */
220void alsa_bg_disable(void) {
221 ep(pthread_mutex_lock(&alsa_bg_lock));
222 alsa_bg_enabled = 0;
223 ep(pthread_cond_broadcast(&alsa_bg_cond));
224 ep(pthread_mutex_unlock(&alsa_bg_lock));
225}
226
227/** @brief Initialize background ALSA playback
228 * @param device Target device or NULL to use default
229 * @param supply Function to call to get audio data to play
230 *
231 * Playback is not initially enabled; see alsa_bg_enable(). When playback is
232 * enabled, @p supply will be called in a background thread to request audio
233 * data. It should return in a timely manner, but playback happens from a
234 * further thread and delays in @p supply will not delay transfer of data to
235 * the sound device (provided it doesn't actually run out).
236 */
237void alsa_bg_init(const char *device,
238 alsa_bg_supply *supply) {
239 snd_pcm_hw_params_t *hwparams;
240 /* Only support one format for now */
241 const int sample_format = SND_PCM_FORMAT_S16_BE;
242 unsigned rate = 44100;
243 int err;
244
245 if((err = snd_pcm_open(&pcm,
246 device ? device : "default",
247 SND_PCM_STREAM_PLAYBACK,
248 0)))
249 fatal(0, "error from snd_pcm_open: %d", err);
250 /* Set up 'hardware' parameters */
251 snd_pcm_hw_params_alloca(&hwparams);
252 if((err = snd_pcm_hw_params_any(pcm, hwparams)) < 0)
253 fatal(0, "error from snd_pcm_hw_params_any: %d", err);
254 if((err = snd_pcm_hw_params_set_access(pcm, hwparams,
255 SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
256 fatal(0, "error from snd_pcm_hw_params_set_access: %d", err);
257 if((err = snd_pcm_hw_params_set_format(pcm, hwparams,
258 sample_format)) < 0)
259
260 fatal(0, "error from snd_pcm_hw_params_set_format (%d): %d",
261 sample_format, err);
262 if((err = snd_pcm_hw_params_set_rate_near(pcm, hwparams, &rate, 0)) < 0)
263 fatal(0, "error from snd_pcm_hw_params_set_rate (%d): %d",
264 rate, err);
265 if((err = snd_pcm_hw_params_set_channels(pcm, hwparams,
266 CHANNELS)) < 0)
267 fatal(0, "error from snd_pcm_hw_params_set_channels (%d): %d",
268 CHANNELS, err);
269 if((err = snd_pcm_hw_params(pcm, hwparams)) < 0)
270 fatal(0, "error calling snd_pcm_hw_params: %d", err);
271
272 /* Record the audio supply function */
273 supplyfn = supply;
274
275 /* Create the audio output thread */
276 alsa_bg_shutdown = 0;
277 alsa_bg_enabled = 0;
278 ep(pthread_create(&alsa_bg_collect_tid, 0, alsa_bg_collect, 0));
279 ep(pthread_create(&alsa_bg_play_tid, 0, alsa_bg_play, 0));
280}
281
4d82d579
RK
282/** @brief Deinitialize background ALSA playback
283 *
284 * The opposite of alsa_bg_init().
285 */
4dadf1a2
RK
286void alsa_bg_close(void) {
287 void *r;
288
289 /* Notify background threads that we're shutting down */
290 ep(pthread_mutex_lock(&alsa_bg_lock));
291 alsa_bg_enabled = 0;
292 alsa_bg_shutdown = 1;
293 ep(pthread_cond_signal(&alsa_bg_cond));
294 ep(pthread_mutex_unlock(&alsa_bg_lock));
295 /* Join background threads when they're done */
296 ep(pthread_join(alsa_bg_collect_tid, &r));
297 ep(pthread_join(alsa_bg_play_tid, &r));
298 /* Close audio device */
299 snd_pcm_close(pcm);
300 pcm = 0;
301}
302
303#endif
304
305/*
306Local Variables:
307c-basic-offset:2
308comment-column:40
309fill-column:79
310indent-tabs-mode:nil
311End:
312*/