Mercurial > audlegacy-plugins
comparison src/Output/OSS/audio.c @ 0:13389e613d67 trunk
[svn] - initial import of audacious-plugins tree (lots to do)
| author | nenolod |
|---|---|
| date | Mon, 18 Sep 2006 01:11:49 -0700 |
| parents | |
| children | 6303e3a8a6b8 |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 0:13389e613d67 |
|---|---|
| 1 /* BMP - Cross-platform multimedia player | |
| 2 * Copyright (C) 2003-2004 BMP development team. | |
| 3 * | |
| 4 * Based on XMMS: | |
| 5 * Copyright (C) 1998-2003 XMMS development team. | |
| 6 * | |
| 7 * This program is free software; you can redistribute it and/or modify | |
| 8 * it under the terms of the GNU General Public License as published by | |
| 9 * the Free Software Foundation; either version 2 of the License, or | |
| 10 * (at your option) any later version. | |
| 11 * | |
| 12 * This program is distributed in the hope that it will be useful, | |
| 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 15 * GNU General Public License for more details. | |
| 16 * | |
| 17 * You should have received a copy of the GNU General Public License | |
| 18 * along with this program; if not, write to the Free Software | |
| 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
| 20 */ | |
| 21 | |
| 22 #include <glib.h> | |
| 23 #include <libaudacious/util.h> | |
| 24 #include <string.h> | |
| 25 | |
| 26 #include <unistd.h> | |
| 27 #include <fcntl.h> | |
| 28 #include <errno.h> | |
| 29 #include <sys/ioctl.h> | |
| 30 #include <sys/time.h> | |
| 31 | |
| 32 #include "OSS.h" | |
| 33 | |
| 34 | |
| 35 #define NFRAGS 32 | |
| 36 | |
| 37 static gint fd = 0; | |
| 38 static char *buffer; | |
| 39 static gboolean going, prebuffer, paused, unpause, do_pause, remove_prebuffer; | |
| 40 static gint device_buffer_used, buffer_size, prebuffer_size, blk_size; | |
| 41 static gint rd_index = 0, wr_index = 0; | |
| 42 static gint output_time_offset = 0; | |
| 43 static guint64 written = 0, output_bytes = 0; | |
| 44 static gint flush; | |
| 45 static gint fragsize, device_buffer_size; | |
| 46 static gchar *device_name; | |
| 47 static GThread *buffer_thread; | |
| 48 static gboolean realtime, select_works; | |
| 49 | |
| 50 static int (*oss_convert_func) (void **data, int length); | |
| 51 static int (*oss_stereo_convert_func) (void **data, int length, int fmt); | |
| 52 | |
| 53 struct format_info { | |
| 54 union { | |
| 55 AFormat xmms; | |
| 56 int oss; | |
| 57 } format; | |
| 58 int frequency; | |
| 59 int channels; | |
| 60 int bps; | |
| 61 }; | |
| 62 | |
| 63 | |
| 64 /* | |
| 65 * The format of the data from the input plugin | |
| 66 * This will never change during a song. | |
| 67 */ | |
| 68 struct format_info input; | |
| 69 | |
| 70 /* | |
| 71 * The format we get from the effect plugin. | |
| 72 * This will be different from input if the effect plugin does | |
| 73 * some kind of format conversion. | |
| 74 */ | |
| 75 struct format_info effect; | |
| 76 | |
| 77 /* | |
| 78 * The format of the data we actually send to the soundcard. | |
| 79 * This might be different from effect if we need to resample or do | |
| 80 * some other format conversion. | |
| 81 */ | |
| 82 struct format_info output; | |
| 83 | |
| 84 | |
| 85 static void | |
| 86 oss_calc_device_buffer_used(void) | |
| 87 { | |
| 88 audio_buf_info buf_info; | |
| 89 if (paused) | |
| 90 device_buffer_used = 0; | |
| 91 else if (!ioctl(fd, SNDCTL_DSP_GETOSPACE, &buf_info)) | |
| 92 device_buffer_used = | |
| 93 (buf_info.fragstotal * buf_info.fragsize) - buf_info.bytes; | |
| 94 } | |
| 95 | |
| 96 | |
| 97 static gint oss_downsample(gpointer ob, guint length, guint speed, | |
| 98 guint espeed); | |
| 99 | |
| 100 static int | |
| 101 oss_calc_bitrate(int oss_fmt, int rate, int channels) | |
| 102 { | |
| 103 int bitrate = rate * channels; | |
| 104 | |
| 105 if (oss_fmt == AFMT_U16_BE || oss_fmt == AFMT_U16_LE || | |
| 106 oss_fmt == AFMT_S16_BE || oss_fmt == AFMT_S16_LE) | |
| 107 bitrate *= 2; | |
| 108 | |
| 109 return bitrate; | |
| 110 } | |
| 111 | |
| 112 static int | |
| 113 oss_get_format(AFormat fmt) | |
| 114 { | |
| 115 int format = 0; | |
| 116 | |
| 117 switch (fmt) { | |
| 118 case FMT_U8: | |
| 119 format = AFMT_U8; | |
| 120 break; | |
| 121 case FMT_S8: | |
| 122 format = AFMT_S8; | |
| 123 break; | |
| 124 case FMT_U16_LE: | |
| 125 format = AFMT_U16_LE; | |
| 126 break; | |
| 127 case FMT_U16_BE: | |
| 128 format = AFMT_U16_BE; | |
| 129 break; | |
| 130 case FMT_U16_NE: | |
| 131 #if (G_BYTE_ORDER == G_BIG_ENDIAN) | |
| 132 format = AFMT_U16_BE; | |
| 133 #else | |
| 134 format = AFMT_U16_LE; | |
| 135 #endif | |
| 136 break; | |
| 137 case FMT_S16_LE: | |
| 138 format = AFMT_S16_LE; | |
| 139 break; | |
| 140 case FMT_S16_BE: | |
| 141 format = AFMT_S16_BE; | |
| 142 break; | |
| 143 case FMT_S16_NE: | |
| 144 #if (G_BYTE_ORDER == G_BIG_ENDIAN) | |
| 145 format = AFMT_S16_BE; | |
| 146 #else | |
| 147 format = AFMT_S16_LE; | |
| 148 #endif | |
| 149 break; | |
| 150 } | |
| 151 | |
| 152 return format; | |
| 153 } | |
| 154 | |
| 155 static void | |
| 156 oss_setup_format(AFormat fmt, int rate, int nch) | |
| 157 { | |
| 158 effect.format.xmms = fmt; | |
| 159 effect.frequency = rate; | |
| 160 effect.channels = nch; | |
| 161 effect.bps = oss_calc_bitrate(oss_get_format(fmt), rate, nch); | |
| 162 | |
| 163 output.format.oss = oss_get_format(fmt); | |
| 164 output.frequency = rate; | |
| 165 output.channels = nch; | |
| 166 | |
| 167 | |
| 168 fragsize = 0; | |
| 169 while ((1L << fragsize) < effect.bps / 25) | |
| 170 fragsize++; | |
| 171 fragsize--; | |
| 172 | |
| 173 device_buffer_size = ((1L << fragsize) * (NFRAGS + 1)); | |
| 174 | |
| 175 oss_set_audio_params(); | |
| 176 | |
| 177 output.bps = oss_calc_bitrate(output.format.oss, output.frequency, | |
| 178 output.channels); | |
| 179 } | |
| 180 | |
| 181 | |
| 182 gint | |
| 183 oss_get_written_time(void) | |
| 184 { | |
| 185 if (!going) | |
| 186 return 0; | |
| 187 return (written * 1000) / effect.bps; | |
| 188 } | |
| 189 | |
| 190 gint | |
| 191 oss_get_output_time(void) | |
| 192 { | |
| 193 guint64 bytes; | |
| 194 | |
| 195 if (!fd || !going) | |
| 196 return 0; | |
| 197 | |
| 198 if (realtime) | |
| 199 oss_calc_device_buffer_used(); | |
| 200 bytes = output_bytes < device_buffer_used ? | |
| 201 0 : output_bytes - device_buffer_used; | |
| 202 | |
| 203 return output_time_offset + ((bytes * 1000) / output.bps); | |
| 204 } | |
| 205 | |
| 206 static int | |
| 207 oss_used(void) | |
| 208 { | |
| 209 if (realtime) | |
| 210 return 0; | |
| 211 else { | |
| 212 if (wr_index >= rd_index) | |
| 213 return wr_index - rd_index; | |
| 214 return buffer_size - (rd_index - wr_index); | |
| 215 } | |
| 216 } | |
| 217 | |
| 218 gint | |
| 219 oss_playing(void) | |
| 220 { | |
| 221 if (!going) | |
| 222 return 0; | |
| 223 if (realtime) | |
| 224 oss_calc_device_buffer_used(); | |
| 225 if (!oss_used() && (device_buffer_used - (3 * blk_size)) <= 0) | |
| 226 return FALSE; | |
| 227 | |
| 228 return TRUE; | |
| 229 } | |
| 230 | |
| 231 gint | |
| 232 oss_free(void) | |
| 233 { | |
| 234 if (!realtime) { | |
| 235 if (remove_prebuffer && prebuffer) { | |
| 236 prebuffer = FALSE; | |
| 237 remove_prebuffer = FALSE; | |
| 238 } | |
| 239 if (prebuffer) | |
| 240 remove_prebuffer = TRUE; | |
| 241 | |
| 242 if (rd_index > wr_index) | |
| 243 return (rd_index - wr_index) - device_buffer_size - 1; | |
| 244 return (buffer_size - (wr_index - rd_index)) - device_buffer_size - 1; | |
| 245 } | |
| 246 else if (paused) | |
| 247 return 0; | |
| 248 else | |
| 249 return 1000000; | |
| 250 } | |
| 251 | |
| 252 static inline ssize_t | |
| 253 write_all(int fd, const void *buf, size_t count) | |
| 254 { | |
| 255 size_t done = 0; | |
| 256 do { | |
| 257 ssize_t n = write(fd, (gchar *) buf + done, count - done); | |
| 258 if (n == -1) { | |
| 259 if (errno == EINTR) | |
| 260 continue; | |
| 261 else | |
| 262 break; | |
| 263 } | |
| 264 done += n; | |
| 265 } while (count > done); | |
| 266 | |
| 267 return done; | |
| 268 } | |
| 269 | |
| 270 static void | |
| 271 oss_write_audio(gpointer data, int length) | |
| 272 { | |
| 273 | |
| 274 audio_buf_info abuf_info; | |
| 275 AFormat new_format; | |
| 276 int new_frequency, new_channels; | |
| 277 EffectPlugin *ep; | |
| 278 | |
| 279 new_format = input.format.xmms; | |
| 280 new_frequency = input.frequency; | |
| 281 new_channels = input.channels; | |
| 282 | |
| 283 | |
| 284 ep = get_current_effect_plugin(); | |
| 285 if (effects_enabled() && ep && ep->query_format) { | |
| 286 ep->query_format(&new_format, &new_frequency, &new_channels); | |
| 287 } | |
| 288 | |
| 289 if (new_format != effect.format.xmms || | |
| 290 new_frequency != effect.frequency || | |
| 291 new_channels != effect.channels) { | |
| 292 output_time_offset += (output_bytes * 1000) / output.bps; | |
| 293 output_bytes = 0; | |
| 294 close(fd); | |
| 295 fd = open(device_name, O_WRONLY); | |
| 296 oss_setup_format(new_format, new_frequency, new_channels); | |
| 297 } | |
| 298 if (effects_enabled() && ep && ep->mod_samples) | |
| 299 length = ep->mod_samples(&data, length, | |
| 300 input.format.xmms, | |
| 301 input.frequency, input.channels); | |
| 302 if (realtime && !ioctl(fd, SNDCTL_DSP_GETOSPACE, &abuf_info)) { | |
| 303 while (abuf_info.bytes < length) { | |
| 304 xmms_usleep(10000); | |
| 305 if (ioctl(fd, SNDCTL_DSP_GETOSPACE, &abuf_info)) | |
| 306 break; | |
| 307 } | |
| 308 } | |
| 309 | |
| 310 if (oss_convert_func != NULL) | |
| 311 length = oss_convert_func(&data, length); | |
| 312 | |
| 313 if (oss_stereo_convert_func != NULL) | |
| 314 length = oss_stereo_convert_func(&data, length, output.format.oss); | |
| 315 | |
| 316 if (effect.frequency == output.frequency) | |
| 317 output_bytes += write_all(fd, data, length); | |
| 318 else | |
| 319 output_bytes += oss_downsample(data, length, | |
| 320 effect.frequency, output.frequency); | |
| 321 } | |
| 322 | |
| 323 static void | |
| 324 swap_endian(guint16 * data, int length) | |
| 325 { | |
| 326 int i; | |
| 327 for (i = 0; i < length; i += 2, data++) | |
| 328 *data = GUINT16_SWAP_LE_BE(*data); | |
| 329 } | |
| 330 | |
| 331 #define NOT_NATIVE_ENDIAN ((IS_BIG_ENDIAN && \ | |
| 332 (output.format.oss == AFMT_S16_LE || \ | |
| 333 output.format.oss == AFMT_U16_LE)) || \ | |
| 334 (!IS_BIG_ENDIAN && \ | |
| 335 (output.format.oss == AFMT_S16_BE || \ | |
| 336 output.format.oss == AFMT_U16_BE))) | |
| 337 | |
| 338 | |
| 339 #define RESAMPLE_STEREO(sample_type) \ | |
| 340 do { \ | |
| 341 const gint shift = sizeof (sample_type); \ | |
| 342 gint i, in_samples, out_samples, x, delta; \ | |
| 343 sample_type *inptr = (sample_type *)ob, *outptr; \ | |
| 344 guint nlen = (((length >> shift) * espeed) / speed); \ | |
| 345 if (nlen == 0) \ | |
| 346 break; \ | |
| 347 nlen <<= shift; \ | |
| 348 if (NOT_NATIVE_ENDIAN) \ | |
| 349 swap_endian(ob, length); \ | |
| 350 if(nlen > nbuffer_size) \ | |
| 351 { \ | |
| 352 nbuffer = g_realloc(nbuffer, nlen); \ | |
| 353 nbuffer_size = nlen; \ | |
| 354 } \ | |
| 355 outptr = (sample_type *)nbuffer; \ | |
| 356 in_samples = length >> shift; \ | |
| 357 out_samples = nlen >> shift; \ | |
| 358 delta = (in_samples << 12) / out_samples; \ | |
| 359 for (x = 0, i = 0; i < out_samples; i++) \ | |
| 360 { \ | |
| 361 gint x1, frac; \ | |
| 362 x1 = (x >> 12) << 12; \ | |
| 363 frac = x - x1; \ | |
| 364 *outptr++ = \ | |
| 365 (sample_type) \ | |
| 366 ((inptr[(x1 >> 12) << 1] * \ | |
| 367 ((1<<12) - frac) + \ | |
| 368 inptr[((x1 >> 12) + 1) << 1] * \ | |
| 369 frac) >> 12); \ | |
| 370 *outptr++ = \ | |
| 371 (sample_type) \ | |
| 372 ((inptr[((x1 >> 12) << 1) + 1] * \ | |
| 373 ((1<<12) - frac) + \ | |
| 374 inptr[(((x1 >> 12) + 1) << 1) + 1] * \ | |
| 375 frac) >> 12); \ | |
| 376 x += delta; \ | |
| 377 } \ | |
| 378 if (NOT_NATIVE_ENDIAN) \ | |
| 379 swap_endian(nbuffer, nlen); \ | |
| 380 w = write_all(fd, nbuffer, nlen); \ | |
| 381 } while (0) | |
| 382 | |
| 383 | |
| 384 #define RESAMPLE_MONO(sample_type) \ | |
| 385 do { \ | |
| 386 const gint shift = sizeof (sample_type) - 1; \ | |
| 387 gint i, x, delta, in_samples, out_samples; \ | |
| 388 sample_type *inptr = (sample_type *)ob, *outptr; \ | |
| 389 guint nlen = (((length >> shift) * espeed) / speed); \ | |
| 390 if (nlen == 0) \ | |
| 391 break; \ | |
| 392 nlen <<= shift; \ | |
| 393 if (NOT_NATIVE_ENDIAN) \ | |
| 394 swap_endian(ob, length); \ | |
| 395 if(nlen > nbuffer_size) \ | |
| 396 { \ | |
| 397 nbuffer = g_realloc(nbuffer, nlen); \ | |
| 398 nbuffer_size = nlen; \ | |
| 399 } \ | |
| 400 outptr = (sample_type *)nbuffer; \ | |
| 401 in_samples = length >> shift; \ | |
| 402 out_samples = nlen >> shift; \ | |
| 403 delta = ((length >> shift) << 12) / out_samples; \ | |
| 404 for (x = 0, i = 0; i < out_samples; i++) \ | |
| 405 { \ | |
| 406 gint x1, frac; \ | |
| 407 x1 = (x >> 12) << 12; \ | |
| 408 frac = x - x1; \ | |
| 409 *outptr++ = \ | |
| 410 (sample_type) \ | |
| 411 ((inptr[x1 >> 12] * ((1<<12) - frac) + \ | |
| 412 inptr[(x1 >> 12) + 1] * frac) >> 12); \ | |
| 413 x += delta; \ | |
| 414 } \ | |
| 415 if (NOT_NATIVE_ENDIAN) \ | |
| 416 swap_endian(nbuffer, nlen); \ | |
| 417 w = write_all(fd, nbuffer, nlen); \ | |
| 418 } while (0) | |
| 419 | |
| 420 | |
| 421 static gint | |
| 422 oss_downsample(gpointer ob, guint length, guint speed, guint espeed) | |
| 423 { | |
| 424 guint w = 0; | |
| 425 static gpointer nbuffer = NULL; | |
| 426 static guint nbuffer_size = 0; | |
| 427 | |
| 428 switch (output.format.oss) { | |
| 429 case AFMT_S16_BE: | |
| 430 case AFMT_S16_LE: | |
| 431 if (output.channels == 2) | |
| 432 RESAMPLE_STEREO(gint16); | |
| 433 else | |
| 434 RESAMPLE_MONO(gint16); | |
| 435 break; | |
| 436 case AFMT_U16_BE: | |
| 437 case AFMT_U16_LE: | |
| 438 if (output.channels == 2) | |
| 439 RESAMPLE_STEREO(guint16); | |
| 440 else | |
| 441 RESAMPLE_MONO(guint16); | |
| 442 break; | |
| 443 case AFMT_S8: | |
| 444 if (output.channels == 2) | |
| 445 RESAMPLE_STEREO(gint8); | |
| 446 else | |
| 447 RESAMPLE_MONO(gint8); | |
| 448 break; | |
| 449 case AFMT_U8: | |
| 450 if (output.channels == 2) | |
| 451 RESAMPLE_STEREO(guint8); | |
| 452 else | |
| 453 RESAMPLE_MONO(guint8); | |
| 454 break; | |
| 455 } | |
| 456 return w; | |
| 457 } | |
| 458 | |
| 459 void | |
| 460 oss_write(gpointer ptr, int length) | |
| 461 { | |
| 462 int cnt, off = 0; | |
| 463 | |
| 464 if (!realtime) { | |
| 465 remove_prebuffer = FALSE; | |
| 466 | |
| 467 written += length; | |
| 468 while (length > 0) { | |
| 469 cnt = MIN(length, buffer_size - wr_index); | |
| 470 memcpy(buffer + wr_index, (char *) ptr + off, cnt); | |
| 471 wr_index = (wr_index + cnt) % buffer_size; | |
| 472 length -= cnt; | |
| 473 off += cnt; | |
| 474 } | |
| 475 } | |
| 476 else { | |
| 477 if (paused) | |
| 478 return; | |
| 479 oss_write_audio(ptr, length); | |
| 480 written += length; | |
| 481 } | |
| 482 } | |
| 483 | |
| 484 void | |
| 485 oss_close(void) | |
| 486 { | |
| 487 if (!going) | |
| 488 return; | |
| 489 going = 0; | |
| 490 if (!realtime) | |
| 491 g_thread_join(buffer_thread); | |
| 492 else { | |
| 493 ioctl(fd, SNDCTL_DSP_RESET, 0); | |
| 494 close(fd); | |
| 495 } | |
| 496 g_free(device_name); | |
| 497 oss_free_convert_buffer(); | |
| 498 wr_index = 0; | |
| 499 rd_index = 0; | |
| 500 } | |
| 501 | |
| 502 void | |
| 503 oss_flush(gint time) | |
| 504 { | |
| 505 if (!realtime) { | |
| 506 flush = time; | |
| 507 while (flush != -1) | |
| 508 xmms_usleep(10000); | |
| 509 } | |
| 510 else { | |
| 511 ioctl(fd, SNDCTL_DSP_RESET, 0); | |
| 512 close(fd); | |
| 513 fd = open(device_name, O_WRONLY); | |
| 514 oss_set_audio_params(); | |
| 515 output_time_offset = time; | |
| 516 written = ((guint64) time * input.bps) / 1000; | |
| 517 output_bytes = 0; | |
| 518 } | |
| 519 } | |
| 520 | |
| 521 void | |
| 522 oss_pause(short p) | |
| 523 { | |
| 524 if (!realtime) { | |
| 525 if (p == TRUE) | |
| 526 do_pause = TRUE; | |
| 527 else | |
| 528 unpause = TRUE; | |
| 529 } | |
| 530 else | |
| 531 paused = p; | |
| 532 | |
| 533 } | |
| 534 | |
| 535 gpointer | |
| 536 oss_loop(gpointer arg) | |
| 537 { | |
| 538 gint length, cnt; | |
| 539 fd_set set; | |
| 540 struct timeval tv; | |
| 541 | |
| 542 while (going) { | |
| 543 if (oss_used() > prebuffer_size) | |
| 544 prebuffer = FALSE; | |
| 545 if (oss_used() > 0 && !paused && !prebuffer) { | |
| 546 tv.tv_sec = 0; | |
| 547 tv.tv_usec = 10000; | |
| 548 FD_ZERO(&set); | |
| 549 FD_SET(fd, &set); | |
| 550 if (!select_works || (select(fd + 1, NULL, &set, NULL, &tv) > 0)) { | |
| 551 length = MIN(blk_size, oss_used()); | |
| 552 while (length > 0) { | |
| 553 cnt = MIN(length, buffer_size - rd_index); | |
| 554 oss_write_audio(buffer + rd_index, cnt); | |
| 555 rd_index = (rd_index + cnt) % buffer_size; | |
| 556 length -= cnt; | |
| 557 } | |
| 558 if (!oss_used()) | |
| 559 ioctl(fd, SNDCTL_DSP_POST, 0); | |
| 560 } | |
| 561 } | |
| 562 else | |
| 563 xmms_usleep(10000); | |
| 564 oss_calc_device_buffer_used(); | |
| 565 if (do_pause && !paused) { | |
| 566 do_pause = FALSE; | |
| 567 paused = TRUE; | |
| 568 /* | |
| 569 * We lose some data here that is sent to the | |
| 570 * soundcard, but not yet played. I don't | |
| 571 * think this is worth fixing. | |
| 572 */ | |
| 573 ioctl(fd, SNDCTL_DSP_RESET, 0); | |
| 574 } | |
| 575 else if (unpause && paused) { | |
| 576 unpause = FALSE; | |
| 577 close(fd); | |
| 578 fd = open(device_name, O_WRONLY); | |
| 579 oss_set_audio_params(); | |
| 580 paused = FALSE; | |
| 581 } | |
| 582 | |
| 583 if (flush != -1) { | |
| 584 /* | |
| 585 * This close and open is a work around of a | |
| 586 * bug that exists in some drivers which cause | |
| 587 * the driver to get fucked up by a reset | |
| 588 */ | |
| 589 | |
| 590 ioctl(fd, SNDCTL_DSP_RESET, 0); | |
| 591 close(fd); | |
| 592 fd = open(device_name, O_WRONLY); | |
| 593 oss_set_audio_params(); | |
| 594 output_time_offset = flush; | |
| 595 written = ((guint64) flush * input.bps) / 1000; | |
| 596 rd_index = wr_index = output_bytes = 0; | |
| 597 flush = -1; | |
| 598 prebuffer = TRUE; | |
| 599 } | |
| 600 | |
| 601 } | |
| 602 | |
| 603 ioctl(fd, SNDCTL_DSP_RESET, 0); | |
| 604 close(fd); | |
| 605 g_free(buffer); | |
| 606 return NULL; | |
| 607 } | |
| 608 | |
| 609 void | |
| 610 oss_set_audio_params(void) | |
| 611 { | |
| 612 int frag, stereo, ret; | |
| 613 struct timeval tv; | |
| 614 fd_set set; | |
| 615 | |
| 616 ioctl(fd, SNDCTL_DSP_RESET, 0); | |
| 617 frag = (NFRAGS << 16) | fragsize; | |
| 618 ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &frag); | |
| 619 /* | |
| 620 * Set the stream format. This ioctl() might fail, but should | |
| 621 * return a format that works if it does. | |
| 622 */ | |
| 623 ioctl(fd, SNDCTL_DSP_SETFMT, &output.format.oss); | |
| 624 if (ioctl(fd, SNDCTL_DSP_SETFMT, &output.format.oss) == -1) | |
| 625 g_warning("SNDCTL_DSP_SETFMT ioctl failed: %s", strerror(errno)); | |
| 626 | |
| 627 stereo = output.channels - 1; | |
| 628 ioctl(fd, SNDCTL_DSP_STEREO, &stereo); | |
| 629 output.channels = stereo + 1; | |
| 630 | |
| 631 oss_stereo_convert_func = oss_get_stereo_convert_func(output.channels, | |
| 632 effect.channels); | |
| 633 | |
| 634 if (ioctl(fd, SNDCTL_DSP_SPEED, &output.frequency) == -1) | |
| 635 g_warning("SNDCTL_DSP_SPEED ioctl failed: %s", strerror(errno)); | |
| 636 | |
| 637 if (ioctl(fd, SNDCTL_DSP_GETBLKSIZE, &blk_size) == -1) | |
| 638 blk_size = 1L << fragsize; | |
| 639 | |
| 640 oss_convert_func = | |
| 641 oss_get_convert_func(output.format.oss, | |
| 642 oss_get_format(effect.format.xmms)); | |
| 643 | |
| 644 /* | |
| 645 * Stupid hack to find out if the driver support selects, some | |
| 646 * drivers won't work properly without a select and some won't | |
| 647 * work with a select :/ | |
| 648 */ | |
| 649 | |
| 650 tv.tv_sec = 0; | |
| 651 tv.tv_usec = 50000; | |
| 652 FD_ZERO(&set); | |
| 653 FD_SET(fd, &set); | |
| 654 ret = select(fd + 1, NULL, &set, NULL, &tv); | |
| 655 if (ret > 0) | |
| 656 select_works = TRUE; | |
| 657 else | |
| 658 select_works = FALSE; | |
| 659 } | |
| 660 | |
| 661 gint | |
| 662 oss_open(AFormat fmt, gint rate, gint nch) | |
| 663 { | |
| 664 | |
| 665 if (oss_cfg.use_alt_audio_device && oss_cfg.alt_audio_device) | |
| 666 device_name = g_strdup(oss_cfg.alt_audio_device); | |
| 667 else { | |
| 668 if (oss_cfg.audio_device > 0) | |
| 669 device_name = | |
| 670 g_strdup_printf("%s%d", DEV_DSP, oss_cfg.audio_device); | |
| 671 else | |
| 672 device_name = g_strdup(DEV_DSP); | |
| 673 } | |
| 674 | |
| 675 fd = open(device_name, O_WRONLY); | |
| 676 | |
| 677 if (fd == -1) { | |
| 678 g_warning("oss_open(): Failed to open audio device (%s): %s", | |
| 679 device_name, strerror(errno)); | |
| 680 g_free(device_name); | |
| 681 return 0; | |
| 682 } | |
| 683 | |
| 684 input.format.xmms = fmt; | |
| 685 input.frequency = rate; | |
| 686 input.channels = nch; | |
| 687 input.bps = oss_calc_bitrate(oss_get_format(fmt), rate, nch); | |
| 688 | |
| 689 oss_setup_format(fmt, rate, nch); | |
| 690 | |
| 691 realtime = xmms_check_realtime_priority(); | |
| 692 | |
| 693 if (!realtime) { | |
| 694 buffer_size = (oss_cfg.buffer_size * input.bps) / 1000; | |
| 695 if (buffer_size < 8192) | |
| 696 buffer_size = 8192; | |
| 697 prebuffer_size = (buffer_size * oss_cfg.prebuffer) / 100; | |
| 698 if (buffer_size - prebuffer_size < 4096) | |
| 699 prebuffer_size = buffer_size - 4096; | |
| 700 | |
| 701 buffer_size += device_buffer_size; | |
| 702 buffer = g_malloc0(buffer_size); | |
| 703 } | |
| 704 flush = -1; | |
| 705 prebuffer = TRUE; | |
| 706 wr_index = rd_index = output_time_offset = written = output_bytes = 0; | |
| 707 paused = FALSE; | |
| 708 do_pause = FALSE; | |
| 709 unpause = FALSE; | |
| 710 remove_prebuffer = FALSE; | |
| 711 | |
| 712 going = 1; | |
| 713 if (!realtime) | |
| 714 buffer_thread = g_thread_create(oss_loop, NULL, TRUE, NULL); | |
| 715 return 1; | |
| 716 } | |
| 717 | |
| 718 void oss_tell(AFormat * fmt, gint * rate, gint * nch) | |
| 719 { | |
| 720 (*fmt) = input.format.xmms; | |
| 721 (*rate) = input.frequency; | |
| 722 (*nch) = input.channels; | |
| 723 } |
