chiark / gitweb /
336c2a3320adb6c77699d045ed89759744aa4131
[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 "common.h"
25
26 #if HAVE_ALSA_ASOUNDLIB_H
27 #include <alsa/asoundlib.h>
28 #include <pthread.h>
29
30 #include "alsabg.h"
31 #include "log.h"
32
33 /** @brief Output handle */
34 static snd_pcm_t *pcm;
35
36 /** @brief Called to get audio data */
37 static alsa_bg_supply *supplyfn;
38
39 static pthread_t alsa_bg_collect_tid, alsa_bg_play_tid;
40
41 /** @brief Set to shut down the background threads */
42 static 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 */
63 static uint8_t alsa_bg_buffer[BUFFER_BYTES];
64
65 /** @brief First playable byte in audio buffer */
66 static unsigned alsa_bg_start;
67
68 /** @brief Number of playable bytes in audio buffer */
69 static unsigned alsa_bg_count;
70
71 /** @brief Current enable status */
72 static int alsa_bg_enabled;
73
74 /** @brief Lock protecting audio buffer pointers */
75 static pthread_mutex_t alsa_bg_lock = PTHREAD_MUTEX_INITIALIZER;
76
77 /** @brief Signaled when buffer contents changes */
78 static 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  */
93 static 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  */
135 static 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) {
180       switch(rframes) {
181       case -EPIPE:
182         error(0, "underrun detected");
183         if((err = snd_pcm_prepare(pcm)))
184           fatal(0, "snd_pcm_prepare: %d", err);
185         break;
186       default:
187         fatal(0, "snd_pcm_writei: %d", rframes);
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 */
208 void 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 */
216 void 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  */
233 void 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
278 void 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 /*
298 Local Variables:
299 c-basic-offset:2
300 comment-column:40
301 fill-column:79
302 indent-tabs-mode:nil
303 End:
304 */