chiark / gitweb /
protogen: The bulk of the eclient code generation.
[disorder] / lib / uaudio-thread.c
1 /*
2  * This file is part of DisOrder.
3  * Copyright (C) 2009 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 lib/uaudio-thread.c
19  * @brief Background thread for audio processing */
20 #include "common.h"
21
22 #include <pthread.h>
23 #include <unistd.h>
24
25 #include "uaudio.h"
26 #include "log.h"
27 #include "mem.h"
28
29 /** @brief Number of buffers
30  *
31  * Must be at least 2 and should normally be at least 3.  We maintain multiple
32  * buffers so that we can read new data into one while the previous is being
33  * played.
34  */
35 #define UAUDIO_THREAD_BUFFERS 4
36
37 /** @brief Buffer data structure */
38 struct uaudio_buffer {
39   /** @brief Pointer to sample data */
40   void *samples;
41
42   /** @brief Count of samples */
43   size_t nsamples;
44 };
45
46 /** @brief Input buffers
47  *
48  * This is actually a ring buffer, managed by @ref uaudio_collect_buffer and
49  * @ref uaudio_play_buffer.
50  *
51  * Initially both pointers are 0.  Whenever the pointers are equal, we
52  * interpreted this as meaning that there is no data stored at all.  A
53  * consequence of this is that maximal occupancy is when the collect point is
54  * just before the play point, so at least one buffer is always empty (hence it
55  * being good for @ref UAUDIO_THREAD_BUFFERS to be at least 3).
56  */
57 static struct uaudio_buffer uaudio_buffers[UAUDIO_THREAD_BUFFERS];
58
59 /** @brief Buffer to read into */
60 static unsigned uaudio_collect_buffer;
61
62 /** @brief Buffer to play from */
63 static unsigned uaudio_play_buffer;
64
65 /** @brief Collection thread ID */
66 static pthread_t uaudio_collect_thread;
67
68 /** @brief Playing thread ID */
69 static pthread_t uaudio_play_thread;
70
71 /** @brief Flags */
72 static unsigned uaudio_thread_flags;
73
74 static uaudio_callback *uaudio_thread_collect_callback;
75 static uaudio_playcallback *uaudio_thread_play_callback;
76 static void *uaudio_thread_userdata;
77 static int uaudio_thread_started;
78 static int uaudio_thread_collecting;
79 static pthread_mutex_t uaudio_thread_lock = PTHREAD_MUTEX_INITIALIZER;
80 static pthread_cond_t uaudio_thread_cond = PTHREAD_COND_INITIALIZER;
81
82 /** @brief Minimum number of samples per chunk */
83 static size_t uaudio_thread_min;
84
85 /** @brief Maximum number of samples per chunk */
86 static size_t uaudio_thread_max;
87
88 /** @brief Set when activated, clear when paused */
89 static int uaudio_thread_activated;
90
91 /** @brief Return number of buffers currently in use */
92 static int uaudio_buffers_used(void) {
93   return (uaudio_collect_buffer - uaudio_play_buffer) % UAUDIO_THREAD_BUFFERS;
94 }
95
96 /** @brief Background thread for audio collection
97  *
98  * Collects data while activated and communicates its status via @ref
99  * uaudio_thread_collecting.
100  */
101 static void *uaudio_collect_thread_fn(void attribute((unused)) *arg) {
102   pthread_mutex_lock(&uaudio_thread_lock);
103   while(uaudio_thread_started) {
104     /* Wait until we're activatd */
105     if(!uaudio_thread_activated) {
106       pthread_cond_wait(&uaudio_thread_cond, &uaudio_thread_lock);
107       continue;
108     }
109     /* We are definitely active now */
110     uaudio_thread_collecting = 1;
111     pthread_cond_broadcast(&uaudio_thread_cond);
112     while(uaudio_thread_activated) {
113       if(uaudio_buffers_used() < UAUDIO_THREAD_BUFFERS - 1) {
114         /* At least one buffer is available.  We release the lock while
115          * collecting data so that other already-filled buffers can be played
116          * without delay.  */
117         struct uaudio_buffer *const b = &uaudio_buffers[uaudio_collect_buffer];
118         pthread_mutex_unlock(&uaudio_thread_lock);
119         //fprintf(stderr, "C%d.", uaudio_collect_buffer);
120         
121         /* Keep on trying until we get the minimum required amount of data */
122         b->nsamples = 0;
123         if(uaudio_thread_activated) {
124           while(b->nsamples < uaudio_thread_min) {
125             b->nsamples += uaudio_thread_collect_callback
126               ((char *)b->samples
127                + b->nsamples * uaudio_sample_size,
128                uaudio_thread_max - b->nsamples,
129                uaudio_thread_userdata);
130           }
131         }
132         pthread_mutex_lock(&uaudio_thread_lock);
133         /* Advance to next buffer */
134         uaudio_collect_buffer = (1 + uaudio_collect_buffer) % UAUDIO_THREAD_BUFFERS;
135         /* Awaken player */
136         pthread_cond_broadcast(&uaudio_thread_cond);
137       } else
138         /* No space, wait for player */
139         pthread_cond_wait(&uaudio_thread_cond, &uaudio_thread_lock);
140     }
141     uaudio_thread_collecting = 0;
142     pthread_cond_broadcast(&uaudio_thread_cond);
143   }
144   pthread_mutex_unlock(&uaudio_thread_lock);
145   return NULL;
146 }
147
148 /** @brief Background thread for audio playing 
149  *
150  * This thread plays data as long as there is something to play.  So the
151  * buffers will drain to empty before deactivation completes.
152  */
153 static void *uaudio_play_thread_fn(void attribute((unused)) *arg) {
154   int resync = 1;
155   unsigned last_flags = 0;
156   unsigned char zero[uaudio_thread_max * uaudio_sample_size];
157   memset(zero, 0, sizeof zero);
158
159   while(uaudio_thread_started) {
160     // If we're paused then just play silence
161     if(!uaudio_thread_activated) {
162       pthread_mutex_unlock(&uaudio_thread_lock);
163       unsigned flags = UAUDIO_PAUSED;
164       if(last_flags & UAUDIO_PLAYING)
165         flags |= UAUDIO_PAUSE;
166       uaudio_thread_play_callback(zero, uaudio_thread_max,
167                                   last_flags = flags);
168       /* We expect the play callback to block for a reasonable period */
169       pthread_mutex_lock(&uaudio_thread_lock);
170       continue;
171     }
172     const int used = uaudio_buffers_used();
173     int go;
174
175     if(resync)
176       go = (used == UAUDIO_THREAD_BUFFERS - 1);
177     else
178       go = (used > 0);
179     if(go) {
180       /* At least one buffer is filled.  We release the lock while playing so
181        * that more collection can go on. */
182       struct uaudio_buffer *const b = &uaudio_buffers[uaudio_play_buffer];
183       pthread_mutex_unlock(&uaudio_thread_lock);
184       //fprintf(stderr, "P%d.", uaudio_play_buffer);
185       size_t played = 0;
186       while(played < b->nsamples) {
187         unsigned flags = UAUDIO_PLAYING;
188         if(last_flags & UAUDIO_PAUSED)
189           flags |= UAUDIO_RESUME;
190         played += uaudio_thread_play_callback((char *)b->samples
191                                               + played * uaudio_sample_size,
192                                               b->nsamples - played,
193                                               last_flags = flags);
194       }
195       pthread_mutex_lock(&uaudio_thread_lock);
196       /* Move to next buffer */
197       uaudio_play_buffer = (1 + uaudio_play_buffer) % UAUDIO_THREAD_BUFFERS;
198       /* Awaken collector */
199       pthread_cond_broadcast(&uaudio_thread_cond);
200       resync = 0;
201     } else {
202       /* Insufficient data to play, wait for collector */
203       pthread_cond_wait(&uaudio_thread_cond, &uaudio_thread_lock);
204       /* (Still) re-synchronizing */
205       resync = 1;
206     }
207   }
208   pthread_mutex_unlock(&uaudio_thread_lock);
209   return NULL;
210 }
211
212 /** @brief Create background threads for audio processing 
213  * @param callback Callback to collect audio data
214  * @param userdata Passed to @p callback
215  * @param playcallback Callback to play audio data
216  * @param min Minimum number of samples to play in a chunk
217  * @param max Maximum number of samples to play in a chunk
218  * @param flags Flags (not currently used)
219  *
220  * @p callback will be called multiple times in quick succession if necessary
221  * to gather at least @p min samples.  Equally @p playcallback may be called
222  * repeatedly in quick succession to play however much was received in a single
223  * chunk.
224  */
225 void uaudio_thread_start(uaudio_callback *callback,
226                          void *userdata,
227                          uaudio_playcallback *playcallback,
228                          size_t min,
229                          size_t max,
230                          unsigned flags) {
231   int e;
232   uaudio_thread_collect_callback = callback;
233   uaudio_thread_userdata = userdata;
234   uaudio_thread_play_callback = playcallback;
235   uaudio_thread_min = min;
236   uaudio_thread_max = max;
237   uaudio_thread_flags = flags;
238   uaudio_thread_started = 1;
239   uaudio_thread_activated = 0;
240   for(int n = 0; n < UAUDIO_THREAD_BUFFERS; ++n)
241     uaudio_buffers[n].samples = xcalloc_noptr(uaudio_thread_max,
242                                               uaudio_sample_size);
243   uaudio_collect_buffer = uaudio_play_buffer = 0;
244   if((e = pthread_create(&uaudio_collect_thread,
245                          NULL,
246                          uaudio_collect_thread_fn,
247                          NULL)))
248     disorder_fatal(e, "pthread_create");
249   if((e = pthread_create(&uaudio_play_thread,
250                          NULL,
251                          uaudio_play_thread_fn,
252                          NULL)))
253     disorder_fatal(e, "pthread_create");
254 }
255
256 /** @brief Shut down background threads for audio processing */
257 void uaudio_thread_stop(void) {
258   void *result;
259
260   pthread_mutex_lock(&uaudio_thread_lock);
261   uaudio_thread_activated = 0;
262   uaudio_thread_started = 0;
263   pthread_cond_broadcast(&uaudio_thread_cond);
264   pthread_mutex_unlock(&uaudio_thread_lock);
265   pthread_join(uaudio_collect_thread, &result);
266   pthread_join(uaudio_play_thread, &result);
267   for(int n = 0; n < UAUDIO_THREAD_BUFFERS; ++n)
268     xfree(uaudio_buffers[n].samples);
269 }
270
271 /** @brief Activate audio output */
272 void uaudio_thread_activate(void) {
273   pthread_mutex_lock(&uaudio_thread_lock);
274   uaudio_thread_activated = 1;
275   pthread_cond_broadcast(&uaudio_thread_cond);
276   pthread_mutex_unlock(&uaudio_thread_lock);
277 }
278
279 /** @brief Deactivate audio output */
280 void uaudio_thread_deactivate(void) {
281   pthread_mutex_lock(&uaudio_thread_lock);
282   uaudio_thread_activated = 0; 
283   pthread_cond_broadcast(&uaudio_thread_cond);
284   pthread_mutex_unlock(&uaudio_thread_lock);
285 }
286
287 /*
288 Local Variables:
289 c-basic-offset:2
290 comment-column:40
291 fill-column:79
292 indent-tabs-mode:nil
293 End:
294 */