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 }