chiark / gitweb /
Merge Unicode 5.1.0 support.
[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_activated;
79 static int uaudio_thread_collecting;
80 static pthread_mutex_t uaudio_thread_lock = PTHREAD_MUTEX_INITIALIZER;
81 static pthread_cond_t uaudio_thread_cond = PTHREAD_COND_INITIALIZER;
82
83 /** @brief Minimum number of samples per chunk */
84 static size_t uaudio_thread_min;
85
86 /** @brief Maximum number of samples per chunk */
87 static size_t uaudio_thread_max;
88
89 /** @brief Return number of buffers currently in use */
90 static int uaudio_buffers_used(void) {
91   return (uaudio_collect_buffer - uaudio_play_buffer) % UAUDIO_THREAD_BUFFERS;
92 }
93
94 /** @brief Background thread for audio collection
95  *
96  * Collects data while activated and communicates its status via @ref
97  * uaudio_thread_collecting.
98  */
99 static void *uaudio_collect_thread_fn(void attribute((unused)) *arg) {
100   pthread_mutex_lock(&uaudio_thread_lock);
101   while(uaudio_thread_started) {
102     /* Wait until we're activatd */
103     if(!uaudio_thread_activated) {
104       pthread_cond_wait(&uaudio_thread_cond, &uaudio_thread_lock);
105       continue;
106     }
107     /* We are definitely active now */
108     uaudio_thread_collecting = 1;
109     pthread_cond_broadcast(&uaudio_thread_cond);
110     while(uaudio_thread_activated
111           || (uaudio_thread_flags & UAUDIO_THREAD_FAKE_PAUSE)) {
112       if(uaudio_buffers_used() < UAUDIO_THREAD_BUFFERS - 1) {
113         /* At least one buffer is available.  We release the lock while
114          * collecting data so that other already-filled buffers can be played
115          * without delay.  */
116         struct uaudio_buffer *const b = &uaudio_buffers[uaudio_collect_buffer];
117         pthread_mutex_unlock(&uaudio_thread_lock);
118         //fprintf(stderr, "C%d.", uaudio_collect_buffer);
119         
120         /* Keep on trying until we get the minimum required amount of data */
121         b->nsamples = 0;
122         if(uaudio_thread_activated) {
123           while(b->nsamples < uaudio_thread_min) {
124             b->nsamples += uaudio_thread_collect_callback
125               ((char *)b->samples
126                + b->nsamples * uaudio_sample_size,
127                uaudio_thread_max - b->nsamples,
128                uaudio_thread_userdata);
129           }
130         } else if(uaudio_thread_flags & UAUDIO_THREAD_FAKE_PAUSE) {
131           memset(b->samples, 0, uaudio_thread_min * uaudio_sample_size);
132           b->nsamples += uaudio_thread_min;
133         }
134         pthread_mutex_lock(&uaudio_thread_lock);
135         /* Advance to next buffer */
136         uaudio_collect_buffer = (1 + uaudio_collect_buffer) % UAUDIO_THREAD_BUFFERS;
137         /* Awaken player */
138         pthread_cond_broadcast(&uaudio_thread_cond);
139       } else
140         /* No space, wait for player */
141         pthread_cond_wait(&uaudio_thread_cond, &uaudio_thread_lock);
142     }
143     uaudio_thread_collecting = 0;
144     pthread_cond_broadcast(&uaudio_thread_cond);
145   }
146   pthread_mutex_unlock(&uaudio_thread_lock);
147   return NULL;
148 }
149
150 /** @brief Background thread for audio playing 
151  *
152  * This thread plays data as long as there is something to play.  So the
153  * buffers will drain to empty before deactivation completes.
154  */
155 static void *uaudio_play_thread_fn(void attribute((unused)) *arg) {
156   int resync = 1;
157
158   pthread_mutex_lock(&uaudio_thread_lock);
159   while(uaudio_thread_started) {
160     const int used = uaudio_buffers_used();
161     int go;
162
163     if(resync)
164       go = (used == UAUDIO_THREAD_BUFFERS - 1);
165     else
166       go = (used > 0);
167     if(go) {
168       /* At least one buffer is filled.  We release the lock while playing so
169        * that more collection can go on. */
170       struct uaudio_buffer *const b = &uaudio_buffers[uaudio_play_buffer];
171       pthread_mutex_unlock(&uaudio_thread_lock);
172       //fprintf(stderr, "P%d.", uaudio_play_buffer);
173       size_t played = 0;
174       while(played < b->nsamples)
175         played += uaudio_thread_play_callback((char *)b->samples
176                                               + played * uaudio_sample_size,
177                                               b->nsamples - played);
178       pthread_mutex_lock(&uaudio_thread_lock);
179       /* Move to next buffer */
180       uaudio_play_buffer = (1 + uaudio_play_buffer) % UAUDIO_THREAD_BUFFERS;
181       /* Awaken collector */
182       pthread_cond_broadcast(&uaudio_thread_cond);
183       resync = 0;
184     } else {
185       /* Insufficient data to play, wait for collector */
186       pthread_cond_wait(&uaudio_thread_cond, &uaudio_thread_lock);
187       /* (Still) re-synchronizing */
188       resync = 1;
189     }
190   }
191   pthread_mutex_unlock(&uaudio_thread_lock);
192   return NULL;
193 }
194
195 /** @brief Create background threads for audio processing 
196  * @param callback Callback to collect audio data
197  * @param userdata Passed to @p callback
198  * @param playcallback Callback to play audio data
199  * @param min Minimum number of samples to play in a chunk
200  * @param max Maximum number of samples to play in a chunk
201  * @param flags Flags
202  *
203  * @p callback will be called multiple times in quick succession if necessary
204  * to gather at least @p min samples.  Equally @p playcallback may be called
205  * repeatedly in quick succession to play however much was received in a single
206  * chunk.
207  *
208  * Possible flags are:
209  * - @ref UAUDIO_THREAD_FAKE_PAUSE
210  */
211 void uaudio_thread_start(uaudio_callback *callback,
212                          void *userdata,
213                          uaudio_playcallback *playcallback,
214                          size_t min,
215                          size_t max,
216                          unsigned flags) {
217   int e;
218   uaudio_thread_collect_callback = callback;
219   uaudio_thread_userdata = userdata;
220   uaudio_thread_play_callback = playcallback;
221   uaudio_thread_min = min;
222   uaudio_thread_max = max;
223   uaudio_thread_flags = flags;
224   uaudio_thread_started = 1;
225   for(int n = 0; n < UAUDIO_THREAD_BUFFERS; ++n)
226     uaudio_buffers[n].samples = xcalloc_noptr(uaudio_thread_max,
227                                               uaudio_sample_size);
228   uaudio_collect_buffer = uaudio_play_buffer = 0;
229   if((e = pthread_create(&uaudio_collect_thread,
230                          NULL,
231                          uaudio_collect_thread_fn,
232                          NULL)))
233     fatal(e, "pthread_create");
234   if((e = pthread_create(&uaudio_play_thread,
235                          NULL,
236                          uaudio_play_thread_fn,
237                          NULL)))
238     fatal(e, "pthread_create");
239 }
240
241 /** @brief Shut down background threads for audio processing */
242 void uaudio_thread_stop(void) {
243   void *result;
244
245   pthread_mutex_lock(&uaudio_thread_lock);
246   uaudio_thread_activated = 0;
247   uaudio_thread_started = 0;
248   pthread_cond_broadcast(&uaudio_thread_cond);
249   pthread_mutex_unlock(&uaudio_thread_lock);
250   pthread_join(uaudio_collect_thread, &result);
251   pthread_join(uaudio_play_thread, &result);
252   for(int n = 0; n < UAUDIO_THREAD_BUFFERS; ++n)
253     xfree(uaudio_buffers[n].samples);
254 }
255
256 /** @brief Activate audio output */
257 void uaudio_thread_activate(void) {
258   pthread_mutex_lock(&uaudio_thread_lock);
259   uaudio_thread_activated = 1;
260   pthread_cond_broadcast(&uaudio_thread_cond);
261   while(!uaudio_thread_collecting)
262     pthread_cond_wait(&uaudio_thread_cond, &uaudio_thread_lock);
263   pthread_mutex_unlock(&uaudio_thread_lock);
264 }
265
266 /** @brief Deactivate audio output */
267 void uaudio_thread_deactivate(void) {
268   pthread_mutex_lock(&uaudio_thread_lock);
269   uaudio_thread_activated = 0;
270   pthread_cond_broadcast(&uaudio_thread_cond);
271   if(!(uaudio_thread_flags & UAUDIO_THREAD_FAKE_PAUSE)) {
272     while(uaudio_thread_collecting || uaudio_buffers_used())
273       pthread_cond_wait(&uaudio_thread_cond, &uaudio_thread_lock);
274   }
275   pthread_mutex_unlock(&uaudio_thread_lock);
276 }
277
278 /*
279 Local Variables:
280 c-basic-offset:2
281 comment-column:40
282 fill-column:79
283 indent-tabs-mode:nil
284 End:
285 */