chiark / gitweb /
A little doxygen tidy-up
[disorder] / lib / uaudio-thread.c
CommitLineData
4fd38868
RK
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 */
38struct 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 */
57static struct uaudio_buffer uaudio_buffers[UAUDIO_THREAD_BUFFERS];
58
59/** @brief Buffer to read into */
60static unsigned uaudio_collect_buffer;
61
62/** @brief Buffer to play from */
63static unsigned uaudio_play_buffer;
64
65/** @brief Collection thread ID */
66static pthread_t uaudio_collect_thread;
67
68/** @brief Playing thread ID */
69static pthread_t uaudio_play_thread;
70
63761c19
RK
71/** @brief Flags */
72static unsigned uaudio_thread_flags;
73
4fd38868
RK
74static uaudio_callback *uaudio_thread_collect_callback;
75static uaudio_playcallback *uaudio_thread_play_callback;
76static void *uaudio_thread_userdata;
77static int uaudio_thread_started;
78static int uaudio_thread_activated;
79static int uaudio_thread_collecting;
80static pthread_mutex_t uaudio_thread_lock = PTHREAD_MUTEX_INITIALIZER;
81static pthread_cond_t uaudio_thread_cond = PTHREAD_COND_INITIALIZER;
82
83/** @brief Minimum number of samples per chunk */
84static size_t uaudio_thread_min;
85
86/** @brief Maximum number of samples per chunk */
87static size_t uaudio_thread_max;
88
89/** @brief Return number of buffers currently in use */
90static 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 */
99static 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);
63761c19
RK
110 while(uaudio_thread_activated
111 || (uaudio_thread_flags & UAUDIO_THREAD_FAKE_PAUSE)) {
4fd38868
RK
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;
63761c19
RK
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;
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
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 */
155static 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
8d251217 201 * @param flags Flags
4fd38868
RK
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.
8d251217
RK
207 *
208 * Possible flags are:
209 * - @ref UAUDIO_THREAD_FAKE_PAUSE
4fd38868
RK
210 */
211void uaudio_thread_start(uaudio_callback *callback,
212 void *userdata,
213 uaudio_playcallback *playcallback,
214 size_t min,
63761c19
RK
215 size_t max,
216 unsigned flags) {
4fd38868
RK
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;
63761c19 223 uaudio_thread_flags = flags;
4fd38868
RK
224 uaudio_thread_started = 1;
225 for(int n = 0; n < UAUDIO_THREAD_BUFFERS; ++n)
8e93ddd1
RK
226 uaudio_buffers[n].samples = xcalloc_noptr(uaudio_thread_max,
227 uaudio_sample_size);
4fd38868
RK
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 */
242void 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 */
257void 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 */
267void uaudio_thread_deactivate(void) {
268 pthread_mutex_lock(&uaudio_thread_lock);
269 uaudio_thread_activated = 0;
270 pthread_cond_broadcast(&uaudio_thread_cond);
63761c19
RK
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 }
4fd38868
RK
275 pthread_mutex_unlock(&uaudio_thread_lock);
276}
277
278/*
279Local Variables:
280c-basic-offset:2
281comment-column:40
282fill-column:79
283indent-tabs-mode:nil
284End:
285*/