[17] | 1 | /* -*- mode: C; tab-width:8; c-basic-offset:8 -*- |
---|
| 2 | * vi:set ts=8: |
---|
| 3 | * |
---|
| 4 | * lin_dsp.c |
---|
| 5 | * |
---|
| 6 | * functions related to the aquisition and management of /dev/dsp under |
---|
| 7 | * linux. |
---|
| 8 | * |
---|
| 9 | */ |
---|
| 10 | #include "al_siteconfig.h" |
---|
| 11 | |
---|
| 12 | #include <AL/al.h> |
---|
| 13 | #include <AL/alext.h> |
---|
| 14 | #include <errno.h> |
---|
| 15 | #include <fcntl.h> |
---|
| 16 | #include <linux/soundcard.h> |
---|
| 17 | #include <stdio.h> |
---|
| 18 | #include <stdlib.h> |
---|
| 19 | #include <string.h> |
---|
| 20 | #include <sys/ioctl.h> |
---|
| 21 | #include <sys/mman.h> |
---|
| 22 | #include <sys/stat.h> |
---|
| 23 | #include <sys/time.h> |
---|
| 24 | #include <sys/types.h> |
---|
| 25 | #include <unistd.h> |
---|
| 26 | |
---|
| 27 | #include "al_config.h" |
---|
| 28 | #include "al_main.h" |
---|
| 29 | #include "al_debug.h" |
---|
| 30 | #include "alc/alc_context.h" |
---|
| 31 | #include "backends/alc_backend.h" |
---|
| 32 | |
---|
| 33 | #ifdef WORDS_BIGENDIAN |
---|
| 34 | #define AFMT_S16 AFMT_S16_BE |
---|
| 35 | #else |
---|
| 36 | #define AFMT_S16 AFMT_S16_LE |
---|
| 37 | #endif /* WORDS_BIGENDIAN */ |
---|
| 38 | |
---|
| 39 | /* |
---|
| 40 | * DONTCARE was (1 << 16), but that breaks 4Front's commercial drivers. |
---|
| 41 | * According to their reference manual, it musts be (2 << 16) or higher |
---|
| 42 | * or it is ignored, which is not good. Thanks to 4Front for tracking this |
---|
| 43 | * down. --ryan. |
---|
| 44 | */ |
---|
| 45 | #define DONTCARE ( 8 << 16) |
---|
| 46 | |
---|
| 47 | /* convert an alc channel to a linux dsp channel */ |
---|
| 48 | static int alcChannel_to_dsp_channel(ALCenum alcc); |
---|
| 49 | |
---|
| 50 | /* /dev/dsp variables */ |
---|
| 51 | static fd_set dsp_fd_set; |
---|
| 52 | static int mixer_fd = -1; /* /dev/mixer file read/write descriptor */ |
---|
| 53 | static ALboolean use_select = AL_TRUE; |
---|
| 54 | |
---|
| 55 | /* gets user prefered path */ |
---|
| 56 | static const char *lin_getwritepath(void); |
---|
| 57 | static const char *lin_getreadpath(void); |
---|
| 58 | static int aquire_read(void); |
---|
| 59 | static int grab_mixerfd(void); |
---|
| 60 | static int try_to_open(const char **paths, int n_paths, const char **used_path, int mode); |
---|
| 61 | |
---|
| 62 | /* set the params associated with a file descriptor */ |
---|
| 63 | static int set_fd(int dsp_fd, ALboolean readable, |
---|
| 64 | ALuint *bufsiz, |
---|
| 65 | ALuint *fmt, |
---|
| 66 | ALuint *speed, |
---|
| 67 | ALuint *channels); |
---|
| 68 | |
---|
| 69 | |
---|
| 70 | /* convert the format channel from /dev/dsp to openal format */ |
---|
| 71 | static int LIN2ALFMT(int fmt, int channels) |
---|
| 72 | { |
---|
| 73 | switch(fmt) |
---|
| 74 | { |
---|
| 75 | case AFMT_U8: |
---|
| 76 | switch(channels) |
---|
| 77 | { |
---|
| 78 | case 1: return AL_FORMAT_MONO8; |
---|
| 79 | case 2: return AL_FORMAT_STEREO8; |
---|
| 80 | case 4: return AL_FORMAT_QUAD8_LOKI; |
---|
| 81 | default: return -1; |
---|
| 82 | } |
---|
| 83 | case AFMT_S16: |
---|
| 84 | switch(channels) |
---|
| 85 | { |
---|
| 86 | case 1: return AL_FORMAT_MONO16; |
---|
| 87 | case 2: return AL_FORMAT_STEREO16; |
---|
| 88 | case 4: return AL_FORMAT_QUAD16_LOKI; |
---|
| 89 | default: return -1; |
---|
| 90 | } |
---|
| 91 | default: |
---|
| 92 | #ifdef DEBUG_MAXIMUS |
---|
| 93 | fprintf(stderr, "unsupported dsp format\n"); |
---|
| 94 | #endif |
---|
| 95 | return -1; |
---|
| 96 | } |
---|
| 97 | } |
---|
| 98 | |
---|
| 99 | /* convert the format channel from openal to /dev/dsp format */ |
---|
| 100 | static int AL2LINFMT(int fmt) |
---|
| 101 | { |
---|
| 102 | switch(fmt) { |
---|
| 103 | case AL_FORMAT_MONO16: |
---|
| 104 | case AL_FORMAT_STEREO16: |
---|
| 105 | case AL_FORMAT_QUAD16_LOKI: |
---|
| 106 | return AFMT_S16; |
---|
| 107 | break; |
---|
| 108 | case AL_FORMAT_MONO8: |
---|
| 109 | case AL_FORMAT_STEREO8: |
---|
| 110 | case AL_FORMAT_QUAD8_LOKI: |
---|
| 111 | return AFMT_U8; |
---|
| 112 | break; |
---|
| 113 | default: |
---|
| 114 | #ifdef DEBUG_MAXIMUS |
---|
| 115 | fprintf(stderr, "unknown format 0x%x\n", fmt); |
---|
| 116 | #endif |
---|
| 117 | break; |
---|
| 118 | } |
---|
| 119 | |
---|
| 120 | return -1; |
---|
| 121 | } |
---|
| 122 | |
---|
| 123 | |
---|
| 124 | /* |
---|
| 125 | * Disable non-blocking on a file descriptor. Returns non-zero on |
---|
| 126 | * success, zero on failure. --ryan. |
---|
| 127 | */ |
---|
| 128 | static int toggle_nonblock(int fd, int state) |
---|
| 129 | { |
---|
| 130 | int retval = 0; |
---|
| 131 | int flags = fcntl(fd, F_GETFL); |
---|
| 132 | if (flags != -1) { |
---|
| 133 | if (state) { |
---|
| 134 | flags |= O_NONBLOCK; |
---|
| 135 | } else { |
---|
| 136 | flags &= ~O_NONBLOCK; |
---|
| 137 | } |
---|
| 138 | |
---|
| 139 | if(fcntl(fd, F_SETFL, flags) != -1) { |
---|
| 140 | retval = 1; |
---|
| 141 | } |
---|
| 142 | } |
---|
| 143 | |
---|
| 144 | if (!retval) { |
---|
| 145 | perror("fcntl"); |
---|
| 146 | } |
---|
| 147 | |
---|
| 148 | return(retval); |
---|
| 149 | } |
---|
| 150 | |
---|
| 151 | |
---|
| 152 | /* |
---|
| 153 | * |
---|
| 154 | * Format of divisor is bit field where: |
---|
| 155 | * |
---|
| 156 | * |
---|
| 157 | * MSHW LSHW |
---|
| 158 | * [ some big value | x ] |
---|
| 159 | * |
---|
| 160 | * where x is translated into 2^x, and used as the |
---|
| 161 | * dma buffer size. |
---|
| 162 | * |
---|
| 163 | */ |
---|
| 164 | static void *grab_write_native(void) |
---|
| 165 | { |
---|
| 166 | static int write_fd; |
---|
| 167 | Rcvar rc_use_select; |
---|
| 168 | const char *writepath = NULL; |
---|
| 169 | int divisor = DONTCARE | _alSpot( _ALC_DEF_BUFSIZ ); |
---|
| 170 | const char *tried_paths[] = { |
---|
| 171 | "", |
---|
| 172 | "/dev/sound/dsp", |
---|
| 173 | "/dev/dsp" |
---|
| 174 | }; |
---|
| 175 | tried_paths[0] = lin_getwritepath(); |
---|
| 176 | write_fd = try_to_open(tried_paths, 3, &writepath, O_WRONLY | O_NONBLOCK); |
---|
| 177 | if(write_fd < 0) { |
---|
| 178 | perror("open /dev/[sound/]dsp"); |
---|
| 179 | return NULL; |
---|
| 180 | } |
---|
| 181 | |
---|
| 182 | if(ioctl(write_fd, SNDCTL_DSP_SETFRAGMENT, &divisor) < 0) { |
---|
| 183 | perror("ioctl SETFRAGMENT grab"); |
---|
| 184 | } |
---|
| 185 | |
---|
| 186 | toggle_nonblock(write_fd, 0); |
---|
| 187 | |
---|
| 188 | /* now get mixer_fd */ |
---|
| 189 | mixer_fd = grab_mixerfd(); |
---|
| 190 | |
---|
| 191 | /* |
---|
| 192 | * Some drivers, ie aurreal ones, don't implemented select. |
---|
| 193 | * I like to use select, as it ensures that we don't hang on |
---|
| 194 | * a bum write forever, but if you're in the unadmirable position |
---|
| 195 | * of having one of these cards I'm sure it's a small comfort. |
---|
| 196 | * |
---|
| 197 | * So, we have a special alrc var: native-use-select, that, if |
---|
| 198 | * set to #f, causes us to not do a select on the fd before |
---|
| 199 | * writing to it. |
---|
| 200 | */ |
---|
| 201 | use_select = AL_TRUE; |
---|
| 202 | |
---|
| 203 | rc_use_select = rc_lookup("native-use-select"); |
---|
| 204 | if(rc_use_select != NULL) { |
---|
| 205 | if(rc_type(rc_use_select) == ALRC_BOOL) { |
---|
| 206 | use_select = rc_tobool(rc_use_select); |
---|
| 207 | } |
---|
| 208 | } |
---|
| 209 | |
---|
| 210 | #ifdef DEBUG |
---|
| 211 | fprintf(stderr, "grab_native: (path %s fd %d)\n", writepath, write_fd); |
---|
| 212 | #endif |
---|
| 213 | |
---|
| 214 | return &write_fd; |
---|
| 215 | } |
---|
| 216 | |
---|
| 217 | /* |
---|
| 218 | * |
---|
| 219 | * Format of divisor is bit field where: |
---|
| 220 | * |
---|
| 221 | * |
---|
| 222 | * MSHW LSHW |
---|
| 223 | * [ some big value | x ] |
---|
| 224 | * |
---|
| 225 | * where x is translated into 2^x, and used as the |
---|
| 226 | * dma buffer size. |
---|
| 227 | * |
---|
| 228 | */ |
---|
| 229 | static void *grab_read_native(void) |
---|
| 230 | { |
---|
| 231 | static int read_fd; |
---|
| 232 | |
---|
| 233 | read_fd = aquire_read(); |
---|
| 234 | if( read_fd < 0) { |
---|
| 235 | return NULL; |
---|
| 236 | } |
---|
| 237 | |
---|
| 238 | return &read_fd; |
---|
| 239 | } |
---|
| 240 | |
---|
| 241 | void * |
---|
| 242 | alcBackendOpenNative_( ALC_OpenMode mode ) |
---|
| 243 | { |
---|
| 244 | return mode == ALC_OPEN_INPUT_ ? grab_read_native() : grab_write_native(); |
---|
| 245 | } |
---|
| 246 | |
---|
| 247 | static int grab_mixerfd(void) { |
---|
| 248 | const char *tried_paths[] = { |
---|
| 249 | "/dev/sound/mixer", |
---|
| 250 | "/dev/mixer" |
---|
| 251 | }; |
---|
| 252 | mixer_fd = try_to_open(tried_paths, 2, NULL, O_WRONLY | O_NONBLOCK); |
---|
| 253 | |
---|
| 254 | if(mixer_fd > 0) { |
---|
| 255 | toggle_nonblock(mixer_fd, 0); |
---|
| 256 | return mixer_fd; |
---|
| 257 | } else { |
---|
| 258 | perror("open /dev/[sound/]mixer"); |
---|
| 259 | } |
---|
| 260 | |
---|
| 261 | return -1; |
---|
| 262 | } |
---|
| 263 | |
---|
| 264 | void release_native(void *handle) { |
---|
| 265 | int handle_fd; |
---|
| 266 | |
---|
| 267 | if(handle == NULL) { |
---|
| 268 | return; |
---|
| 269 | } |
---|
| 270 | |
---|
| 271 | handle_fd = *(int *) handle; |
---|
| 272 | |
---|
| 273 | if(ioctl(handle_fd, SNDCTL_DSP_RESET) < 0) { |
---|
| 274 | #ifdef DEBUG |
---|
| 275 | fprintf(stderr, "Couldn't reset dsp\n"); |
---|
| 276 | #endif |
---|
| 277 | } |
---|
| 278 | |
---|
| 279 | ioctl(handle_fd, SNDCTL_DSP_SYNC, NULL); |
---|
| 280 | if((close(handle_fd) < 0) || (close(mixer_fd) < 0)) { |
---|
| 281 | return; |
---|
| 282 | } |
---|
| 283 | |
---|
| 284 | *(int *) handle = -1; |
---|
| 285 | mixer_fd = -1; |
---|
| 286 | |
---|
| 287 | return; |
---|
| 288 | } |
---|
| 289 | |
---|
| 290 | ALfloat |
---|
| 291 | get_nativechannel(UNUSED(void *handle), ALuint channel) |
---|
| 292 | { |
---|
| 293 | int request = alcChannel_to_dsp_channel(channel); |
---|
| 294 | int retval; |
---|
| 295 | if(ioctl(mixer_fd, MIXER_READ(request), &retval) < 0) { |
---|
| 296 | return -1; |
---|
| 297 | } |
---|
| 298 | return (retval >> 8) / 100.0; |
---|
| 299 | } |
---|
| 300 | |
---|
| 301 | |
---|
| 302 | /* |
---|
| 303 | * Okay: |
---|
| 304 | * |
---|
| 305 | * Set audio channel expects an integer, in the range of |
---|
| 306 | * 0 - 100. But wait! It expects the integer to be |
---|
| 307 | * partitioned into a 16bit empty, L/R channel pair (high bits left, |
---|
| 308 | * low bits right), each 8 bit pair in the range 0 - 100. |
---|
| 309 | * |
---|
| 310 | * Kludgey, and obviously not the right way to do this |
---|
| 311 | */ |
---|
| 312 | int set_nativechannel(UNUSED(void *handle), ALuint channel, ALfloat volume) { |
---|
| 313 | int request = alcChannel_to_dsp_channel(channel); |
---|
| 314 | int unnormalizedvolume; |
---|
| 315 | |
---|
| 316 | unnormalizedvolume = volume * 100; |
---|
| 317 | unnormalizedvolume <<= 8; |
---|
| 318 | unnormalizedvolume += (volume * 100); |
---|
| 319 | |
---|
| 320 | if(ioctl(mixer_fd, MIXER_WRITE(request), &unnormalizedvolume) < 0) { |
---|
| 321 | return -1; |
---|
| 322 | } |
---|
| 323 | |
---|
| 324 | return 0; |
---|
| 325 | } |
---|
| 326 | |
---|
| 327 | /* convert the mixer channel from ALC to /dev/mixer format */ |
---|
| 328 | static int alcChannel_to_dsp_channel(ALCenum alcc) { |
---|
| 329 | switch(alcc) { |
---|
| 330 | case ALC_CHAN_MAIN_LOKI: return SOUND_MIXER_VOLUME; |
---|
| 331 | case ALC_CHAN_CD_LOKI: return SOUND_MIXER_CD; |
---|
| 332 | case ALC_CHAN_PCM_LOKI: return SOUND_MIXER_PCM; |
---|
| 333 | default: return -1; |
---|
| 334 | } |
---|
| 335 | } |
---|
| 336 | |
---|
| 337 | void pause_nativedevice(void *handle) { |
---|
| 338 | int fd; |
---|
| 339 | |
---|
| 340 | if(handle == NULL) { |
---|
| 341 | return; |
---|
| 342 | } |
---|
| 343 | |
---|
| 344 | fd = *(int *) handle; |
---|
| 345 | |
---|
| 346 | toggle_nonblock(fd, 1); |
---|
| 347 | |
---|
| 348 | #if 0 |
---|
| 349 | if(ioctl(fd, SNDCTL_DSP_POST, 0) == -1) { |
---|
| 350 | perror("ioctl"); |
---|
| 351 | } |
---|
| 352 | #endif |
---|
| 353 | |
---|
| 354 | return; |
---|
| 355 | } |
---|
| 356 | |
---|
| 357 | void resume_nativedevice(void *handle) { |
---|
| 358 | int fd; |
---|
| 359 | |
---|
| 360 | if(handle == NULL) { |
---|
| 361 | return; |
---|
| 362 | } |
---|
| 363 | |
---|
| 364 | fd = *(int *) handle; |
---|
| 365 | |
---|
| 366 | toggle_nonblock(fd, 0); |
---|
| 367 | |
---|
| 368 | /* |
---|
| 369 | if(ioctl(fd, SNDCTL_DSP_SYNC, 0) == -1) { |
---|
| 370 | perror("ioctl"); |
---|
| 371 | } |
---|
| 372 | */ |
---|
| 373 | |
---|
| 374 | return; |
---|
| 375 | } |
---|
| 376 | |
---|
| 377 | static const char *lin_getwritepath(void) { |
---|
| 378 | Rcvar devdsp_path = rc_lookup("lin-dsp-path"); |
---|
| 379 | static char retval[65]; /* FIXME */ |
---|
| 380 | |
---|
| 381 | if(devdsp_path == NULL) { |
---|
| 382 | return NULL; |
---|
| 383 | } |
---|
| 384 | |
---|
| 385 | switch(rc_type(devdsp_path)) { |
---|
| 386 | case ALRC_STRING: |
---|
| 387 | rc_tostr0(devdsp_path, retval, 64); |
---|
| 388 | break; |
---|
| 389 | default: |
---|
| 390 | return NULL; |
---|
| 391 | } |
---|
| 392 | |
---|
| 393 | return retval; |
---|
| 394 | } |
---|
| 395 | |
---|
| 396 | static const char *lin_getreadpath(void) { |
---|
| 397 | Rcvar devdsp_path = rc_lookup("lin-dsp-read-path"); |
---|
| 398 | static char retval[65]; /* FIXME */ |
---|
| 399 | |
---|
| 400 | if(devdsp_path == NULL) { |
---|
| 401 | /* |
---|
| 402 | * no explicit read path? try the default |
---|
| 403 | * path. |
---|
| 404 | */ |
---|
| 405 | devdsp_path = rc_lookup("lin-dsp-path"); |
---|
| 406 | } |
---|
| 407 | |
---|
| 408 | if(devdsp_path == NULL) { |
---|
| 409 | return NULL; |
---|
| 410 | } |
---|
| 411 | |
---|
| 412 | switch(rc_type(devdsp_path)) { |
---|
| 413 | case ALRC_STRING: |
---|
| 414 | rc_tostr0(devdsp_path, retval, 64); |
---|
| 415 | break; |
---|
| 416 | default: |
---|
| 417 | return NULL; |
---|
| 418 | } |
---|
| 419 | |
---|
| 420 | |
---|
| 421 | /* |
---|
| 422 | * stupid. /dev/dsp1 cannot be used for reading, |
---|
| 423 | * only /dev/dsp |
---|
| 424 | */ |
---|
| 425 | if(retval[strlen(retval) - 1] == '1') { |
---|
| 426 | retval[strlen(retval) - 1] = '\0'; |
---|
| 427 | } |
---|
| 428 | |
---|
| 429 | return retval; |
---|
| 430 | } |
---|
| 431 | |
---|
| 432 | /* capture data from the audio device */ |
---|
| 433 | ALsizei capture_nativedevice(void *handle, |
---|
| 434 | void *capture_buffer, |
---|
| 435 | int bufsiz) { |
---|
| 436 | int read_fd = *(int *)handle; |
---|
| 437 | int retval; |
---|
| 438 | |
---|
| 439 | retval = read(read_fd, capture_buffer, bufsiz); |
---|
| 440 | return retval > 0 ? retval : 0; |
---|
| 441 | } |
---|
| 442 | |
---|
| 443 | static int aquire_read(void) { |
---|
| 444 | int read_fd; |
---|
| 445 | const char *readpath = NULL; |
---|
| 446 | int divisor = _alSpot(_ALC_DEF_BUFSIZ) | (1<<16); |
---|
| 447 | const char *tried_paths[] = { |
---|
| 448 | "", |
---|
| 449 | "/dev/sound/dsp", |
---|
| 450 | "/dev/dsp" |
---|
| 451 | }; |
---|
| 452 | tried_paths[0] = lin_getreadpath(); |
---|
| 453 | read_fd = try_to_open(tried_paths, 3, &readpath, O_RDONLY | O_NONBLOCK); |
---|
| 454 | if(read_fd >= 0) { |
---|
| 455 | #if 0 /* Reads should be non-blocking */ |
---|
| 456 | toggle_nonblock(read_fd, 0); |
---|
| 457 | #endif |
---|
| 458 | if(ioctl(read_fd, SNDCTL_DSP_SETFRAGMENT, &divisor) < 0) { |
---|
| 459 | perror("ioctl SETFRAGMENT"); |
---|
| 460 | } |
---|
| 461 | } |
---|
| 462 | |
---|
| 463 | return read_fd; |
---|
| 464 | } |
---|
| 465 | |
---|
| 466 | static ALboolean set_write_native(UNUSED(void *handle), |
---|
| 467 | ALuint *bufsiz, |
---|
| 468 | ALenum *fmt, |
---|
| 469 | ALuint *speed) { |
---|
| 470 | int write_fd = *(int *)handle; |
---|
| 471 | ALuint linformat; |
---|
| 472 | ALuint channels = _alGetChannelsFromFormat(*fmt); |
---|
| 473 | int err; |
---|
| 474 | |
---|
| 475 | if(write_fd < 0) { |
---|
| 476 | return AL_FALSE; |
---|
| 477 | } |
---|
| 478 | |
---|
| 479 | linformat = AL2LINFMT(*fmt); |
---|
| 480 | |
---|
| 481 | err = set_fd(write_fd, AL_FALSE, bufsiz, &linformat, speed, &channels); |
---|
| 482 | if(err < 0) { |
---|
| 483 | #ifdef DEBUG |
---|
| 484 | fprintf(stderr, "Could not do write_fd\n"); |
---|
| 485 | #endif |
---|
| 486 | return AL_FALSE; |
---|
| 487 | } |
---|
| 488 | |
---|
| 489 | /* set format for caller */ |
---|
| 490 | *fmt = LIN2ALFMT(linformat, channels); |
---|
| 491 | |
---|
| 492 | return AL_TRUE; |
---|
| 493 | } |
---|
| 494 | |
---|
| 495 | static ALboolean set_read_native(UNUSED(void *handle), |
---|
| 496 | ALuint *bufsiz, |
---|
| 497 | ALenum *fmt, |
---|
| 498 | ALuint *speed) { |
---|
| 499 | int read_fd = *(int *)handle; |
---|
| 500 | ALuint linformat; |
---|
| 501 | ALuint channels = 1; |
---|
| 502 | |
---|
| 503 | linformat = AL2LINFMT(*fmt); |
---|
| 504 | |
---|
| 505 | if(set_fd(read_fd, AL_TRUE, bufsiz, &linformat, speed, &channels) >= 0) { |
---|
| 506 | /* set format for caller */ |
---|
| 507 | *fmt = LIN2ALFMT(linformat, channels); |
---|
| 508 | |
---|
| 509 | return AL_TRUE; |
---|
| 510 | } |
---|
| 511 | |
---|
| 512 | return AL_FALSE; |
---|
| 513 | } |
---|
| 514 | |
---|
| 515 | ALboolean |
---|
| 516 | alcBackendSetAttributesNative_(ALC_OpenMode mode, void *handle, ALuint *bufsiz, ALenum *fmt, ALuint *speed) |
---|
| 517 | { |
---|
| 518 | return mode == ALC_OPEN_INPUT_ ? |
---|
| 519 | set_read_native(handle, bufsiz, fmt, speed) : |
---|
| 520 | set_write_native(handle, bufsiz, fmt, speed); |
---|
| 521 | } |
---|
| 522 | |
---|
| 523 | void native_blitbuffer(void *handle, void *dataptr, int bytes_to_write) { |
---|
| 524 | struct timeval tv = { 0, 800000 }; /* at most .8 secs */ |
---|
| 525 | int iterator = 0; |
---|
| 526 | int err; |
---|
| 527 | int fd; |
---|
| 528 | |
---|
| 529 | if(handle == NULL) { |
---|
| 530 | return; |
---|
| 531 | } |
---|
| 532 | |
---|
| 533 | fd = *(int *) handle; |
---|
| 534 | |
---|
| 535 | assert( fd >= 0 ); |
---|
| 536 | |
---|
| 537 | for(iterator = bytes_to_write; iterator > 0; ) { |
---|
| 538 | FD_ZERO(&dsp_fd_set); |
---|
| 539 | FD_SET(fd, &dsp_fd_set); |
---|
| 540 | |
---|
| 541 | if(use_select == AL_TRUE) { |
---|
| 542 | err = select(fd + 1, NULL, &dsp_fd_set, NULL, &tv); |
---|
| 543 | if(FD_ISSET(fd, &dsp_fd_set) == 0) { |
---|
| 544 | /* |
---|
| 545 | * error or timeout occured, don't try |
---|
| 546 | * and write. |
---|
| 547 | */ |
---|
| 548 | fprintf(stderr, |
---|
| 549 | "native_blitbuffer: select error occured\n"); |
---|
| 550 | return; |
---|
| 551 | } |
---|
| 552 | } |
---|
| 553 | |
---|
| 554 | assert(iterator > 0); |
---|
| 555 | assert(iterator <= bytes_to_write); |
---|
| 556 | |
---|
| 557 | err = write(fd, |
---|
| 558 | (char *) dataptr + bytes_to_write - iterator, |
---|
| 559 | iterator); |
---|
| 560 | |
---|
| 561 | if(err < 0) { |
---|
| 562 | #ifdef DEBUG_MAXIMUS |
---|
| 563 | fprintf( stderr, "write error: ( fd %d error %s )\n", |
---|
| 564 | fd, strerror(err)); |
---|
| 565 | #endif |
---|
| 566 | assert( 0 ); |
---|
| 567 | return; |
---|
| 568 | } |
---|
| 569 | |
---|
| 570 | iterator -= err; |
---|
| 571 | }; |
---|
| 572 | |
---|
| 573 | return; |
---|
| 574 | } |
---|
| 575 | |
---|
| 576 | static int set_fd(int dsp_fd, ALboolean readable, |
---|
| 577 | ALuint *bufsiz, |
---|
| 578 | ALuint *fmt, |
---|
| 579 | ALuint *speed, |
---|
| 580 | ALuint *channels) |
---|
| 581 | { |
---|
| 582 | struct audio_buf_info info; |
---|
| 583 | |
---|
| 584 | if(dsp_fd < 0) { |
---|
| 585 | return -1; |
---|
| 586 | } |
---|
| 587 | |
---|
| 588 | #ifdef DEBUG |
---|
| 589 | fprintf( stderr, "set_fd in: bufsiz %d fmt 0x%x speed %d channels %d\n", |
---|
| 590 | *bufsiz, *fmt, *speed, *channels ); |
---|
| 591 | #endif |
---|
| 592 | |
---|
| 593 | |
---|
| 594 | #if 0 /* This code breaks 4Front's commercial drivers. Just say no. --ryan. */ |
---|
| 595 | { |
---|
| 596 | int divisor = DONTCARE | _alSpot( *bufsiz ); |
---|
| 597 | if( ioctl(dsp_fd, SNDCTL_DSP_SETFRAGMENT, &divisor ) < 0) { |
---|
| 598 | #ifdef DEBUG |
---|
| 599 | perror("ioctl SETFRAGMENT"); |
---|
| 600 | #endif |
---|
| 601 | } |
---|
| 602 | } |
---|
| 603 | #endif |
---|
| 604 | |
---|
| 605 | |
---|
| 606 | /* reset card defaults */ |
---|
| 607 | if(ioctl(dsp_fd, SNDCTL_DSP_RESET, NULL) < 0) { |
---|
| 608 | #ifdef DEBUG |
---|
| 609 | perror("set_devsp reset ioctl"); |
---|
| 610 | #endif |
---|
| 611 | return AL_FALSE; |
---|
| 612 | } |
---|
| 613 | |
---|
| 614 | if(ioctl(dsp_fd, SNDCTL_DSP_SETFMT, fmt) < 0) { |
---|
| 615 | #ifdef DEBUG |
---|
| 616 | fprintf(stderr, "fmt %d\n", *fmt); |
---|
| 617 | perror("set_devsp format ioctl"); |
---|
| 618 | #endif |
---|
| 619 | return AL_FALSE; |
---|
| 620 | } |
---|
| 621 | |
---|
| 622 | if(ioctl(dsp_fd, SNDCTL_DSP_CHANNELS, channels)) { |
---|
| 623 | #ifdef DEBUG |
---|
| 624 | fprintf(stderr, "channels %d\n", *channels); |
---|
| 625 | perror("set_devsp channels ioctl"); |
---|
| 626 | #endif |
---|
| 627 | return AL_FALSE; |
---|
| 628 | } |
---|
| 629 | |
---|
| 630 | if( readable == AL_TRUE ) { |
---|
| 631 | /* |
---|
| 632 | * This is for reading. Don't really use |
---|
| 633 | * the speed argument. |
---|
| 634 | */ |
---|
| 635 | *speed = 16000; |
---|
| 636 | |
---|
| 637 | /* Try to set the speed (ignore value), then read it back */ |
---|
| 638 | ioctl(dsp_fd, SNDCTL_DSP_SPEED, speed); |
---|
| 639 | if (ioctl(dsp_fd, SOUND_PCM_READ_RATE, speed) < 0) { |
---|
| 640 | #ifdef DEBUG |
---|
| 641 | char errbuf[256]; |
---|
| 642 | |
---|
| 643 | snprintf(errbuf, sizeof(errbuf), "(fd %d speed %d)", dsp_fd, *speed); |
---|
| 644 | |
---|
| 645 | perror(errbuf); |
---|
| 646 | return AL_FALSE; |
---|
| 647 | #endif |
---|
| 648 | } |
---|
| 649 | /*printf("Set recording rate: %d\n", *speed);*/ |
---|
| 650 | |
---|
| 651 | } else { |
---|
| 652 | /* writable, set speed, otherwise no */ |
---|
| 653 | if(ioctl(dsp_fd, SNDCTL_DSP_SPEED, speed) < 0) { |
---|
| 654 | #ifdef DEBUG |
---|
| 655 | char errbuf[256]; |
---|
| 656 | |
---|
| 657 | snprintf(errbuf, sizeof(errbuf), "(fd %d speed %d)", dsp_fd, *speed); |
---|
| 658 | |
---|
| 659 | perror(errbuf); |
---|
| 660 | return AL_FALSE; |
---|
| 661 | #endif |
---|
| 662 | } |
---|
| 663 | |
---|
| 664 | if(ioctl(dsp_fd, SNDCTL_DSP_GETOSPACE, &info) < 0) { |
---|
| 665 | #ifdef DEBUG |
---|
| 666 | perror("ioctl SNDCTL_DSP_GETOSPACE"); |
---|
| 667 | #endif |
---|
| 668 | } |
---|
| 669 | |
---|
| 670 | /* set bufsiz correctly */ |
---|
| 671 | |
---|
| 672 | *bufsiz = info.fragsize; |
---|
| 673 | |
---|
| 674 | #ifdef DEBUG |
---|
| 675 | if( *bufsiz & (*bufsiz - 1) ) { |
---|
| 676 | fprintf( stderr, "Non power-of-two bufsiz %d\n", |
---|
| 677 | *bufsiz ); |
---|
| 678 | } |
---|
| 679 | #endif |
---|
| 680 | } |
---|
| 681 | |
---|
| 682 | #ifdef DEBUG |
---|
| 683 | fprintf( stderr, "set_fd out: bufsiz %d fmt 0x%x speed %d channels %d\n", |
---|
| 684 | *bufsiz, *fmt, *speed, *channels ); |
---|
| 685 | #endif |
---|
| 686 | |
---|
| 687 | return 0; |
---|
| 688 | } |
---|
| 689 | |
---|
| 690 | int try_to_open(const char **paths, int n_paths, const char **used_path, int mode) |
---|
| 691 | { |
---|
| 692 | int i, fd = -1; |
---|
| 693 | for(i = 0; i != n_paths; ++i) |
---|
| 694 | { |
---|
| 695 | if(paths[i]) |
---|
| 696 | { |
---|
| 697 | if(used_path) *used_path = paths[i]; |
---|
| 698 | fd = open(paths[i], mode); |
---|
| 699 | if(fd >= 0) return fd; |
---|
| 700 | } |
---|
| 701 | } |
---|
| 702 | return fd; |
---|
| 703 | } |
---|
| 704 | |
---|