chiark / gitweb /
Build Makefile for sounds directory.
[jog] / ausys-win32.c
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
68 typedef 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
75 const char *const ausys_suffix = "wav";
76
77 /*----- Static data -------------------------------------------------------*/
78
79 static qnode *qhead = 0, *qtail = 0;    /* Queue of samples to play */
80 static qnode *qfree = 0;                /* Queue of samples to free */
81
82 static pthread_t tid_play;              /* The sample-playing thread */
83 static pthread_mutex_t mx_queue;        /* Mutex for @qhead@ */
84 static pthread_cond_t cv_play;          /* More samples to play */
85
86 static pthread_t tid_free;              /* The sample-freeing thread */
87 static pthread_mutex_t mx_free;         /* Mutex for @qfree@ */
88 static pthread_cond_t cv_free;          /* More sample data to free */
89
90 static 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
126 static 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
170 static 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
206 void 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
238 void 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
256 void 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
263 void 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
282 au_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
307 void 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
336 void 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 -------------------------------------------------*/