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