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