New audio subsystem.
[jog] / ausys-win32.c
CommitLineData
e9060e7e 1/* -*-c-*-
2 *
3 * $Id: ausys-win32.c,v 1.1 2002/02/02 19:16:28 mdw Exp $
4 *
5 * Unix-specific (SDL) audio handling
6 *
7 * (c) 2002 Mark Wooding
8 */
9
10/*----- Licensing notice --------------------------------------------------*
11 *
12 * This file is part of Jog: Programming for a jogging machine.
13 *
14 * Jog is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
18 *
19 * Jog is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with Jog; if not, write to the Free Software Foundation,
26 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 */
28
29/*----- Revision history --------------------------------------------------*
30 *
31 * $Log: ausys-win32.c,v $
32 * Revision 1.1 2002/02/02 19:16:28 mdw
33 * New audio subsystem.
34 *
35 */
36
37/*----- Header files ------------------------------------------------------*/
38
39#ifdef HAVE_CONFIG_H
40# include "config.h"
41#endif
42
43#include <errno.h>
44#include <stdio.h>
45#include <string.h>
46
47#include <sys/types.h>
48#include <sys/time.h>
49#include <unistd.h>
50#include <pthread.h>
51
52#include <windows.h>
53
54#include <mLib/alloc.h>
55#include <mLib/bits.h>
56#include <mLib/sub.h>
57#include <mLib/trace.h>
58
59#include "au.h"
60#include "ausys.h"
61#include "err.h"
62#include "jog.h"
63
64/*----- Data structures ---------------------------------------------------*/
65
66/* --- Queue of samples to play --- */
67
68typedef struct qnode {
69 struct qnode *next; /* Next item in the queue */
70 au_data *a; /* Pointer to sample data */
71} qnode;
72
73/*----- Global variables --------------------------------------------------*/
74
75const char *const ausys_suffix = "wav";
76
77/*----- Static data -------------------------------------------------------*/
78
79static qnode *qhead = 0, *qtail = 0; /* Queue of samples to play */
80static qnode *qfree = 0; /* Queue of samples to free */
81
82static pthread_t tid_play; /* The sample-playing thread */
83static pthread_mutex_t mx_queue; /* Mutex for @qhead@ */
84static pthread_cond_t cv_play; /* More samples to play */
85
86static pthread_t tid_free; /* The sample-freeing thread */
87static pthread_mutex_t mx_free; /* Mutex for @qfree@ */
88static pthread_cond_t cv_free; /* More sample data to free */
89
90static pthread_mutex_t mx_sub; /* Protects mLib @sub@ functions */
91
92/*----- Thread structure --------------------------------------------------*
93 *
94 * In order to ensure that samples don't overlap each other, we play them
95 * synchronously in a separate thread. The @play@ function reads samples to
96 * play from the queue at @qhead@. Hence @qhead@ must be locked when it's
97 * modified, using @mx_queue@.
98 *
99 * In order to keep latency down when new sample data is wanted, we don't do
100 * potentially tedious things like freeing sample data blocks in the @play@
101 * thread. Instead, there's a queue of sample blocks which need freeing, and
102 * a separate thread @dofree@ which processes the queue. The queue, @qfree@
103 * is locked by @mx_free@. When new nodes are added to @qfree@, the
104 * condition @cv_free@ is signalled.
105 *
106 * Finally, the mLib `sub' module isn't thread-safe, so it's locked
107 * separately by @mx_sub@.
108 *
109 * There's an ordering on mutexes. If you want more than one mutex at a
110 * time, you must lock the greatest first. The ordering is:
111 *
112 * play > free > sub
113 */
114
115/*----- Main code ---------------------------------------------------------*/
116
117/* --- @dofree@ --- *
118 *
119 * Arguments: @void *u@ = unused pointer
120 *
121 * Returns: An unused pointer.
122 *
123 * Use: Frees unwanted sample queue nodes.
124 */
125
126static void *play(void *u)
127{
128 qnode *q, *qq = 0;
129
130 for (;;) {
131
132 /* --- If we have nothing to do locally, fetch the external queue --- */
133
134 if (!qq) {
135 pthread_mutex_lock(&mx_queue);
136 while (!qhead) {
137 T( trace(T_AUSYS, "ausys: waiting for samples to play"); )
138 pthread_cond_wait(&cv_play, &mx_queue);
139 }
140 qq = qhead;
141 qhead = qtail = 0;
142 pthread_mutex_unlock(&mx_queue);
143 }
144
145 /* --- Play the next sample --- */
146 q = qq;
147 qq = q->next;
148 T( trace(T_AUSYS, "ausys: playing `%s'", SYM_NAME(q->a->s)); )
149 PlaySound((const void *)q->a->p, 0, SND_SYNC | SND_MEMORY);
150
151 /* --- Put it on the free list --- */
152
153 pthread_mutex_lock(&mx_free);
154 q->next = qfree;
155 qfree = q;
156 pthread_mutex_unlock(&mx_free);
157 pthread_cond_signal(&cv_free);
158 }
159}
160
161/* --- @dofree@ --- *
162 *
163 * Arguments: @void *u@ = unused pointer
164 *
165 * Returns: An unused pointer.
166 *
167 * Use: Frees unwanted sample queue nodes.
168 */
169
170static void *dofree(void *u)
171{
172 qnode *q, *qq;
173
174 pthread_mutex_lock(&mx_free);
175 for (;;) {
176 T( trace(T_AUSYS, "ausys: dofree sleeping"); )
177 pthread_cond_wait(&cv_free, &mx_free);
178 T( trace(T_AUSYS, "ausys: dofree woken: work to do"); )
179
180 while (qfree) {
181 q = qfree;
182 qfree = 0;
183 pthread_mutex_lock(&mx_sub);
184 while (q) {
185 qq = q->next;
186 T( trace(T_AUSYS, "ausys: freeing `%s'", SYM_NAME(q->a->s)); )
187 au_free_unlocked(q->a);
188 DESTROY(q);
189 q = qq;
190 }
191 pthread_mutex_unlock(&mx_sub);
192 }
193 }
194}
195
196/* --- @ausys_init@ --- *
197 *
198 * Arguments: ---
199 *
200 * Returns: ---
201 *
202 * Use: Does any initialization required by the system-specific audio
203 * handler.
204 */
205
206void ausys_init(void)
207{
208 pthread_attr_t ta;
209 int e;
210
211 if ((e = pthread_mutex_init(&mx_free, 0)) != 0 ||
212 (e = pthread_mutex_init(&mx_sub, 0)) != 0 ||
213 (e = pthread_mutex_init(&mx_queue, 0)) != 0 ||
214 (e = pthread_cond_init(&cv_play, 0)) != 0 ||
215 (e = pthread_cond_init(&cv_free, 0)) != 0 ||
216 (e = pthread_attr_init(&ta)) != 0 ||
217 (e = pthread_attr_setdetachstate(&ta, PTHREAD_CREATE_DETACHED)) != 0 ||
218 (e = pthread_create(&tid_free, &ta, play, 0)) != 0 ||
219 (e = pthread_create(&tid_free, &ta, dofree, 0)) != 0) {
220 err_report(ERR_AUDIO, ERRAU_INIT, e,
221 "couldn't create audio threads: %s", strerror(errno));
222 exit(EXIT_FAILURE);
223 }
224 pthread_attr_destroy(&ta);
225
226 T( trace(T_AUSYS, "ausys: initalized ok"); )
227}
228
229/* --- @ausys_shutdown@ --- *
230 *
231 * Arguments: ---
232 *
233 * Returns: ---
234 *
235 * Use: Does any tidying up required.
236 */
237
238void ausys_shutdown(void)
239{
240 pthread_cancel(tid_free);
241 pthread_cancel(tid_play);
242 T( trace(T_AUSYS, "ausys: shut down ok"); )
243}
244
245/* --- @ausys_lock@, @ausys_unlock@ --- *
246 *
247 * Arguments: ---
248 *
249 * Returns: ---
250 *
251 * Use: Locks or unlocks the audio subsystem. This protects the
252 * audio queue from becoming corrupted during all the tedious
253 * asynchronous stuff.
254 */
255
256void ausys_lock(void)
257{
258 pthread_mutex_lock(&mx_free);
259 pthread_mutex_lock(&mx_sub);
260 T( trace(T_AUSYS, "ausys: acquired lock"); )
261}
262
263void ausys_unlock(void)
264{
265 pthread_mutex_unlock(&mx_sub);
266 pthread_mutex_unlock(&mx_free);
267 T( trace(T_AUSYS, "ausys: released lock"); )
268}
269
270/* --- @ausys_decode@ --- *
271 *
272 * Arguments: @au_sample *s@ = pointer to sample block
273 * @const void *p@ = pointer to sample file contents
274 * @size_t sz@ = size of sample file contents
275 *
276 * Returns: Pointer to a sample data structure.
277 *
278 * Use: Decodes a WAV file into something the system-specific layer
279 * actually wants to deal with.
280 */
281
282au_data *ausys_decode(au_sample *s, const void *p, size_t sz)
283{
284 au_data *a;
285
286 pthread_mutex_lock(&mx_sub);
287 a = CREATE(au_data);
288 pthread_mutex_unlock(&mx_sub);
289 a->p = xmalloc(sz);
290 memcpy(a->p, p, sz);
291 a->sz = sz;
292
293 T( trace(T_AUSYS, "ausys: decoded `%s' ok", SYM_NAME(s)); )
294 return (a);
295}
296
297/* --- @ausys_queue@ --- *
298 *
299 * Arguments: @au_data *a@ = an audio thingy to play
300 *
301 * Returns: ---
302 *
303 * Use: Queues an audio sample to be played. The sample should be
304 * freed (with @au_free@) when it's no longer wanted.
305 */
306
307void ausys_queue(au_data *a)
308{
309 qnode *q;
310
311 pthread_mutex_lock(&mx_sub);
312 q = CREATE(qnode);
313 pthread_mutex_unlock(&mx_sub);
314 q->next = 0;
315 q->a = a;
316 pthread_mutex_lock(&mx_queue);
317 if (qtail)
318 qtail->next = q;
319 else
320 qhead = q;
321 qtail = q;
322 pthread_mutex_unlock(&mx_queue);
323 pthread_cond_signal(&cv_play);
324 T( trace(T_AUSYS, "ausys: queuing `%s'", SYM_NAME(a->s)); )
325}
326
327/* --- @ausys_free@ --- *
328 *
329 * Arguments: @au_data *a@ = an audio thingy to free
330 *
331 * Returns: ---
332 *
333 * Use: Frees a decoded audio sample.
334 */
335
336void ausys_free(au_data *a)
337{
338 xfree(a->p);
339 DESTROY(a);
340 T( trace(T_AUSYS, "ausys: freeing data for `%s' ok", SYM_NAME(a->s)); )
341}
342
343/*----- That's all, folks -------------------------------------------------*/