2 * This file is part of DisOrder.
3 * Copyright (C) 2009 Richard Kettlewell
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.
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.
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/>.
18 /** @file lib/uaudio-thread.c
19 * @brief Background thread for audio processing */
29 /** @brief Number of buffers
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
35 #define UAUDIO_THREAD_BUFFERS 4
37 /** @brief Buffer data structure */
38 struct uaudio_buffer
{
39 /** @brief Pointer to sample data */
42 /** @brief Count of samples */
46 /** @brief Input buffers
48 * This is actually a ring buffer, managed by @ref uaudio_collect_buffer and
49 * @ref uaudio_play_buffer.
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).
57 static struct uaudio_buffer uaudio_buffers
[UAUDIO_THREAD_BUFFERS
];
59 /** @brief Buffer to read into */
60 static unsigned uaudio_collect_buffer
;
62 /** @brief Buffer to play from */
63 static unsigned uaudio_play_buffer
;
65 /** @brief Collection thread ID */
66 static pthread_t uaudio_collect_thread
;
68 /** @brief Playing thread ID */
69 static pthread_t uaudio_play_thread
;
72 static unsigned uaudio_thread_flags
;
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
;
83 /** @brief Minimum number of samples per chunk */
84 static size_t uaudio_thread_min
;
86 /** @brief Maximum number of samples per chunk */
87 static size_t uaudio_thread_max
;
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
;
94 /** @brief Background thread for audio collection
96 * Collects data while activated and communicates its status via @ref
97 * uaudio_thread_collecting.
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
);
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
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);
120 /* Keep on trying until we get the minimum required amount of data */
122 if(uaudio_thread_activated
) {
123 while(b
->nsamples
< uaudio_thread_min
) {
124 b
->nsamples
+= uaudio_thread_collect_callback
126 + b
->nsamples
* uaudio_sample_size
,
127 uaudio_thread_max
- b
->nsamples
,
128 uaudio_thread_userdata
);
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
;
134 pthread_mutex_lock(&uaudio_thread_lock
);
135 /* Advance to next buffer */
136 uaudio_collect_buffer
= (1 + uaudio_collect_buffer
) % UAUDIO_THREAD_BUFFERS
;
138 pthread_cond_broadcast(&uaudio_thread_cond
);
140 /* No space, wait for player */
141 pthread_cond_wait(&uaudio_thread_cond
, &uaudio_thread_lock
);
143 uaudio_thread_collecting
= 0;
144 pthread_cond_broadcast(&uaudio_thread_cond
);
146 pthread_mutex_unlock(&uaudio_thread_lock
);
150 /** @brief Background thread for audio playing
152 * This thread plays data as long as there is something to play. So the
153 * buffers will drain to empty before deactivation completes.
155 static void *uaudio_play_thread_fn(void attribute((unused
)) *arg
) {
158 pthread_mutex_lock(&uaudio_thread_lock
);
159 while(uaudio_thread_started
) {
160 const int used
= uaudio_buffers_used();
164 go
= (used
== UAUDIO_THREAD_BUFFERS
- 1);
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);
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
);
185 /* Insufficient data to play, wait for collector */
186 pthread_cond_wait(&uaudio_thread_cond
, &uaudio_thread_lock
);
187 /* (Still) re-synchronizing */
191 pthread_mutex_unlock(&uaudio_thread_lock
);
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
202 * @p callback will be called multiple times in quick succession if necessary
203 * to gather at least @p min samples. Equally @p playcallback may be called
204 * repeatedly in quick succession to play however much was received in a single
207 void uaudio_thread_start(uaudio_callback
*callback
,
209 uaudio_playcallback
*playcallback
,
214 uaudio_thread_collect_callback
= callback
;
215 uaudio_thread_userdata
= userdata
;
216 uaudio_thread_play_callback
= playcallback
;
217 uaudio_thread_min
= min
;
218 uaudio_thread_max
= max
;
219 uaudio_thread_flags
= flags
;
220 uaudio_thread_started
= 1;
221 for(int n
= 0; n
< UAUDIO_THREAD_BUFFERS
; ++n
)
222 uaudio_buffers
[n
].samples
= xcalloc_noptr(uaudio_thread_max
,
224 uaudio_collect_buffer
= uaudio_play_buffer
= 0;
225 if((e
= pthread_create(&uaudio_collect_thread
,
227 uaudio_collect_thread_fn
,
229 fatal(e
, "pthread_create");
230 if((e
= pthread_create(&uaudio_play_thread
,
232 uaudio_play_thread_fn
,
234 fatal(e
, "pthread_create");
237 /** @brief Shut down background threads for audio processing */
238 void uaudio_thread_stop(void) {
241 pthread_mutex_lock(&uaudio_thread_lock
);
242 uaudio_thread_activated
= 0;
243 uaudio_thread_started
= 0;
244 pthread_cond_broadcast(&uaudio_thread_cond
);
245 pthread_mutex_unlock(&uaudio_thread_lock
);
246 pthread_join(uaudio_collect_thread
, &result
);
247 pthread_join(uaudio_play_thread
, &result
);
248 for(int n
= 0; n
< UAUDIO_THREAD_BUFFERS
; ++n
)
249 xfree(uaudio_buffers
[n
].samples
);
252 /** @brief Activate audio output */
253 void uaudio_thread_activate(void) {
254 pthread_mutex_lock(&uaudio_thread_lock
);
255 uaudio_thread_activated
= 1;
256 pthread_cond_broadcast(&uaudio_thread_cond
);
257 while(!uaudio_thread_collecting
)
258 pthread_cond_wait(&uaudio_thread_cond
, &uaudio_thread_lock
);
259 pthread_mutex_unlock(&uaudio_thread_lock
);
262 /** @brief Deactivate audio output */
263 void uaudio_thread_deactivate(void) {
264 pthread_mutex_lock(&uaudio_thread_lock
);
265 uaudio_thread_activated
= 0;
266 pthread_cond_broadcast(&uaudio_thread_cond
);
267 if(!(uaudio_thread_flags
& UAUDIO_THREAD_FAKE_PAUSE
)) {
268 while(uaudio_thread_collecting
|| uaudio_buffers_used())
269 pthread_cond_wait(&uaudio_thread_cond
, &uaudio_thread_lock
);
271 pthread_mutex_unlock(&uaudio_thread_lock
);