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