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