chiark / gitweb /
Merge Core Audio fixes
[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  * 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.
26  */
27
28 #include "common.h"
29
30 #if HAVE_ALSA_ASOUNDLIB_H
31 #include <alsa/asoundlib.h>
32 #include <pthread.h>
33
34 #include "alsabg.h"
35 #include "log.h"
36
37 /** @brief Output handle */
38 static snd_pcm_t *pcm;
39
40 /** @brief Called to get audio data */
41 static alsa_bg_supply *supplyfn;
42
43 static pthread_t alsa_bg_collect_tid, alsa_bg_play_tid;
44
45 /** @brief Set to shut down the background threads */
46 static 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 */
67 static uint8_t alsa_bg_buffer[BUFFER_BYTES];
68
69 /** @brief First playable byte in audio buffer */
70 static unsigned alsa_bg_start;
71
72 /** @brief Number of playable bytes in audio buffer */
73 static unsigned alsa_bg_count;
74
75 /** @brief Current enable status */
76 static int alsa_bg_enabled;
77
78 /** @brief Lock protecting audio buffer pointers */
79 static pthread_mutex_t alsa_bg_lock = PTHREAD_MUTEX_INITIALIZER;
80
81 /** @brief Signaled when buffer contents changes */
82 static 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  */
97 static 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  */
139 static 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) {
184       switch(rframes) {
185       case -EPIPE:
186         error(0, "underrun detected");
187         if((err = snd_pcm_prepare(pcm)))
188           fatal(0, "snd_pcm_prepare: %d", err);
189         break;
190       default:
191         fatal(0, "snd_pcm_writei: %d", rframes);
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 */
212 void 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 */
220 void 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  */
237 void 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
282 /** @brief Deinitialize background ALSA playback
283  *
284  * The opposite of alsa_bg_init().
285  */
286 void 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 /*
306 Local Variables:
307 c-basic-offset:2
308 comment-column:40
309 fill-column:79
310 indent-tabs-mode:nil
311 End:
312 */