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