Mercurial > sdl-ios-xcode
annotate src/thread/bsdi/SDL_syssem.c @ 942:41a59de7f2ed
Here are patches for SDL12 and SDL_mixer for 4 or 6 channel
surround sound on Linux using the Alsa driver. To use them, naturally
you need a sound card that will do 4 or 6 channels and probably also a
recent version of the Alsa drivers and library. Since the only SDL
output driver that knows about surround sound is the Alsa driver,
you���ll want to choose it, using:
export SDL_AUDIODRIVER=alsa
There are no syntactic changes to the programming API. No new
library calls, no differences in arguments.
There are two semantic changes:
(1) For library calls with number of channels as an argument, formerly
you could use only 1 or 2 for the number of channels. Now you
can also use 4 or 6.
(2) The two "left" and "right" arguments to Mix_SetPanning, for the
case of 4 or 6 channels, no longer simply control the volumes of
the left and right channels. Now the "left" argument is converted
to an angle and Mix_SetPosition is called, and the "right" argu-
ment is ignored.
With two exceptions, so far as I know, the modified SDL12 and
SDL_mixer work the same way as the original versions, when opened for
1 or 2 channel output. The two exceptions are bugs which I fixed.
Well, the first, anyway, is a bug for sure. When rate conversions up
or down by a factor of two are applied (in src/audio/SDL_audiocvt.c),
streams with different numbers of channels (that is, mono and stereo)
are treated the same way: either each sample is copied or every other
sample is omitted. This is ok for mono, but for stereo, it is frames
that should be copied or omitted, where by "frame" I mean a portion of
the stream containing one sample for each channel. (In the SDL source,
confusingly, sometimes frames are called "samples".) So for these
rate conversions, stereo streams have to be treated differently, and
they are, in my modified version.
The other problem that might be characterized as a bug arises
when SDL_mixer is passed a multichannel chunk which does not have an
integral number of frames. Due to the way the effect_position code
loops over frames, when the chunk ends with a partial frame, memory
outside the chunk buffer will be accessed. In the case of stereo,
it���s possible that because malloc may give more memory than requested,
this potential problem never actually causes a segment fault. I don���t
know. For 6 channel chunks, I do know, and it does cause segment
faults.
If SDL_mixer is passed defective chunks and this causes a segment
fault, arguably, that���s not a bug in SDL_mixer. Still, whether or not
it counts as a bug, it���s easy to protect against, so why not? I added
code in mixer.c to discard any partial frame at the end of a chunk.
Then what about when SDL or SDL_mixer is opened for 4 or 6 chan-
nel output? What happens with the parts of the current library
designed for stereo? I don���t know whether I���ve covered all the bases,
but I���ve tried:
(1) For playing 2 channel waves, or other cases where SDL knows it has
to match up a 2 channel source with a 4 or 6 channel output, I���ve
added code in SDL_audiocvt.c to make the necessary conversions.
(2) For playing midis using timidity, I���ve converted timidity to do 4
or 6 channel output, upon request.
(3) For playing mods using mikmod, I put ad hoc code in music.c to
convert the stereo output that mikmod produces to 4 or 6 chan-
nels. Obviously it would be better to change the mikmod code to
mix down into 4 or 6 channels, but I have a hard time following
the code in mikmod, so I didn���t do that.
(4) For playing mp3s, I put ad hoc code in smpeg to copy channels in
the case when 4 or 6 channel output is needed.
(5) There seems to be no problem with .ogg files - stereo .oggs can be
up converted as .wavs are.
(6) The effect_position code in SDL_mixer is now generalized to in-
clude the cases of 4 and 6 channel streams.
I���ve done a very limited amount of compatibility testing for some
of the games using SDL I happen to have. For details, see the file
TESTS.
I���ve put into a separate archive, Surround-SDL-testfiles.tgz, a
couple of 6 channel wave files for testing and a 6 channel ogg file.
If you have the right hardware and version of Alsa, you should be able
to play the wave files with the Alsa utility aplay (and hear all
channels, except maybe lfe, for chan-id.wav, since it���s rather faint).
Don���t expect aplay to give good sound, though. There���s something
wrong with the current version of aplay.
The canyon.ogg file is to test loading of 6 channel oggs. After
patching and compiling, you can play it with playmus. (My version of
ogg123 will not play it, and I had to patch mplayer to get it to play
6 channel oggs.)
Greg Lee <greg@ling.lll.hawaii.edu>
Thus, July 1, 2004
author | Sam Lantinga <slouken@libsdl.org> |
---|---|
date | Sat, 21 Aug 2004 12:27:02 +0000 |
parents | b8d311d90021 |
children | c9b51268668f |
rev | line source |
---|---|
256 | 1 /* |
2 SDL - Simple DirectMedia Layer | |
769
b8d311d90021
Updated copyright information for 2004 (Happy New Year!)
Sam Lantinga <slouken@libsdl.org>
parents:
297
diff
changeset
|
3 Copyright (C) 1997-2004 Sam Lantinga |
256 | 4 |
5 This library is free software; you can redistribute it and/or | |
6 modify it under the terms of the GNU Library General Public | |
7 License as published by the Free Software Foundation; either | |
8 version 2 of the License, or (at your option) any later version. | |
9 | |
10 This library 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 GNU | |
13 Library General Public License for more details. | |
14 | |
15 You should have received a copy of the GNU Library General Public | |
16 License along with this library; if not, write to the Free | |
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
18 | |
19 Sam Lantinga | |
20 slouken@devolution.com | |
21 */ | |
22 | |
23 #ifdef SAVE_RCSID | |
24 static char rcsid = | |
25 "@(#) $Id$"; | |
26 #endif | |
27 | |
28 #include <stdlib.h> | |
29 #include "SDL_error.h" | |
30 #include "SDL_thread.h" | |
31 #include "SDL_timer.h" | |
32 | |
33 #ifdef SDL_USE_PTHREADS | |
34 | |
35 #include <stdio.h> | |
36 #include <stdlib.h> | |
37 #include <unistd.h> /* For getpid() */ | |
38 #include <pthread.h> | |
39 | |
40 | |
41 /* | |
42 * This is semaphore.h inlined here so that BSD/OS POSIX semaphore are | |
43 * completely selfcontained without requiring any additional include files | |
44 * or libraries not present in the stock system | |
45 */ | |
46 | |
47 /* semaphore.h: POSIX 1003.1b semaphores */ | |
48 | |
49 /*- | |
50 * Copyright (c) 1996, 1997 | |
51 * HD Associates, Inc. All rights reserved. | |
52 * | |
53 * Redistribution and use in source and binary forms, with or without | |
54 * modification, are permitted provided that the following conditions | |
55 * are met: | |
56 * 1. Redistributions of source code must retain the above copyright | |
57 * notice, this list of conditions and the following disclaimer. | |
58 * 2. Redistributions in binary form must reproduce the above copyright | |
59 * notice, this list of conditions and the following disclaimer in the | |
60 * documentation and/or other materials provided with the distribution. | |
61 * 3. All advertising materials mentioning features or use of this software | |
62 * must display the following acknowledgement: | |
63 * This product includes software developed by HD Associates, Inc | |
64 * 4. Neither the name of the author nor the names of any co-contributors | |
65 * may be used to endorse or promote products derived from this software | |
66 * without specific prior written permission. | |
67 * | |
68 * THIS SOFTWARE IS PROVIDED BY HD ASSOCIATES AND CONTRIBUTORS ``AS IS'' AND | |
69 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
70 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
71 * ARE DISCLAIMED. IN NO EVENT SHALL HD ASSOCIATES OR CONTRIBUTORS BE LIABLE | |
72 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
73 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
74 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
75 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
76 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
77 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
78 * SUCH DAMAGE. | |
79 * | |
80 * $FreeBSD: src/sys/posix4/semaphore.h,v 1.6 2000/01/20 07:55:42 jasone Exp $ | |
81 */ | |
82 | |
83 #include <machine/limits.h> | |
84 | |
85 #include <sys/types.h> | |
86 #include <fcntl.h> | |
87 | |
88 /* Opaque type definition. */ | |
89 struct sem; | |
90 typedef struct sem *sem_t; | |
91 | |
92 #define SEM_FAILED ((sem_t *)0) | |
93 #define SEM_VALUE_MAX UINT_MAX | |
94 | |
95 #include <sys/cdefs.h> | |
96 | |
97 __BEGIN_DECLS | |
98 int sem_init __P((sem_t *, int, unsigned int)); | |
99 int sem_destroy __P((sem_t *)); | |
100 sem_t *sem_open __P((const char *, int, ...)); | |
101 int sem_close __P((sem_t *)); | |
102 int sem_unlink __P((const char *)); | |
103 int sem_wait __P((sem_t *)); | |
104 int sem_trywait __P((sem_t *)); | |
105 int sem_post __P((sem_t *)); | |
106 int sem_getvalue __P((sem_t *, int *)); | |
107 __END_DECLS | |
108 | |
109 /* END of inlined semaphore.h */ | |
110 | |
111 /* Wrapper around POSIX 1003.1b semaphores */ | |
112 | |
113 struct SDL_semaphore { | |
114 sem_t *sem; | |
115 sem_t sem_data; | |
116 }; | |
117 | |
118 /* Create a semaphore, initialized with value */ | |
119 SDL_sem *SDL_CreateSemaphore(Uint32 initial_value) | |
120 { | |
121 SDL_sem *sem = (SDL_sem *) malloc(sizeof(SDL_sem)); | |
122 if ( sem ) { | |
123 if ( sem_init(&sem->sem_data, 0, initial_value) < 0 ) { | |
124 SDL_SetError("sem_init() failed"); | |
125 free(sem); | |
126 sem = NULL; | |
127 } else { | |
128 sem->sem = &sem->sem_data; | |
129 } | |
130 } else { | |
131 SDL_OutOfMemory(); | |
132 } | |
133 return sem; | |
134 } | |
135 | |
136 void SDL_DestroySemaphore(SDL_sem *sem) | |
137 { | |
138 if ( sem ) { | |
139 sem_destroy(sem->sem); | |
140 free(sem); | |
141 } | |
142 } | |
143 | |
144 int SDL_SemTryWait(SDL_sem *sem) | |
145 { | |
146 int retval; | |
147 | |
148 if ( ! sem ) { | |
149 SDL_SetError("Passed a NULL semaphore"); | |
150 return -1; | |
151 } | |
152 retval = SDL_MUTEX_TIMEDOUT; | |
153 if ( sem_trywait(sem->sem) == 0 ) | |
154 retval = 0; | |
155 return retval; | |
156 } | |
157 | |
158 int SDL_SemWait(SDL_sem *sem) | |
159 { | |
160 int retval; | |
161 | |
162 if ( ! sem ) { | |
163 SDL_SetError("Passed a NULL semaphore"); | |
164 return -1; | |
165 } | |
166 | |
167 retval = sem_wait(sem->sem); | |
168 if ( retval < 0 ) { | |
169 SDL_SetError("sem_wait() failed"); | |
170 } | |
171 return retval; | |
172 } | |
173 | |
174 int SDL_SemWaitTimeout(SDL_sem *sem, Uint32 timeout) | |
175 { | |
176 int retval; | |
177 | |
178 if ( ! sem ) { | |
179 SDL_SetError("Passed a NULL semaphore"); | |
180 return -1; | |
181 } | |
182 | |
183 /* Try the easy cases first */ | |
184 if ( timeout == 0 ) { | |
185 return SDL_SemTryWait(sem); | |
186 } | |
187 if ( timeout == SDL_MUTEX_MAXWAIT ) { | |
188 return SDL_SemWait(sem); | |
189 } | |
190 | |
191 /* Ack! We have to busy wait... */ | |
192 timeout += SDL_GetTicks(); | |
193 do { | |
194 retval = SDL_SemTryWait(sem); | |
195 if ( retval == 0 ) { | |
196 break; | |
197 } | |
198 SDL_Delay(1); | |
199 } while ( SDL_GetTicks() < timeout ); | |
200 | |
201 return retval; | |
202 } | |
203 | |
204 Uint32 SDL_SemValue(SDL_sem *sem) | |
205 { | |
206 int ret = 0; | |
207 if ( sem ) { | |
208 sem_getvalue(sem->sem, &ret); | |
209 if ( ret < 0 ) { | |
210 ret = 0; | |
211 } | |
212 } | |
213 return (Uint32)ret; | |
214 } | |
215 | |
216 int SDL_SemPost(SDL_sem *sem) | |
217 { | |
218 int retval; | |
219 | |
220 if ( ! sem ) { | |
221 SDL_SetError("Passed a NULL semaphore"); | |
222 return -1; | |
223 } | |
224 | |
225 retval = sem_post(sem->sem); | |
226 if ( retval < 0 ) { | |
227 SDL_SetError("sem_post() failed"); | |
228 } | |
229 return retval; | |
230 } | |
231 | |
232 /* | |
233 * BEGIN inlined uthread_sem.c. This is done here so that no extra libraries | |
234 * or include files not present in BSD/OS are required | |
235 */ | |
236 | |
237 /* | |
238 * Copyright (C) 2000 Jason Evans <jasone@freebsd.org>. | |
239 * All rights reserved. | |
240 * | |
241 * Redistribution and use in source and binary forms, with or without | |
242 * modification, are permitted provided that the following conditions | |
243 * are met: | |
244 * 1. Redistributions of source code must retain the above copyright | |
245 * notice(s), this list of conditions and the following disclaimer as | |
246 * the first lines of this file unmodified other than the possible | |
247 * addition of one or more copyright notices. | |
248 * 2. Redistributions in binary form must reproduce the above copyright | |
249 * notice(s), this list of conditions and the following disclaimer in | |
250 * the documentation and/or other materials provided with the | |
251 * distribution. | |
252 * | |
253 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY | |
254 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
255 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
256 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE | |
257 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
258 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
259 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR | |
260 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | |
261 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE | |
262 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, | |
263 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
264 * | |
265 * $FreeBSD: src/lib/libc_r/uthread/uthread_sem.c,v 1.3.2.1 2000/07/18 02:05:57 jasone Exp $ | |
266 */ | |
267 | |
268 #include <errno.h> | |
269 #include <pthread.h> | |
270 | |
271 /* Begin thread_private.h kluge */ | |
272 /* | |
273 * These come out of (or should go into) thread_private.h - rather than have | |
274 * to copy (or symlink) the files from the source tree these definitions are | |
275 * inlined here. Obviously these go away when this module is part of libc. | |
276 */ | |
277 struct sem { | |
278 #define SEM_MAGIC ((u_int32_t) 0x09fa4012) | |
279 u_int32_t magic; | |
280 pthread_mutex_t lock; | |
281 pthread_cond_t gtzero; | |
282 u_int32_t count; | |
283 u_int32_t nwaiters; | |
284 }; | |
285 | |
286 extern pthread_once_t _thread_init_once; | |
287 extern int _threads_initialized; | |
288 extern void _thread_init __P((void)); | |
289 #define THREAD_INIT() \ | |
290 (void) pthread_once(&_thread_init_once, _thread_init) | |
291 #define THREAD_SAFE() \ | |
292 (_threads_initialized != 0) | |
293 | |
294 #define _SEM_CHECK_VALIDITY(sem) \ | |
295 if ((*(sem))->magic != SEM_MAGIC) { \ | |
296 errno = EINVAL; \ | |
297 retval = -1; \ | |
298 goto RETURN; \ | |
299 } | |
300 /* End thread_private.h kluge */ | |
301 | |
302 int | |
303 sem_init(sem_t *sem, int pshared, unsigned int value) | |
304 { | |
305 int retval; | |
306 | |
307 if (!THREAD_SAFE()) | |
308 THREAD_INIT(); | |
309 | |
310 /* | |
311 * Range check the arguments. | |
312 */ | |
313 if (pshared != 0) { | |
314 /* | |
315 * The user wants a semaphore that can be shared among | |
316 * processes, which this implementation can't do. Sounds like a | |
317 * permissions problem to me (yeah right). | |
318 */ | |
319 errno = EPERM; | |
320 retval = -1; | |
321 goto RETURN; | |
322 } | |
323 | |
324 if (value > SEM_VALUE_MAX) { | |
325 errno = EINVAL; | |
326 retval = -1; | |
327 goto RETURN; | |
328 } | |
329 | |
330 *sem = (sem_t)malloc(sizeof(struct sem)); | |
331 if (*sem == NULL) { | |
332 errno = ENOSPC; | |
333 retval = -1; | |
334 goto RETURN; | |
335 } | |
336 | |
337 /* | |
338 * Initialize the semaphore. | |
339 */ | |
340 if (pthread_mutex_init(&(*sem)->lock, NULL) != 0) { | |
341 free(*sem); | |
342 errno = ENOSPC; | |
343 retval = -1; | |
344 goto RETURN; | |
345 } | |
346 | |
347 if (pthread_cond_init(&(*sem)->gtzero, NULL) != 0) { | |
348 pthread_mutex_destroy(&(*sem)->lock); | |
349 free(*sem); | |
350 errno = ENOSPC; | |
351 retval = -1; | |
352 goto RETURN; | |
353 } | |
354 | |
355 (*sem)->count = (u_int32_t)value; | |
356 (*sem)->nwaiters = 0; | |
357 (*sem)->magic = SEM_MAGIC; | |
358 | |
359 retval = 0; | |
360 RETURN: | |
361 return retval; | |
362 } | |
363 | |
364 int | |
365 sem_destroy(sem_t *sem) | |
366 { | |
367 int retval; | |
368 | |
369 _SEM_CHECK_VALIDITY(sem); | |
370 | |
371 /* Make sure there are no waiters. */ | |
372 pthread_mutex_lock(&(*sem)->lock); | |
373 if ((*sem)->nwaiters > 0) { | |
374 pthread_mutex_unlock(&(*sem)->lock); | |
375 errno = EBUSY; | |
376 retval = -1; | |
377 goto RETURN; | |
378 } | |
379 pthread_mutex_unlock(&(*sem)->lock); | |
380 | |
381 pthread_mutex_destroy(&(*sem)->lock); | |
382 pthread_cond_destroy(&(*sem)->gtzero); | |
383 (*sem)->magic = 0; | |
384 | |
385 free(*sem); | |
386 | |
387 retval = 0; | |
388 RETURN: | |
389 return retval; | |
390 } | |
391 | |
392 sem_t * | |
393 sem_open(const char *name, int oflag, ...) | |
394 { | |
395 errno = ENOSYS; | |
396 return SEM_FAILED; | |
397 } | |
398 | |
399 int | |
400 sem_close(sem_t *sem) | |
401 { | |
402 errno = ENOSYS; | |
403 return -1; | |
404 } | |
405 | |
406 int | |
407 sem_unlink(const char *name) | |
408 { | |
409 errno = ENOSYS; | |
410 return -1; | |
411 } | |
412 | |
413 int | |
414 sem_wait(sem_t *sem) | |
415 { | |
416 int retval; | |
417 | |
418 pthread_testcancel(); | |
419 | |
420 _SEM_CHECK_VALIDITY(sem); | |
421 | |
422 pthread_mutex_lock(&(*sem)->lock); | |
423 | |
424 while ((*sem)->count == 0) { | |
425 (*sem)->nwaiters++; | |
426 pthread_cond_wait(&(*sem)->gtzero, &(*sem)->lock); | |
427 (*sem)->nwaiters--; | |
428 } | |
429 (*sem)->count--; | |
430 | |
431 pthread_mutex_unlock(&(*sem)->lock); | |
432 | |
433 retval = 0; | |
434 RETURN: | |
435 | |
436 pthread_testcancel(); | |
437 return retval; | |
438 } | |
439 | |
440 int | |
441 sem_trywait(sem_t *sem) | |
442 { | |
443 int retval; | |
444 | |
445 _SEM_CHECK_VALIDITY(sem); | |
446 | |
447 pthread_mutex_lock(&(*sem)->lock); | |
448 | |
449 if ((*sem)->count > 0) { | |
450 (*sem)->count--; | |
451 retval = 0; | |
452 } else { | |
453 errno = EAGAIN; | |
454 retval = -1; | |
455 } | |
456 | |
457 pthread_mutex_unlock(&(*sem)->lock); | |
458 | |
459 RETURN: | |
460 return retval; | |
461 } | |
462 | |
463 int | |
464 sem_post(sem_t *sem) | |
465 { | |
466 int retval; | |
467 | |
468 _SEM_CHECK_VALIDITY(sem); | |
469 | |
470 pthread_mutex_lock(&(*sem)->lock); | |
471 | |
472 (*sem)->count++; | |
473 if ((*sem)->nwaiters > 0) { | |
474 /* | |
475 * We must use pthread_cond_broadcast() rather than | |
476 * pthread_cond_signal() in order to assure that the highest | |
477 * priority thread is run by the scheduler, since | |
478 * pthread_cond_signal() signals waiting threads in FIFO order. | |
479 */ | |
480 pthread_cond_broadcast(&(*sem)->gtzero); | |
481 } | |
482 | |
483 pthread_mutex_unlock(&(*sem)->lock); | |
484 | |
485 retval = 0; | |
486 RETURN: | |
487 return retval; | |
488 } | |
489 | |
490 int | |
491 sem_getvalue(sem_t *sem, int *sval) | |
492 { | |
493 int retval; | |
494 | |
495 _SEM_CHECK_VALIDITY(sem); | |
496 | |
497 pthread_mutex_lock(&(*sem)->lock); | |
498 *sval = (int)(*sem)->count; | |
499 pthread_mutex_unlock(&(*sem)->lock); | |
500 | |
501 retval = 0; | |
502 RETURN: | |
503 return retval; | |
504 } | |
505 | |
506 /* END of inlined uthread_sem.c */ | |
507 #endif /* SDL_USE_PTHREADS */ |