Mercurial > sdl-ios-xcode
annotate src/cdrom/openbsd/SDL_syscdrom.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 | 0f3aa6ab3341 |
rev | line source |
---|---|
0 | 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 |
0 | 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 | |
252
e8157fcb3114
Updated the source with the correct e-mail address
Sam Lantinga <slouken@libsdl.org>
parents:
25
diff
changeset
|
20 slouken@libsdl.org |
0 | 21 */ |
22 | |
23 #ifdef SAVE_RCSID | |
24 static char rcsid = | |
25 "@(#) $Id$"; | |
26 #endif | |
27 | |
28 /* Functions for system-level CD-ROM audio control */ | |
29 | |
30 #include <sys/types.h> | |
31 #include <sys/ioctl.h> | |
32 #include <stdlib.h> | |
33 #include <sys/stat.h> | |
34 #include <fcntl.h> | |
35 #include <stdio.h> | |
36 #include <string.h> | |
37 #include <errno.h> | |
38 #include <unistd.h> | |
1
cf2af46e9e2a
Changes since SDL 1.2.0 release
Sam Lantinga <slouken@lokigames.com>
parents:
0
diff
changeset
|
39 #include <sys/ioctl.h> |
0 | 40 #include <sys/cdio.h> |
41 | |
42 #include "SDL_error.h" | |
43 #include "SDL_cdrom.h" | |
44 #include "SDL_syscdrom.h" | |
45 | |
46 | |
47 /* The maximum number of CD-ROM drives we'll detect */ | |
48 #define MAX_DRIVES 16 | |
49 | |
50 /* A list of available CD-ROM drives */ | |
51 static char *SDL_cdlist[MAX_DRIVES]; | |
52 static dev_t SDL_cdmode[MAX_DRIVES]; | |
53 | |
54 /* The system-dependent CD control functions */ | |
55 static const char *SDL_SYS_CDName(int drive); | |
56 static int SDL_SYS_CDOpen(int drive); | |
57 static int SDL_SYS_CDGetTOC(SDL_CD *cdrom); | |
58 static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position); | |
59 static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length); | |
60 static int SDL_SYS_CDPause(SDL_CD *cdrom); | |
61 static int SDL_SYS_CDResume(SDL_CD *cdrom); | |
62 static int SDL_SYS_CDStop(SDL_CD *cdrom); | |
63 static int SDL_SYS_CDEject(SDL_CD *cdrom); | |
64 static void SDL_SYS_CDClose(SDL_CD *cdrom); | |
65 | |
66 /* Some ioctl() errno values which occur when the tray is empty */ | |
67 #define ERRNO_TRAYEMPTY(errno) \ | |
1
cf2af46e9e2a
Changes since SDL 1.2.0 release
Sam Lantinga <slouken@lokigames.com>
parents:
0
diff
changeset
|
68 ((errno == EIO) || (errno == ENOENT) || (errno == EINVAL) || \ |
cf2af46e9e2a
Changes since SDL 1.2.0 release
Sam Lantinga <slouken@lokigames.com>
parents:
0
diff
changeset
|
69 (errno == ENODEV)) |
0 | 70 |
71 /* Check a drive to see if it is a CD-ROM */ | |
72 static int CheckDrive(char *drive, struct stat *stbuf) | |
73 { | |
74 int is_cd, cdfd; | |
75 struct ioc_read_subchannel info; | |
76 | |
77 /* If it doesn't exist, return -1 */ | |
78 if ( stat(drive, stbuf) < 0 ) { | |
79 return(-1); | |
80 } | |
81 | |
82 /* If it does exist, verify that it's an available CD-ROM */ | |
83 is_cd = 0; | |
84 if ( S_ISCHR(stbuf->st_mode) || S_ISBLK(stbuf->st_mode) ) { | |
85 cdfd = open(drive, (O_RDONLY|O_EXCL|O_NONBLOCK), 0); | |
86 if ( cdfd >= 0 ) { | |
87 info.address_format = CD_MSF_FORMAT; | |
88 info.data_format = CD_CURRENT_POSITION; | |
89 info.data_len = 0; | |
90 info.data = NULL; | |
91 /* Under Linux, EIO occurs when a disk is not present. | |
92 This isn't 100% reliable, so we use the USE_MNTENT | |
93 code above instead. | |
94 */ | |
95 if ( (ioctl(cdfd, CDIOCREADSUBCHANNEL, &info) == 0) || | |
96 ERRNO_TRAYEMPTY(errno) ) { | |
97 is_cd = 1; | |
98 } | |
99 close(cdfd); | |
100 } | |
1
cf2af46e9e2a
Changes since SDL 1.2.0 release
Sam Lantinga <slouken@lokigames.com>
parents:
0
diff
changeset
|
101 else if (ERRNO_TRAYEMPTY(errno)) |
cf2af46e9e2a
Changes since SDL 1.2.0 release
Sam Lantinga <slouken@lokigames.com>
parents:
0
diff
changeset
|
102 is_cd = 1; |
0 | 103 } |
104 return(is_cd); | |
105 } | |
106 | |
107 /* Add a CD-ROM drive to our list of valid drives */ | |
108 static void AddDrive(char *drive, struct stat *stbuf) | |
109 { | |
110 int i; | |
111 | |
112 if ( SDL_numcds < MAX_DRIVES ) { | |
113 /* Check to make sure it's not already in our list. | |
114 This can happen when we see a drive via symbolic link. | |
115 */ | |
116 for ( i=0; i<SDL_numcds; ++i ) { | |
117 if ( stbuf->st_rdev == SDL_cdmode[i] ) { | |
118 #ifdef DEBUG_CDROM | |
119 fprintf(stderr, "Duplicate drive detected: %s == %s\n", drive, SDL_cdlist[i]); | |
120 #endif | |
121 return; | |
122 } | |
123 } | |
124 | |
125 /* Add this drive to our list */ | |
126 i = SDL_numcds; | |
127 SDL_cdlist[i] = (char *)malloc(strlen(drive)+1); | |
128 if ( SDL_cdlist[i] == NULL ) { | |
129 SDL_OutOfMemory(); | |
130 return; | |
131 } | |
132 strcpy(SDL_cdlist[i], drive); | |
133 SDL_cdmode[i] = stbuf->st_rdev; | |
134 ++SDL_numcds; | |
135 #ifdef DEBUG_CDROM | |
136 fprintf(stderr, "Added CD-ROM drive: %s\n", drive); | |
137 #endif | |
138 } | |
139 } | |
140 | |
141 int SDL_SYS_CDInit(void) | |
142 { | |
143 static char *checklist[] = { | |
1
cf2af46e9e2a
Changes since SDL 1.2.0 release
Sam Lantinga <slouken@lokigames.com>
parents:
0
diff
changeset
|
144 #ifdef __OpenBSD__ |
25
3fbf86244fd2
Date: Wed, 9 May 2001 18:03:20 -0600
Sam Lantinga <slouken@lokigames.com>
parents:
1
diff
changeset
|
145 "?0 cd?c", "cdrom", NULL |
1
cf2af46e9e2a
Changes since SDL 1.2.0 release
Sam Lantinga <slouken@lokigames.com>
parents:
0
diff
changeset
|
146 #else |
0 | 147 "?0 cd?c", "?0 acd?c", "cdrom", NULL |
1
cf2af46e9e2a
Changes since SDL 1.2.0 release
Sam Lantinga <slouken@lokigames.com>
parents:
0
diff
changeset
|
148 #endif |
0 | 149 }; |
150 char *SDLcdrom; | |
151 int i, j, exists; | |
152 char drive[32]; | |
153 struct stat stbuf; | |
154 | |
155 /* Fill in our driver capabilities */ | |
156 SDL_CDcaps.Name = SDL_SYS_CDName; | |
157 SDL_CDcaps.Open = SDL_SYS_CDOpen; | |
158 SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC; | |
159 SDL_CDcaps.Status = SDL_SYS_CDStatus; | |
160 SDL_CDcaps.Play = SDL_SYS_CDPlay; | |
161 SDL_CDcaps.Pause = SDL_SYS_CDPause; | |
162 SDL_CDcaps.Resume = SDL_SYS_CDResume; | |
163 SDL_CDcaps.Stop = SDL_SYS_CDStop; | |
164 SDL_CDcaps.Eject = SDL_SYS_CDEject; | |
165 SDL_CDcaps.Close = SDL_SYS_CDClose; | |
166 | |
167 /* Look in the environment for our CD-ROM drive list */ | |
168 SDLcdrom = getenv("SDL_CDROM"); /* ':' separated list of devices */ | |
169 if ( SDLcdrom != NULL ) { | |
170 char *cdpath, *delim; | |
171 cdpath = malloc(strlen(SDLcdrom)+1); | |
172 if ( cdpath != NULL ) { | |
173 strcpy(cdpath, SDLcdrom); | |
174 SDLcdrom = cdpath; | |
175 do { | |
176 delim = strchr(SDLcdrom, ':'); | |
177 if ( delim ) { | |
178 *delim++ = '\0'; | |
179 } | |
180 if ( CheckDrive(SDLcdrom, &stbuf) > 0 ) { | |
181 AddDrive(SDLcdrom, &stbuf); | |
182 } | |
183 if ( delim ) { | |
184 SDLcdrom = delim; | |
185 } else { | |
186 SDLcdrom = NULL; | |
187 } | |
188 } while ( SDLcdrom ); | |
189 free(cdpath); | |
190 } | |
191 | |
192 /* If we found our drives, there's nothing left to do */ | |
193 if ( SDL_numcds > 0 ) { | |
194 return(0); | |
195 } | |
196 } | |
197 | |
198 /* Scan the system for CD-ROM drives */ | |
199 for ( i=0; checklist[i]; ++i ) { | |
200 if ( checklist[i][0] == '?' ) { | |
201 char *insert; | |
202 exists = 1; | |
203 for ( j=checklist[i][1]; exists; ++j ) { | |
204 sprintf(drive, "/dev/%s", &checklist[i][3]); | |
205 insert = strchr(drive, '?'); | |
206 if ( insert != NULL ) { | |
207 *insert = j; | |
208 } | |
209 switch (CheckDrive(drive, &stbuf)) { | |
210 /* Drive exists and is a CD-ROM */ | |
211 case 1: | |
212 AddDrive(drive, &stbuf); | |
213 break; | |
214 /* Drive exists, but isn't a CD-ROM */ | |
215 case 0: | |
216 break; | |
217 /* Drive doesn't exist */ | |
218 case -1: | |
219 exists = 0; | |
220 break; | |
221 } | |
222 } | |
223 } else { | |
224 sprintf(drive, "/dev/%s", checklist[i]); | |
225 if ( CheckDrive(drive, &stbuf) > 0 ) { | |
226 AddDrive(drive, &stbuf); | |
227 } | |
228 } | |
229 } | |
230 return(0); | |
231 } | |
232 | |
233 /* General ioctl() CD-ROM command function */ | |
234 static int SDL_SYS_CDioctl(int id, int command, void *arg) | |
235 { | |
236 int retval; | |
237 | |
238 retval = ioctl(id, command, arg); | |
239 if ( retval < 0 ) { | |
240 SDL_SetError("ioctl() error: %s", strerror(errno)); | |
241 } | |
242 return(retval); | |
243 } | |
244 | |
245 static const char *SDL_SYS_CDName(int drive) | |
246 { | |
247 return(SDL_cdlist[drive]); | |
248 } | |
249 | |
250 static int SDL_SYS_CDOpen(int drive) | |
251 { | |
252 return(open(SDL_cdlist[drive], (O_RDONLY|O_EXCL|O_NONBLOCK), 0)); | |
253 } | |
254 | |
255 static int SDL_SYS_CDGetTOC(SDL_CD *cdrom) | |
256 { | |
257 struct ioc_toc_header toc; | |
258 int i, okay; | |
259 struct ioc_read_toc_entry entry; | |
260 struct cd_toc_entry data; | |
261 | |
262 okay = 0; | |
263 if ( SDL_SYS_CDioctl(cdrom->id, CDIOREADTOCHEADER, &toc) == 0 ) { | |
264 cdrom->numtracks = toc.ending_track-toc.starting_track+1; | |
265 if ( cdrom->numtracks > SDL_MAX_TRACKS ) { | |
266 cdrom->numtracks = SDL_MAX_TRACKS; | |
267 } | |
268 /* Read all the track TOC entries */ | |
269 for ( i=0; i<=cdrom->numtracks; ++i ) { | |
270 if ( i == cdrom->numtracks ) { | |
271 cdrom->track[i].id = 0xAA; /* CDROM_LEADOUT */ | |
272 } else { | |
273 cdrom->track[i].id = toc.starting_track+i; | |
274 } | |
275 entry.starting_track = cdrom->track[i].id; | |
276 entry.address_format = CD_MSF_FORMAT; | |
277 entry.data_len = sizeof(data); | |
278 entry.data = &data; | |
279 if ( SDL_SYS_CDioctl(cdrom->id, CDIOREADTOCENTRYS, | |
280 &entry) < 0 ) { | |
281 break; | |
282 } else { | |
283 cdrom->track[i].type = data.control; | |
284 cdrom->track[i].offset = MSF_TO_FRAMES( | |
285 data.addr.msf.minute, | |
286 data.addr.msf.second, | |
287 data.addr.msf.frame); | |
288 cdrom->track[i].length = 0; | |
289 if ( i > 0 ) { | |
290 cdrom->track[i-1].length = | |
291 cdrom->track[i].offset- | |
292 cdrom->track[i-1].offset; | |
293 } | |
294 } | |
295 } | |
296 if ( i == (cdrom->numtracks+1) ) { | |
297 okay = 1; | |
298 } | |
299 } | |
300 return(okay ? 0 : -1); | |
301 } | |
302 | |
303 /* Get CD-ROM status */ | |
304 static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position) | |
305 { | |
306 CDstatus status; | |
307 struct ioc_toc_header toc; | |
308 struct ioc_read_subchannel info; | |
309 struct cd_sub_channel_info data; | |
310 | |
311 info.address_format = CD_MSF_FORMAT; | |
312 info.data_format = CD_CURRENT_POSITION; | |
313 info.track = 0; | |
314 info.data_len = sizeof(data); | |
315 info.data = &data; | |
316 if ( ioctl(cdrom->id, CDIOCREADSUBCHANNEL, &info) < 0 ) { | |
317 if ( ERRNO_TRAYEMPTY(errno) ) { | |
318 status = CD_TRAYEMPTY; | |
319 } else { | |
320 status = CD_ERROR; | |
321 } | |
322 } else { | |
323 switch (data.header.audio_status) { | |
324 case CD_AS_AUDIO_INVALID: | |
325 case CD_AS_NO_STATUS: | |
326 /* Try to determine if there's a CD available */ | |
327 if (ioctl(cdrom->id,CDIOREADTOCHEADER,&toc)==0) | |
328 status = CD_STOPPED; | |
329 else | |
330 status = CD_TRAYEMPTY; | |
331 break; | |
332 case CD_AS_PLAY_COMPLETED: | |
333 status = CD_STOPPED; | |
334 break; | |
335 case CD_AS_PLAY_IN_PROGRESS: | |
336 status = CD_PLAYING; | |
337 break; | |
338 case CD_AS_PLAY_PAUSED: | |
339 status = CD_PAUSED; | |
340 break; | |
341 default: | |
342 status = CD_ERROR; | |
343 break; | |
344 } | |
345 } | |
346 if ( position ) { | |
347 if ( status == CD_PLAYING || (status == CD_PAUSED) ) { | |
348 *position = MSF_TO_FRAMES( | |
349 data.what.position.absaddr.msf.minute, | |
350 data.what.position.absaddr.msf.second, | |
351 data.what.position.absaddr.msf.frame); | |
352 } else { | |
353 *position = 0; | |
354 } | |
355 } | |
356 return(status); | |
357 } | |
358 | |
359 /* Start play */ | |
360 static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length) | |
361 { | |
362 struct ioc_play_msf playtime; | |
363 | |
364 FRAMES_TO_MSF(start, | |
365 &playtime.start_m, &playtime.start_s, &playtime.start_f); | |
366 FRAMES_TO_MSF(start+length, | |
367 &playtime.end_m, &playtime.end_s, &playtime.end_f); | |
368 #ifdef DEBUG_CDROM | |
369 fprintf(stderr, "Trying to play from %d:%d:%d to %d:%d:%d\n", | |
370 playtime.start_m, playtime.start_s, playtime.start_f, | |
371 playtime.end_m, playtime.end_s, playtime.end_f); | |
372 #endif | |
373 ioctl(cdrom->id, CDIOCSTART, 0); | |
374 return(SDL_SYS_CDioctl(cdrom->id, CDIOCPLAYMSF, &playtime)); | |
375 } | |
376 | |
377 /* Pause play */ | |
378 static int SDL_SYS_CDPause(SDL_CD *cdrom) | |
379 { | |
380 return(SDL_SYS_CDioctl(cdrom->id, CDIOCPAUSE, 0)); | |
381 } | |
382 | |
383 /* Resume play */ | |
384 static int SDL_SYS_CDResume(SDL_CD *cdrom) | |
385 { | |
386 return(SDL_SYS_CDioctl(cdrom->id, CDIOCRESUME, 0)); | |
387 } | |
388 | |
389 /* Stop play */ | |
390 static int SDL_SYS_CDStop(SDL_CD *cdrom) | |
391 { | |
392 return(SDL_SYS_CDioctl(cdrom->id, CDIOCSTOP, 0)); | |
393 } | |
394 | |
395 /* Eject the CD-ROM */ | |
396 static int SDL_SYS_CDEject(SDL_CD *cdrom) | |
397 { | |
398 return(SDL_SYS_CDioctl(cdrom->id, CDIOCEJECT, 0)); | |
399 } | |
400 | |
401 /* Close the CD-ROM handle */ | |
402 static void SDL_SYS_CDClose(SDL_CD *cdrom) | |
403 { | |
404 close(cdrom->id); | |
405 } | |
406 | |
407 void SDL_SYS_CDQuit(void) | |
408 { | |
409 int i; | |
410 | |
411 if ( SDL_numcds > 0 ) { | |
412 for ( i=0; i<SDL_numcds; ++i ) { | |
413 free(SDL_cdlist[i]); | |
414 } | |
415 SDL_numcds = 0; | |
416 } | |
417 } | |
418 |