Mercurial > audlegacy
changeset 1635:a3a6f657f895 trunk
[svn] - fork the OSS plugin for CoreAudio work
author | nenolod |
---|---|
date | Thu, 07 Sep 2006 09:11:46 -0700 |
parents | f84a1228e6b4 |
children | 09905c29250d |
files | ChangeLog Plugins/Output/CoreAudio/OSS.c Plugins/Output/CoreAudio/OSS.h Plugins/Output/CoreAudio/about.c Plugins/Output/CoreAudio/audio.c Plugins/Output/CoreAudio/configure.c Plugins/Output/CoreAudio/convert.c Plugins/Output/CoreAudio/init.c Plugins/Output/CoreAudio/mixer.c |
diffstat | 9 files changed, 1921 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- a/ChangeLog Wed Sep 06 22:10:19 2006 -0700 +++ b/ChangeLog Thu Sep 07 09:11:46 2006 -0700 @@ -1,3 +1,13 @@ +2006-09-07 05:10:19 +0000 William Pitcock <nenolod@nenolod.net> + revision [2199] + - kludge for libintl borkenness + + + Changes: Modified: + +0 -2 trunk/audacious/Makefile + +1 -0 trunk/audacious/intl + + 2006-09-07 05:03:27 +0000 William Pitcock <nenolod@nenolod.net> revision [2197] - include time.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/Output/CoreAudio/OSS.c Thu Sep 07 09:11:46 2006 -0700 @@ -0,0 +1,72 @@ +/* BMP - Cross-platform multimedia player + * Copyright (C) 2003-2004 BMP development team. + * + * Based on XMMS: + * Copyright (C) 1998-2003 XMMS development team. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "OSS.h" + +#include <glib.h> +#include <glib/gi18n.h> +#include <stdlib.h> + +OutputPlugin oss_op = { + NULL, + NULL, + NULL, /* Description */ + oss_init, + oss_cleanup, + oss_about, + oss_configure, + oss_get_volume, + oss_set_volume, + oss_open, + oss_write, + oss_close, + oss_flush, + oss_pause, + oss_free, + oss_playing, + oss_get_output_time, + oss_get_written_time, + oss_tell +}; + +OutputPlugin * +get_oplugin_info(void) +{ + oss_op.description = g_strdup_printf(_("OSS Output Plugin")); + return &oss_op; +} + + +void oss_cleanup(void) +{ + g_free(oss_op.description); + oss_op.description = NULL; + + if (oss_cfg.alt_audio_device) { + free(oss_cfg.alt_audio_device); + oss_cfg.alt_audio_device = NULL; + } + + if (oss_cfg.alt_mixer_device) { + free(oss_cfg.alt_mixer_device); + oss_cfg.alt_mixer_device = NULL; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/Output/CoreAudio/OSS.h Thu Sep 07 09:11:46 2006 -0700 @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2006 William Pitcock <nenolod -at- nenolod.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef COREAUDIO_H +#define COREAUDIO_H + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <glib.h> + +#include <CoreServices/CoreServices.h> +#include <AudioUnit/AudioUnit.h> + +#include "audacious/plugin.h" + +#define IS_BIG_ENDIAN (G_BYTE_ORDER == G_BIG_ENDIAN) + +extern OutputPlugin op; + +struct CoreAudioState { + +}; + +void ca_init(void); +void ca_cleanup(void); +void ca_about(void); +void ca_configure(void); + +void ca_get_volume(int *l, int *r); +void ca_set_volume(int l, int r); + +int ca_playing(void); +int ca_free(void); +void ca_write(void *ptr, int length); +void ca_close(void); +void ca_flush(int time); +void ca_pause(short p); +int ca_open(AFormat fmt, int rate, int nch); +int ca_get_output_time(void); +int ca_get_written_time(void); +void ca_set_audio_params(void); + +void ca_free_convert_buffer(void); +int (*ca_get_convert_func(int output, int input)) (void **, int); +int (*ca_get_stereo_convert_func(int output, int input)) (void **, int, + int); + +void ca_tell(AFormat * fmt, gint * rate, gint * nch); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/Output/CoreAudio/about.c Thu Sep 07 09:11:46 2006 -0700 @@ -0,0 +1,57 @@ +/* BMP - Cross-platform multimedia player + * Copyright (C) 2003-2004 BMP development team. + * + * Based on XMMS: + * Copyright (C) 1998-2003 XMMS development team. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "OSS.h" + +#include <glib.h> +#include <glib/gi18n.h> +#include <gtk/gtk.h> + +#include <libaudacious/util.h> + + +void +oss_about(void) +{ + static GtkWidget *dialog; + + if (dialog != NULL) + return; + + dialog = xmms_show_message(_("About OSS Driver"), + _("Audacious OSS Driver\n\n " + "This program is free software; you can redistribute it and/or modify\n" + "it under the terms of the GNU General Public License as published by\n" + "the Free Software Foundation; either version 2 of the License, or\n" + "(at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,\n" + "USA."), _("Ok"), FALSE, NULL, NULL); + g_signal_connect(G_OBJECT(dialog), "destroy", + G_CALLBACK(gtk_widget_destroyed), &dialog); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/Output/CoreAudio/audio.c Thu Sep 07 09:11:46 2006 -0700 @@ -0,0 +1,723 @@ +/* BMP - Cross-platform multimedia player + * Copyright (C) 2003-2004 BMP development team. + * + * Based on XMMS: + * Copyright (C) 1998-2003 XMMS development team. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <glib.h> +#include <libaudacious/util.h> +#include <string.h> + +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/ioctl.h> +#include <sys/time.h> + +#include "OSS.h" + + +#define NFRAGS 32 + +static gint fd = 0; +static char *buffer; +static gboolean going, prebuffer, paused, unpause, do_pause, remove_prebuffer; +static gint device_buffer_used, buffer_size, prebuffer_size, blk_size; +static gint rd_index = 0, wr_index = 0; +static gint output_time_offset = 0; +static guint64 written = 0, output_bytes = 0; +static gint flush; +static gint fragsize, device_buffer_size; +static gchar *device_name; +static GThread *buffer_thread; +static gboolean realtime, select_works; + +static int (*oss_convert_func) (void **data, int length); +static int (*oss_stereo_convert_func) (void **data, int length, int fmt); + +struct format_info { + union { + AFormat xmms; + int oss; + } format; + int frequency; + int channels; + int bps; +}; + + +/* + * The format of the data from the input plugin + * This will never change during a song. + */ +struct format_info input; + +/* + * The format we get from the effect plugin. + * This will be different from input if the effect plugin does + * some kind of format conversion. + */ +struct format_info effect; + +/* + * The format of the data we actually send to the soundcard. + * This might be different from effect if we need to resample or do + * some other format conversion. + */ +struct format_info output; + + +static void +oss_calc_device_buffer_used(void) +{ + audio_buf_info buf_info; + if (paused) + device_buffer_used = 0; + else if (!ioctl(fd, SNDCTL_DSP_GETOSPACE, &buf_info)) + device_buffer_used = + (buf_info.fragstotal * buf_info.fragsize) - buf_info.bytes; +} + + +static gint oss_downsample(gpointer ob, guint length, guint speed, + guint espeed); + +static int +oss_calc_bitrate(int oss_fmt, int rate, int channels) +{ + int bitrate = rate * channels; + + if (oss_fmt == AFMT_U16_BE || oss_fmt == AFMT_U16_LE || + oss_fmt == AFMT_S16_BE || oss_fmt == AFMT_S16_LE) + bitrate *= 2; + + return bitrate; +} + +static int +oss_get_format(AFormat fmt) +{ + int format = 0; + + switch (fmt) { + case FMT_U8: + format = AFMT_U8; + break; + case FMT_S8: + format = AFMT_S8; + break; + case FMT_U16_LE: + format = AFMT_U16_LE; + break; + case FMT_U16_BE: + format = AFMT_U16_BE; + break; + case FMT_U16_NE: +#if (G_BYTE_ORDER == G_BIG_ENDIAN) + format = AFMT_U16_BE; +#else + format = AFMT_U16_LE; +#endif + break; + case FMT_S16_LE: + format = AFMT_S16_LE; + break; + case FMT_S16_BE: + format = AFMT_S16_BE; + break; + case FMT_S16_NE: +#if (G_BYTE_ORDER == G_BIG_ENDIAN) + format = AFMT_S16_BE; +#else + format = AFMT_S16_LE; +#endif + break; + } + + return format; +} + +static void +oss_setup_format(AFormat fmt, int rate, int nch) +{ + effect.format.xmms = fmt; + effect.frequency = rate; + effect.channels = nch; + effect.bps = oss_calc_bitrate(oss_get_format(fmt), rate, nch); + + output.format.oss = oss_get_format(fmt); + output.frequency = rate; + output.channels = nch; + + + fragsize = 0; + while ((1L << fragsize) < effect.bps / 25) + fragsize++; + fragsize--; + + device_buffer_size = ((1L << fragsize) * (NFRAGS + 1)); + + oss_set_audio_params(); + + output.bps = oss_calc_bitrate(output.format.oss, output.frequency, + output.channels); +} + + +gint +oss_get_written_time(void) +{ + if (!going) + return 0; + return (written * 1000) / effect.bps; +} + +gint +oss_get_output_time(void) +{ + guint64 bytes; + + if (!fd || !going) + return 0; + + if (realtime) + oss_calc_device_buffer_used(); + bytes = output_bytes < device_buffer_used ? + 0 : output_bytes - device_buffer_used; + + return output_time_offset + ((bytes * 1000) / output.bps); +} + +static int +oss_used(void) +{ + if (realtime) + return 0; + else { + if (wr_index >= rd_index) + return wr_index - rd_index; + return buffer_size - (rd_index - wr_index); + } +} + +gint +oss_playing(void) +{ + if (!going) + return 0; + if (realtime) + oss_calc_device_buffer_used(); + if (!oss_used() && (device_buffer_used - (3 * blk_size)) <= 0) + return FALSE; + + return TRUE; +} + +gint +oss_free(void) +{ + if (!realtime) { + if (remove_prebuffer && prebuffer) { + prebuffer = FALSE; + remove_prebuffer = FALSE; + } + if (prebuffer) + remove_prebuffer = TRUE; + + if (rd_index > wr_index) + return (rd_index - wr_index) - device_buffer_size - 1; + return (buffer_size - (wr_index - rd_index)) - device_buffer_size - 1; + } + else if (paused) + return 0; + else + return 1000000; +} + +static inline ssize_t +write_all(int fd, const void *buf, size_t count) +{ + size_t done = 0; + do { + ssize_t n = write(fd, (gchar *) buf + done, count - done); + if (n == -1) { + if (errno == EINTR) + continue; + else + break; + } + done += n; + } while (count > done); + + return done; +} + +static void +oss_write_audio(gpointer data, int length) +{ + + audio_buf_info abuf_info; + AFormat new_format; + int new_frequency, new_channels; + EffectPlugin *ep; + + new_format = input.format.xmms; + new_frequency = input.frequency; + new_channels = input.channels; + + + ep = get_current_effect_plugin(); + if (effects_enabled() && ep && ep->query_format) { + ep->query_format(&new_format, &new_frequency, &new_channels); + } + + if (new_format != effect.format.xmms || + new_frequency != effect.frequency || + new_channels != effect.channels) { + output_time_offset += (output_bytes * 1000) / output.bps; + output_bytes = 0; + close(fd); + fd = open(device_name, O_WRONLY); + oss_setup_format(new_format, new_frequency, new_channels); + } + if (effects_enabled() && ep && ep->mod_samples) + length = ep->mod_samples(&data, length, + input.format.xmms, + input.frequency, input.channels); + if (realtime && !ioctl(fd, SNDCTL_DSP_GETOSPACE, &abuf_info)) { + while (abuf_info.bytes < length) { + xmms_usleep(10000); + if (ioctl(fd, SNDCTL_DSP_GETOSPACE, &abuf_info)) + break; + } + } + + if (oss_convert_func != NULL) + length = oss_convert_func(&data, length); + + if (oss_stereo_convert_func != NULL) + length = oss_stereo_convert_func(&data, length, output.format.oss); + + if (effect.frequency == output.frequency) + output_bytes += write_all(fd, data, length); + else + output_bytes += oss_downsample(data, length, + effect.frequency, output.frequency); +} + +static void +swap_endian(guint16 * data, int length) +{ + int i; + for (i = 0; i < length; i += 2, data++) + *data = GUINT16_SWAP_LE_BE(*data); +} + +#define NOT_NATIVE_ENDIAN ((IS_BIG_ENDIAN && \ + (output.format.oss == AFMT_S16_LE || \ + output.format.oss == AFMT_U16_LE)) || \ + (!IS_BIG_ENDIAN && \ + (output.format.oss == AFMT_S16_BE || \ + output.format.oss == AFMT_U16_BE))) + + +#define RESAMPLE_STEREO(sample_type) \ +do { \ + const gint shift = sizeof (sample_type); \ + gint i, in_samples, out_samples, x, delta; \ + sample_type *inptr = (sample_type *)ob, *outptr; \ + guint nlen = (((length >> shift) * espeed) / speed); \ + if (nlen == 0) \ + break; \ + nlen <<= shift; \ + if (NOT_NATIVE_ENDIAN) \ + swap_endian(ob, length); \ + if(nlen > nbuffer_size) \ + { \ + nbuffer = g_realloc(nbuffer, nlen); \ + nbuffer_size = nlen; \ + } \ + outptr = (sample_type *)nbuffer; \ + in_samples = length >> shift; \ + out_samples = nlen >> shift; \ + delta = (in_samples << 12) / out_samples; \ + for (x = 0, i = 0; i < out_samples; i++) \ + { \ + gint x1, frac; \ + x1 = (x >> 12) << 12; \ + frac = x - x1; \ + *outptr++ = \ + (sample_type) \ + ((inptr[(x1 >> 12) << 1] * \ + ((1<<12) - frac) + \ + inptr[((x1 >> 12) + 1) << 1] * \ + frac) >> 12); \ + *outptr++ = \ + (sample_type) \ + ((inptr[((x1 >> 12) << 1) + 1] * \ + ((1<<12) - frac) + \ + inptr[(((x1 >> 12) + 1) << 1) + 1] * \ + frac) >> 12); \ + x += delta; \ + } \ + if (NOT_NATIVE_ENDIAN) \ + swap_endian(nbuffer, nlen); \ + w = write_all(fd, nbuffer, nlen); \ +} while (0) + + +#define RESAMPLE_MONO(sample_type) \ +do { \ + const gint shift = sizeof (sample_type) - 1; \ + gint i, x, delta, in_samples, out_samples; \ + sample_type *inptr = (sample_type *)ob, *outptr; \ + guint nlen = (((length >> shift) * espeed) / speed); \ + if (nlen == 0) \ + break; \ + nlen <<= shift; \ + if (NOT_NATIVE_ENDIAN) \ + swap_endian(ob, length); \ + if(nlen > nbuffer_size) \ + { \ + nbuffer = g_realloc(nbuffer, nlen); \ + nbuffer_size = nlen; \ + } \ + outptr = (sample_type *)nbuffer; \ + in_samples = length >> shift; \ + out_samples = nlen >> shift; \ + delta = ((length >> shift) << 12) / out_samples; \ + for (x = 0, i = 0; i < out_samples; i++) \ + { \ + gint x1, frac; \ + x1 = (x >> 12) << 12; \ + frac = x - x1; \ + *outptr++ = \ + (sample_type) \ + ((inptr[x1 >> 12] * ((1<<12) - frac) + \ + inptr[(x1 >> 12) + 1] * frac) >> 12); \ + x += delta; \ + } \ + if (NOT_NATIVE_ENDIAN) \ + swap_endian(nbuffer, nlen); \ + w = write_all(fd, nbuffer, nlen); \ +} while (0) + + +static gint +oss_downsample(gpointer ob, guint length, guint speed, guint espeed) +{ + guint w = 0; + static gpointer nbuffer = NULL; + static guint nbuffer_size = 0; + + switch (output.format.oss) { + case AFMT_S16_BE: + case AFMT_S16_LE: + if (output.channels == 2) + RESAMPLE_STEREO(gint16); + else + RESAMPLE_MONO(gint16); + break; + case AFMT_U16_BE: + case AFMT_U16_LE: + if (output.channels == 2) + RESAMPLE_STEREO(guint16); + else + RESAMPLE_MONO(guint16); + break; + case AFMT_S8: + if (output.channels == 2) + RESAMPLE_STEREO(gint8); + else + RESAMPLE_MONO(gint8); + break; + case AFMT_U8: + if (output.channels == 2) + RESAMPLE_STEREO(guint8); + else + RESAMPLE_MONO(guint8); + break; + } + return w; +} + +void +oss_write(gpointer ptr, int length) +{ + int cnt, off = 0; + + if (!realtime) { + remove_prebuffer = FALSE; + + written += length; + while (length > 0) { + cnt = MIN(length, buffer_size - wr_index); + memcpy(buffer + wr_index, (char *) ptr + off, cnt); + wr_index = (wr_index + cnt) % buffer_size; + length -= cnt; + off += cnt; + } + } + else { + if (paused) + return; + oss_write_audio(ptr, length); + written += length; + } +} + +void +oss_close(void) +{ + if (!going) + return; + going = 0; + if (!realtime) + g_thread_join(buffer_thread); + else { + ioctl(fd, SNDCTL_DSP_RESET, 0); + close(fd); + } + g_free(device_name); + oss_free_convert_buffer(); + wr_index = 0; + rd_index = 0; +} + +void +oss_flush(gint time) +{ + if (!realtime) { + flush = time; + while (flush != -1) + xmms_usleep(10000); + } + else { + ioctl(fd, SNDCTL_DSP_RESET, 0); + close(fd); + fd = open(device_name, O_WRONLY); + oss_set_audio_params(); + output_time_offset = time; + written = ((guint64) time * input.bps) / 1000; + output_bytes = 0; + } +} + +void +oss_pause(short p) +{ + if (!realtime) { + if (p == TRUE) + do_pause = TRUE; + else + unpause = TRUE; + } + else + paused = p; + +} + +gpointer +oss_loop(gpointer arg) +{ + gint length, cnt; + fd_set set; + struct timeval tv; + + while (going) { + if (oss_used() > prebuffer_size) + prebuffer = FALSE; + if (oss_used() > 0 && !paused && !prebuffer) { + tv.tv_sec = 0; + tv.tv_usec = 10000; + FD_ZERO(&set); + FD_SET(fd, &set); + if (!select_works || (select(fd + 1, NULL, &set, NULL, &tv) > 0)) { + length = MIN(blk_size, oss_used()); + while (length > 0) { + cnt = MIN(length, buffer_size - rd_index); + oss_write_audio(buffer + rd_index, cnt); + rd_index = (rd_index + cnt) % buffer_size; + length -= cnt; + } + if (!oss_used()) + ioctl(fd, SNDCTL_DSP_POST, 0); + } + } + else + xmms_usleep(10000); + oss_calc_device_buffer_used(); + if (do_pause && !paused) { + do_pause = FALSE; + paused = TRUE; + /* + * We lose some data here that is sent to the + * soundcard, but not yet played. I don't + * think this is worth fixing. + */ + ioctl(fd, SNDCTL_DSP_RESET, 0); + } + else if (unpause && paused) { + unpause = FALSE; + close(fd); + fd = open(device_name, O_WRONLY); + oss_set_audio_params(); + paused = FALSE; + } + + if (flush != -1) { + /* + * This close and open is a work around of a + * bug that exists in some drivers which cause + * the driver to get fucked up by a reset + */ + + ioctl(fd, SNDCTL_DSP_RESET, 0); + close(fd); + fd = open(device_name, O_WRONLY); + oss_set_audio_params(); + output_time_offset = flush; + written = ((guint64) flush * input.bps) / 1000; + rd_index = wr_index = output_bytes = 0; + flush = -1; + prebuffer = TRUE; + } + + } + + ioctl(fd, SNDCTL_DSP_RESET, 0); + close(fd); + g_free(buffer); + return NULL; +} + +void +oss_set_audio_params(void) +{ + int frag, stereo, ret; + struct timeval tv; + fd_set set; + + ioctl(fd, SNDCTL_DSP_RESET, 0); + frag = (NFRAGS << 16) | fragsize; + ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &frag); + /* + * Set the stream format. This ioctl() might fail, but should + * return a format that works if it does. + */ + ioctl(fd, SNDCTL_DSP_SETFMT, &output.format.oss); + if (ioctl(fd, SNDCTL_DSP_SETFMT, &output.format.oss) == -1) + g_warning("SNDCTL_DSP_SETFMT ioctl failed: %s", strerror(errno)); + + stereo = output.channels - 1; + ioctl(fd, SNDCTL_DSP_STEREO, &stereo); + output.channels = stereo + 1; + + oss_stereo_convert_func = oss_get_stereo_convert_func(output.channels, + effect.channels); + + if (ioctl(fd, SNDCTL_DSP_SPEED, &output.frequency) == -1) + g_warning("SNDCTL_DSP_SPEED ioctl failed: %s", strerror(errno)); + + if (ioctl(fd, SNDCTL_DSP_GETBLKSIZE, &blk_size) == -1) + blk_size = 1L << fragsize; + + oss_convert_func = + oss_get_convert_func(output.format.oss, + oss_get_format(effect.format.xmms)); + + /* + * Stupid hack to find out if the driver support selects, some + * drivers won't work properly without a select and some won't + * work with a select :/ + */ + + tv.tv_sec = 0; + tv.tv_usec = 50000; + FD_ZERO(&set); + FD_SET(fd, &set); + ret = select(fd + 1, NULL, &set, NULL, &tv); + if (ret > 0) + select_works = TRUE; + else + select_works = FALSE; +} + +gint +oss_open(AFormat fmt, gint rate, gint nch) +{ + + if (oss_cfg.use_alt_audio_device && oss_cfg.alt_audio_device) + device_name = g_strdup(oss_cfg.alt_audio_device); + else { + if (oss_cfg.audio_device > 0) + device_name = + g_strdup_printf("%s%d", DEV_DSP, oss_cfg.audio_device); + else + device_name = g_strdup(DEV_DSP); + } + + fd = open(device_name, O_WRONLY); + + if (fd == -1) { + g_warning("oss_open(): Failed to open audio device (%s): %s", + device_name, strerror(errno)); + g_free(device_name); + return 0; + } + + input.format.xmms = fmt; + input.frequency = rate; + input.channels = nch; + input.bps = oss_calc_bitrate(oss_get_format(fmt), rate, nch); + + oss_setup_format(fmt, rate, nch); + + realtime = xmms_check_realtime_priority(); + + if (!realtime) { + buffer_size = (oss_cfg.buffer_size * input.bps) / 1000; + if (buffer_size < 8192) + buffer_size = 8192; + prebuffer_size = (buffer_size * oss_cfg.prebuffer) / 100; + if (buffer_size - prebuffer_size < 4096) + prebuffer_size = buffer_size - 4096; + + buffer_size += device_buffer_size; + buffer = g_malloc0(buffer_size); + } + flush = -1; + prebuffer = TRUE; + wr_index = rd_index = output_time_offset = written = output_bytes = 0; + paused = FALSE; + do_pause = FALSE; + unpause = FALSE; + remove_prebuffer = FALSE; + + going = 1; + if (!realtime) + buffer_thread = g_thread_create(oss_loop, NULL, TRUE, NULL); + return 1; +} + +void oss_tell(AFormat * fmt, gint * rate, gint * nch) +{ + (*fmt) = input.format.xmms; + (*rate) = input.frequency; + (*nch) = input.channels; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/Output/CoreAudio/configure.c Thu Sep 07 09:11:46 2006 -0700 @@ -0,0 +1,375 @@ +/* BMP - Cross-platform multimedia player + * Copyright (C) 2003-2004 BMP development team. + * + * Based on XMMS: + * Copyright (C) 1998-2003 XMMS development team. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "OSS.h" + +#include <glib.h> +#include <glib/gi18n.h> +#include <gtk/gtk.h> +#include <stdio.h> +#include <string.h> + +#include <libaudacious/configdb.h> + + +static GtkWidget *configure_win = NULL; +static GtkWidget *mixer_usemaster_check, *buffer_size_spin, *buffer_pre_spin; +static GtkWidget *adevice_use_alt_check, *audio_alt_device_entry; +static GtkWidget *mdevice_use_alt_check, *mixer_alt_device_entry; +static gint audio_device, mixer_device; + +static void +configure_win_ok_cb(GtkWidget * w, gpointer data) +{ + ConfigDb *db; + + oss_cfg.audio_device = audio_device; + oss_cfg.mixer_device = mixer_device; + oss_cfg.buffer_size = + gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(buffer_size_spin)); + oss_cfg.prebuffer = + gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(buffer_pre_spin)); + oss_cfg.use_master = + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON + (mixer_usemaster_check)); + oss_cfg.use_alt_audio_device = + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON + (adevice_use_alt_check)); + oss_cfg.use_alt_mixer_device = + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON + (mdevice_use_alt_check)); + g_free(oss_cfg.alt_audio_device); + oss_cfg.alt_audio_device = + gtk_editable_get_chars(GTK_EDITABLE(audio_alt_device_entry), 0, -1); + g_strstrip(oss_cfg.alt_audio_device); + g_free(oss_cfg.alt_mixer_device); + oss_cfg.alt_mixer_device = + gtk_editable_get_chars(GTK_EDITABLE(mixer_alt_device_entry), 0, -1); + g_strstrip(oss_cfg.alt_mixer_device); + + if (oss_cfg.use_alt_audio_device) + /* do a minimum of sanity checking */ + if (oss_cfg.alt_audio_device[0] != '/') + oss_cfg.use_alt_audio_device = FALSE; + if (oss_cfg.use_alt_mixer_device) + if (oss_cfg.alt_mixer_device[0] != '/') + oss_cfg.use_alt_mixer_device = FALSE; + + db = bmp_cfg_db_open(); + + bmp_cfg_db_set_int(db, "OSS", "audio_device", oss_cfg.audio_device); + bmp_cfg_db_set_int(db, "OSS", "mixer_device", oss_cfg.mixer_device); + bmp_cfg_db_set_int(db, "OSS", "buffer_size", oss_cfg.buffer_size); + bmp_cfg_db_set_int(db, "OSS", "prebuffer", oss_cfg.prebuffer); + bmp_cfg_db_set_bool(db, "OSS", "use_master", oss_cfg.use_master); + bmp_cfg_db_set_bool(db, "OSS", "use_alt_audio_device", + oss_cfg.use_alt_audio_device); + bmp_cfg_db_set_string(db, "OSS", "alt_audio_device", + oss_cfg.alt_audio_device); + bmp_cfg_db_set_bool(db, "OSS", "use_alt_mixer_device", + oss_cfg.use_alt_mixer_device); + bmp_cfg_db_set_string(db, "OSS", "alt_mixer_device", + oss_cfg.alt_mixer_device); + bmp_cfg_db_close(db); +} + +static void +configure_win_audio_dev_cb(GtkWidget * widget, gint device) +{ + audio_device = device; +} + +static void +configure_win_mixer_dev_cb(GtkWidget * widget, gint device) +{ + mixer_device = device; +} + +static void +audio_device_toggled(GtkToggleButton * widget, gpointer data) +{ + gboolean use_alt_audio_device = gtk_toggle_button_get_active(widget); + gtk_widget_set_sensitive(GTK_WIDGET(data), !use_alt_audio_device); + gtk_widget_set_sensitive(audio_alt_device_entry, use_alt_audio_device); +} + +static void +mixer_device_toggled(GtkToggleButton * widget, gpointer data) +{ + gboolean use_alt_device = gtk_toggle_button_get_active(widget); + gtk_widget_set_sensitive(GTK_WIDGET(data), !use_alt_device); + gtk_widget_set_sensitive(mixer_alt_device_entry, use_alt_device); +} + +static void +scan_devices(gchar * type, GtkWidget * option_menu, GtkSignalFunc sigfunc) +{ + GtkWidget *menu, *item; + FILE *file; + gchar buffer[256], *temp, *tmp2; + gboolean found = FALSE; + gint index = 0; + + menu = gtk_menu_new(); + + if ((file = fopen("/dev/sndstat", "r"))) { + while (fgets(buffer, 255, file)) { + if (found && buffer[0] == '\n') + break; + if (buffer[strlen(buffer) - 1] == '\n') + buffer[strlen(buffer) - 1] = '\0'; + if (found) { + if (index == 0) { + tmp2 = strchr(buffer, ':'); + if (tmp2) { + tmp2++; + while (*tmp2 == ' ') + tmp2++; + } + else + tmp2 = buffer; + temp = g_strdup_printf(_("Default (%s)"), tmp2); + item = gtk_menu_item_new_with_label(temp); + g_free(temp); + } + else + item = gtk_menu_item_new_with_label(buffer); + g_signal_connect(G_OBJECT(item), "activate", + G_CALLBACK(sigfunc), (gpointer)(long)(index++)); + gtk_widget_show(item); + gtk_menu_append(GTK_MENU(menu), item); + } + if (!strcasecmp(buffer, type)) + found = 1; + + } + fclose(file); + } + else { + item = gtk_menu_item_new_with_label(_("Default")); + g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(sigfunc), + (gpointer) 0); + gtk_widget_show(item); + gtk_menu_append(GTK_MENU(menu), item); + } + gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), menu); +} + +void +oss_configure(void) +{ + GtkWidget *vbox, *notebook; + GtkWidget *dev_vbox, *adevice_frame, *adevice_box, *adevice; + GtkWidget *mdevice_frame, *mdevice_box, *mdevice; + GtkWidget *buffer_frame, *buffer_vbox, *buffer_table; + GtkWidget *buffer_size_box, *buffer_size_label; + GtkObject *buffer_size_adj, *buffer_pre_adj; + GtkWidget *buffer_pre_box, *buffer_pre_label; + GtkWidget *audio_alt_box, *mixer_alt_box; + GtkWidget *bbox, *ok, *cancel; + GtkWidget *mixer_table, *mixer_frame; + + if (configure_win) { + gtk_window_present(GTK_WINDOW(configure_win)); + return; + } + + configure_win = gtk_window_new(GTK_WINDOW_TOPLEVEL); + g_signal_connect(G_OBJECT(configure_win), "destroy", + G_CALLBACK(gtk_widget_destroyed), &configure_win); + gtk_window_set_title(GTK_WINDOW(configure_win), + _("OSS Driver configuration")); + gtk_window_set_type_hint(GTK_WINDOW(configure_win), + GDK_WINDOW_TYPE_HINT_DIALOG); + gtk_window_set_resizable(GTK_WINDOW(configure_win), FALSE); + gtk_window_set_position(GTK_WINDOW(configure_win), GTK_WIN_POS_CENTER); + gtk_container_border_width(GTK_CONTAINER(configure_win), 10); + + vbox = gtk_vbox_new(FALSE, 10); + gtk_container_add(GTK_CONTAINER(configure_win), vbox); + + notebook = gtk_notebook_new(); + gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0); + + dev_vbox = gtk_vbox_new(FALSE, 5); + gtk_container_set_border_width(GTK_CONTAINER(dev_vbox), 5); + + adevice_frame = gtk_frame_new(_("Audio device:")); + gtk_box_pack_start(GTK_BOX(dev_vbox), adevice_frame, FALSE, FALSE, 0); + + adevice_box = gtk_vbox_new(FALSE, 5); + gtk_container_set_border_width(GTK_CONTAINER(adevice_box), 5); + gtk_container_add(GTK_CONTAINER(adevice_frame), adevice_box); + + adevice = gtk_option_menu_new(); + gtk_box_pack_start(GTK_BOX(adevice_box), adevice, TRUE, TRUE, 0); +#if defined(HAVE_NEWPCM) + scan_devices("Installed devices:", adevice, + GTK_SIGNAL_FUNC(configure_win_audio_dev_cb)); +#else + scan_devices("Audio devices:", adevice, + GTK_SIGNAL_FUNC(configure_win_audio_dev_cb)); +#endif + audio_device = oss_cfg.audio_device; + gtk_option_menu_set_history(GTK_OPTION_MENU(adevice), + oss_cfg.audio_device); + audio_alt_box = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start_defaults(GTK_BOX(adevice_box), audio_alt_box); + adevice_use_alt_check = + gtk_check_button_new_with_label(_("Use alternate device:")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(adevice_use_alt_check), + oss_cfg.use_alt_audio_device); + g_signal_connect(G_OBJECT(adevice_use_alt_check), "toggled", + G_CALLBACK(audio_device_toggled), adevice); + gtk_box_pack_start(GTK_BOX(audio_alt_box), adevice_use_alt_check, + FALSE, FALSE, 0); + audio_alt_device_entry = gtk_entry_new(); + if (oss_cfg.alt_audio_device != NULL) + gtk_entry_set_text(GTK_ENTRY(audio_alt_device_entry), + oss_cfg.alt_audio_device); + else + gtk_entry_set_text(GTK_ENTRY(audio_alt_device_entry), DEV_DSP); + gtk_box_pack_start_defaults(GTK_BOX(audio_alt_box), + audio_alt_device_entry); + + if (oss_cfg.use_alt_audio_device) + gtk_widget_set_sensitive(adevice, FALSE); + else + gtk_widget_set_sensitive(audio_alt_device_entry, FALSE); + + mdevice_frame = gtk_frame_new(_("Mixer device:")); + gtk_box_pack_start(GTK_BOX(dev_vbox), mdevice_frame, FALSE, FALSE, 0); + + mdevice_box = gtk_vbox_new(FALSE, 0); + gtk_container_set_border_width(GTK_CONTAINER(mdevice_box), 5); + gtk_container_add(GTK_CONTAINER(mdevice_frame), mdevice_box); + + mdevice = gtk_option_menu_new(); + gtk_box_pack_start(GTK_BOX(mdevice_box), mdevice, TRUE, TRUE, 0); +#if defined(HAVE_NEWPCM) + scan_devices("Installed devices:", mdevice, configure_win_mixer_dev_cb); +#else + scan_devices("Mixers:", mdevice, G_CALLBACK(configure_win_mixer_dev_cb)); +#endif + mixer_device = oss_cfg.mixer_device; + gtk_option_menu_set_history(GTK_OPTION_MENU(mdevice), + oss_cfg.mixer_device); + mixer_alt_box = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start_defaults(GTK_BOX(mdevice_box), mixer_alt_box); + mdevice_use_alt_check = + gtk_check_button_new_with_label(_("Use alternate device:")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(mdevice_use_alt_check), + oss_cfg.use_alt_mixer_device); + g_signal_connect(G_OBJECT(mdevice_use_alt_check), "toggled", + G_CALLBACK(mixer_device_toggled), mdevice); + gtk_box_pack_start(GTK_BOX(mixer_alt_box), mdevice_use_alt_check, + FALSE, FALSE, 0); + mixer_alt_device_entry = gtk_entry_new(); + if (oss_cfg.alt_mixer_device != NULL) + gtk_entry_set_text(GTK_ENTRY(mixer_alt_device_entry), + oss_cfg.alt_mixer_device); + else + gtk_entry_set_text(GTK_ENTRY(mixer_alt_device_entry), DEV_MIXER); + gtk_box_pack_start_defaults(GTK_BOX(mixer_alt_box), + mixer_alt_device_entry); + + if (oss_cfg.use_alt_mixer_device) + gtk_widget_set_sensitive(mdevice, FALSE); + else + gtk_widget_set_sensitive(mixer_alt_device_entry, FALSE); + + gtk_notebook_append_page(GTK_NOTEBOOK(notebook), dev_vbox, + gtk_label_new(_("Devices"))); + + buffer_frame = gtk_frame_new(_("Buffering:")); + gtk_container_set_border_width(GTK_CONTAINER(buffer_frame), 5); + + buffer_vbox = gtk_vbox_new(FALSE, 0); + gtk_container_add(GTK_CONTAINER(buffer_frame), buffer_vbox); + + buffer_table = gtk_table_new(2, 1, TRUE); + gtk_container_set_border_width(GTK_CONTAINER(buffer_table), 5); + gtk_box_pack_start(GTK_BOX(buffer_vbox), buffer_table, FALSE, FALSE, 0); + + buffer_size_box = gtk_hbox_new(FALSE, 5); + gtk_table_attach_defaults(GTK_TABLE(buffer_table), buffer_size_box, 0, + 1, 0, 1); + buffer_size_label = gtk_label_new(_("Buffer size (ms):")); + gtk_box_pack_start(GTK_BOX(buffer_size_box), buffer_size_label, FALSE, + FALSE, 0); + buffer_size_adj = + gtk_adjustment_new(oss_cfg.buffer_size, 200, 10000, 100, 100, 100); + buffer_size_spin = + gtk_spin_button_new(GTK_ADJUSTMENT(buffer_size_adj), 8, 0); + gtk_widget_set_usize(buffer_size_spin, 60, -1); + gtk_box_pack_start(GTK_BOX(buffer_size_box), buffer_size_spin, FALSE, + FALSE, 0); + + buffer_pre_box = gtk_hbox_new(FALSE, 5); + gtk_table_attach_defaults(GTK_TABLE(buffer_table), buffer_pre_box, 1, + 2, 0, 1); + buffer_pre_label = gtk_label_new(_("Pre-buffer (percent):")); + gtk_box_pack_start(GTK_BOX(buffer_pre_box), buffer_pre_label, FALSE, + FALSE, 0); + buffer_pre_adj = gtk_adjustment_new(oss_cfg.prebuffer, 0, 90, 1, 1, 1); + buffer_pre_spin = + gtk_spin_button_new(GTK_ADJUSTMENT(buffer_pre_adj), 1, 0); + gtk_widget_set_usize(buffer_pre_spin, 60, -1); + gtk_box_pack_start(GTK_BOX(buffer_pre_box), buffer_pre_spin, FALSE, + FALSE, 0); + + gtk_notebook_append_page(GTK_NOTEBOOK(notebook), buffer_frame, + gtk_label_new(_("Buffering"))); + mixer_frame = gtk_frame_new(_("Mixer Settings:")); + gtk_container_set_border_width(GTK_CONTAINER(mixer_frame), 5); + mixer_table = gtk_table_new(3, 2, TRUE); + gtk_container_add(GTK_CONTAINER(mixer_frame), mixer_table); + gtk_container_set_border_width(GTK_CONTAINER(mixer_table), 5); + mixer_usemaster_check = + gtk_check_button_new_with_label(_("Volume controls Master not PCM")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(mixer_usemaster_check), + oss_cfg.use_master); + gtk_table_attach_defaults(GTK_TABLE(mixer_table), + mixer_usemaster_check, 0, 1, 0, 1); + gtk_notebook_append_page(GTK_NOTEBOOK(notebook), mixer_frame, + gtk_label_new(_("Mixer"))); + + bbox = gtk_hbutton_box_new(); + gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); + gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5); + gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0); + + cancel = gtk_button_new_from_stock(GTK_STOCK_CLOSE); + g_signal_connect_swapped(G_OBJECT(cancel), "clicked", + G_CALLBACK(gtk_widget_destroy), + GTK_OBJECT(configure_win)); + GTK_WIDGET_SET_FLAGS(cancel, GTK_CAN_DEFAULT); + gtk_box_pack_start(GTK_BOX(bbox), cancel, TRUE, TRUE, 0); + + ok = gtk_button_new_from_stock(GTK_STOCK_APPLY); + g_signal_connect(G_OBJECT(ok), "clicked", + G_CALLBACK(configure_win_ok_cb), NULL); + GTK_WIDGET_SET_FLAGS(ok, GTK_CAN_DEFAULT); + gtk_box_pack_start(GTK_BOX(bbox), ok, TRUE, TRUE, 0); + gtk_widget_grab_default(ok); + + gtk_widget_show_all(configure_win); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/Output/CoreAudio/convert.c Thu Sep 07 09:11:46 2006 -0700 @@ -0,0 +1,446 @@ +/* + * Copyright (C) 2001 Haavard Kvaalen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "OSS.h" + +struct buffer { + void *buffer; + int size; +} format_buffer, stereo_buffer; + + +static void * +oss_get_convert_buffer(struct buffer *buffer, size_t size) +{ + if (size > 0 && size <= (size_t)buffer->size) + return buffer->buffer; + + buffer->size = size; + buffer->buffer = g_realloc(buffer->buffer, size); + return buffer->buffer; +} + +void +oss_free_convert_buffer(void) +{ + oss_get_convert_buffer(&format_buffer, 0); + oss_get_convert_buffer(&stereo_buffer, 0); +} + + +static int +convert_swap_endian(void **data, int length) +{ + guint16 *ptr = *data; + int i; + for (i = 0; i < length; i += 2, ptr++) + *ptr = GUINT16_SWAP_LE_BE(*ptr); + + return i; +} + +static int +convert_swap_sign_and_endian_to_native(void **data, int length) +{ + guint16 *ptr = *data; + int i; + for (i = 0; i < length; i += 2, ptr++) + *ptr = GUINT16_SWAP_LE_BE(*ptr) ^ 1 << 15; + + return i; +} + +static int +convert_swap_sign_and_endian_to_alien(void **data, int length) +{ + guint16 *ptr = *data; + int i; + for (i = 0; i < length; i += 2, ptr++) + *ptr = GUINT16_SWAP_LE_BE(*ptr ^ 1 << 15); + + return i; +} + +static int +convert_swap_sign16(void **data, int length) +{ + gint16 *ptr = *data; + int i; + for (i = 0; i < length; i += 2, ptr++) + *ptr ^= 1 << 15; + + return i; +} + +static int +convert_swap_sign8(void **data, int length) +{ + gint8 *ptr = *data; + int i; + for (i = 0; i < length; i++) + *ptr++ ^= 1 << 7; + + return i; +} + +static int +convert_to_8_native_endian(void **data, int length) +{ + gint8 *output = *data; + gint16 *input = *data; + int i; + for (i = 0; i < length / 2; i++) + *output++ = *input++ >> 8; + + return i; +} + +static int +convert_to_8_native_endian_swap_sign(void **data, int length) +{ + gint8 *output = *data; + gint16 *input = *data; + int i; + for (i = 0; i < length / 2; i++) + *output++ = (*input++ >> 8) ^ (1 << 7); + + return i; +} + + +static int +convert_to_8_alien_endian(void **data, int length) +{ + gint8 *output = *data; + gint16 *input = *data; + int i; + for (i = 0; i < length / 2; i++) + *output++ = *input++ & 0xff; + + return i; +} + +static int +convert_to_8_alien_endian_swap_sign(void **data, int length) +{ + gint8 *output = *data; + gint16 *input = *data; + int i; + for (i = 0; i < length / 2; i++) + *output++ = (*input++ & 0xff) ^ (1 << 7); + + return i; +} + +static int +convert_to_16_native_endian(void **data, int length) +{ + guint8 *input = *data; + guint16 *output; + int i; + *data = oss_get_convert_buffer(&format_buffer, length * 2); + output = *data; + for (i = 0; i < length; i++) + *output++ = *input++ << 8; + + return i * 2; +} + +static int +convert_to_16_native_endian_swap_sign(void **data, int length) +{ + guint8 *input = *data; + guint16 *output; + int i; + *data = oss_get_convert_buffer(&format_buffer, length * 2); + output = *data; + for (i = 0; i < length; i++) + *output++ = (*input++ << 8) ^ (1 << 15); + + return i * 2; +} + + +static int +convert_to_16_alien_endian(void **data, int length) +{ + guint8 *input = *data; + guint16 *output; + int i; + *data = oss_get_convert_buffer(&format_buffer, length * 2); + output = *data; + for (i = 0; i < length; i++) + *output++ = *input++; + + return i * 2; +} + +static int +convert_to_16_alien_endian_swap_sign(void **data, int length) +{ + guint8 *input = *data; + guint16 *output; + int i; + *data = oss_get_convert_buffer(&format_buffer, length * 2); + output = *data; + for (i = 0; i < length; i++) + *output++ = *input++ ^ (1 << 7); + + return i * 2; +} + +int (*oss_get_convert_func(int output, int input)) (void **, int) { + if (output == input) + return NULL; + + if ((output == AFMT_U16_BE && input == AFMT_U16_LE) || + (output == AFMT_U16_LE && input == AFMT_U16_BE) || + (output == AFMT_S16_BE && input == AFMT_S16_LE) || + (output == AFMT_S16_LE && input == AFMT_S16_BE)) + return convert_swap_endian; + + if ((output == AFMT_U16_BE && input == AFMT_S16_BE) || + (output == AFMT_U16_LE && input == AFMT_S16_LE) || + (output == AFMT_S16_BE && input == AFMT_U16_BE) || + (output == AFMT_S16_LE && input == AFMT_U16_LE)) + return convert_swap_sign16; + + if ((IS_BIG_ENDIAN && + ((output == AFMT_U16_BE && input == AFMT_S16_LE) || + (output == AFMT_S16_BE && input == AFMT_U16_LE))) || + (!IS_BIG_ENDIAN && + ((output == AFMT_U16_LE && input == AFMT_S16_BE) || + (output == AFMT_S16_LE && input == AFMT_U16_BE)))) + return convert_swap_sign_and_endian_to_native; + + if ((!IS_BIG_ENDIAN && + ((output == AFMT_U16_BE && input == AFMT_S16_LE) || + (output == AFMT_S16_BE && input == AFMT_U16_LE))) || + (IS_BIG_ENDIAN && + ((output == AFMT_U16_LE && input == AFMT_S16_BE) || + (output == AFMT_S16_LE && input == AFMT_U16_BE)))) + return convert_swap_sign_and_endian_to_alien; + + if ((IS_BIG_ENDIAN && + ((output == AFMT_U8 && input == AFMT_U16_BE) || + (output == AFMT_S8 && input == AFMT_S16_BE))) || + (!IS_BIG_ENDIAN && + ((output == AFMT_U8 && input == AFMT_U16_LE) || + (output == AFMT_S8 && input == AFMT_S16_LE)))) + return convert_to_8_native_endian; + + if ((IS_BIG_ENDIAN && + ((output == AFMT_U8 && input == AFMT_S16_BE) || + (output == AFMT_S8 && input == AFMT_U16_BE))) || + (!IS_BIG_ENDIAN && + ((output == AFMT_U8 && input == AFMT_S16_LE) || + (output == AFMT_S8 && input == AFMT_U16_LE)))) + return convert_to_8_native_endian_swap_sign; + + if ((!IS_BIG_ENDIAN && + ((output == AFMT_U8 && input == AFMT_U16_BE) || + (output == AFMT_S8 && input == AFMT_S16_BE))) || + (IS_BIG_ENDIAN && + ((output == AFMT_U8 && input == AFMT_U16_LE) || + (output == AFMT_S8 && input == AFMT_S16_LE)))) + return convert_to_8_alien_endian; + + if ((!IS_BIG_ENDIAN && + ((output == AFMT_U8 && input == AFMT_S16_BE) || + (output == AFMT_S8 && input == AFMT_U16_BE))) || + (IS_BIG_ENDIAN && + ((output == AFMT_U8 && input == AFMT_S16_LE) || + (output == AFMT_S8 && input == AFMT_U16_LE)))) + return convert_to_8_alien_endian_swap_sign; + + if ((output == AFMT_U8 && input == AFMT_S8) || + (output == AFMT_S8 && input == AFMT_U8)) + return convert_swap_sign8; + + if ((IS_BIG_ENDIAN && + ((output == AFMT_U16_BE && input == AFMT_U8) || + (output == AFMT_S16_BE && input == AFMT_S8))) || + (!IS_BIG_ENDIAN && + ((output == AFMT_U16_LE && input == AFMT_U8) || + (output == AFMT_S16_LE && input == AFMT_S8)))) + return convert_to_16_native_endian; + + if ((IS_BIG_ENDIAN && + ((output == AFMT_U16_BE && input == AFMT_S8) || + (output == AFMT_S16_BE && input == AFMT_U8))) || + (!IS_BIG_ENDIAN && + ((output == AFMT_U16_LE && input == AFMT_S8) || + (output == AFMT_S16_LE && input == AFMT_U8)))) + return convert_to_16_native_endian_swap_sign; + + if ((!IS_BIG_ENDIAN && + ((output == AFMT_U16_BE && input == AFMT_U8) || + (output == AFMT_S16_BE && input == AFMT_S8))) || + (IS_BIG_ENDIAN && + ((output == AFMT_U16_LE && input == AFMT_U8) || + (output == AFMT_S16_LE && input == AFMT_S8)))) + return convert_to_16_alien_endian; + + if ((!IS_BIG_ENDIAN && + ((output == AFMT_U16_BE && input == AFMT_S8) || + (output == AFMT_S16_BE && input == AFMT_U8))) || + (IS_BIG_ENDIAN && + ((output == AFMT_U16_LE && input == AFMT_S8) || + (output == AFMT_S16_LE && input == AFMT_U8)))) + return convert_to_16_alien_endian_swap_sign; + + g_warning("Translation needed, but not available.\n" + "Input: %d; Output %d.", input, output); + return NULL; +} + +static int +convert_mono_to_stereo(void **data, int length, int fmt) +{ + int i; + void *outbuf = oss_get_convert_buffer(&stereo_buffer, length * 2); + + if (fmt == AFMT_U8 || fmt == AFMT_S8) { + guint8 *output = outbuf, *input = *data; + for (i = 0; i < length; i++) { + *output++ = *input; + *output++ = *input; + input++; + } + } + else { + guint16 *output = outbuf, *input = *data; + for (i = 0; i < length / 2; i++) { + *output++ = *input; + *output++ = *input; + input++; + } + } + *data = outbuf; + + return length * 2; +} + +static int +convert_stereo_to_mono(void **data, int length, int fmt) +{ + int i; + + switch (fmt) { + case AFMT_U8: + { + guint8 *output = *data, *input = *data; + for (i = 0; i < length / 2; i++) { + guint16 tmp; + tmp = *input++; + tmp += *input++; + *output++ = tmp / 2; + } + } + break; + case AFMT_S8: + { + gint8 *output = *data, *input = *data; + for (i = 0; i < length / 2; i++) { + gint16 tmp; + tmp = *input++; + tmp += *input++; + *output++ = tmp / 2; + } + } + break; + case AFMT_U16_LE: + { + guint16 *output = *data, *input = *data; + for (i = 0; i < length / 4; i++) { + guint32 tmp; + guint16 stmp; + tmp = GUINT16_FROM_LE(*input); + input++; + tmp += GUINT16_FROM_LE(*input); + input++; + stmp = tmp / 2; + *output++ = GUINT16_TO_LE(stmp); + } + } + break; + case AFMT_U16_BE: + { + guint16 *output = *data, *input = *data; + for (i = 0; i < length / 4; i++) { + guint32 tmp; + guint16 stmp; + tmp = GUINT16_FROM_BE(*input); + input++; + tmp += GUINT16_FROM_BE(*input); + input++; + stmp = tmp / 2; + *output++ = GUINT16_TO_BE(stmp); + } + } + break; + case AFMT_S16_LE: + { + gint16 *output = *data, *input = *data; + for (i = 0; i < length / 4; i++) { + gint32 tmp; + gint16 stmp; + tmp = GINT16_FROM_LE(*input); + input++; + tmp += GINT16_FROM_LE(*input); + input++; + stmp = tmp / 2; + *output++ = GINT16_TO_LE(stmp); + } + } + break; + case AFMT_S16_BE: + { + gint16 *output = *data, *input = *data; + for (i = 0; i < length / 4; i++) { + gint32 tmp; + gint16 stmp; + tmp = GINT16_FROM_BE(*input); + input++; + tmp += GINT16_FROM_BE(*input); + input++; + stmp = tmp / 2; + *output++ = GINT16_TO_BE(stmp); + } + } + break; + default: + g_error("unknown format"); + } + + return length / 2; +} + +int (*oss_get_stereo_convert_func(int output, int input)) (void **, int, int) { + if (output == input) + return NULL; + + if (input == 1 && output == 2) + return convert_mono_to_stereo; + if (input == 2 && output == 1) + return convert_stereo_to_mono; + + g_warning("Input has %d channels, soundcard uses %d channels\n" + "No conversion is available", input, output); + return NULL; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/Output/CoreAudio/init.c Thu Sep 07 09:11:46 2006 -0700 @@ -0,0 +1,61 @@ +/* BMP - Cross-platform multimedia player + * Copyright (C) 2003-2004 BMP development team. + * + * Based on XMMS: + * Copyright (C) 1998-2003 XMMS development team. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <glib.h> +#include <string.h> +#include <libaudacious/configdb.h> +#include "OSS.h" + + +OSSConfig oss_cfg; + +void +oss_init(void) +{ + ConfigDb *db; + + memset(&oss_cfg, 0, sizeof(OSSConfig)); + + oss_cfg.audio_device = 0; + oss_cfg.mixer_device = 0; + oss_cfg.buffer_size = 3000; + oss_cfg.prebuffer = 25; + oss_cfg.use_alt_audio_device = FALSE; + oss_cfg.alt_audio_device = NULL; + oss_cfg.use_master = 0; + + if ((db = bmp_cfg_db_open())) { + bmp_cfg_db_get_int(db, "OSS", "audio_device", &oss_cfg.audio_device); + bmp_cfg_db_get_int(db, "OSS", "mixer_device", &oss_cfg.mixer_device); + bmp_cfg_db_get_int(db, "OSS", "buffer_size", &oss_cfg.buffer_size); + bmp_cfg_db_get_int(db, "OSS", "prebuffer", &oss_cfg.prebuffer); + bmp_cfg_db_get_bool(db, "OSS", "use_master", &oss_cfg.use_master); + bmp_cfg_db_get_bool(db, "OSS", "use_alt_audio_device", + &oss_cfg.use_alt_audio_device); + bmp_cfg_db_get_string(db, "OSS", "alt_audio_device", + &oss_cfg.alt_audio_device); + bmp_cfg_db_get_bool(db, "OSS", "use_alt_mixer_device", + &oss_cfg.use_alt_mixer_device); + bmp_cfg_db_get_string(db, "OSS", "alt_mixer_device", + &oss_cfg.alt_mixer_device); + bmp_cfg_db_close(db); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/Output/CoreAudio/mixer.c Thu Sep 07 09:11:46 2006 -0700 @@ -0,0 +1,110 @@ +/* BMP - Cross-platform multimedia player + * Copyright (C) 2003-2004 BMP development team. + * + * Based on XMMS: + * Copyright (C) 1998-2003 XMMS development team. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#include <glib.h> +#include <stdio.h> +#include <string.h> + +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/ioctl.h> + +#include "OSS.h" + + +static char * +get_mixer_device(void) +{ + char *name; + + if (oss_cfg.use_alt_mixer_device && oss_cfg.alt_mixer_device) + name = g_strdup(oss_cfg.alt_mixer_device); + else if (oss_cfg.mixer_device > 0) + name = g_strdup_printf("%s%d", DEV_MIXER, oss_cfg.mixer_device); + else + name = g_strdup(DEV_MIXER); + + return name; +} + +void +oss_get_volume(int *l, int *r) +{ + int fd, v, devs; + long cmd; + gchar *devname; + + devname = get_mixer_device(); + fd = open(devname, O_RDONLY); + g_free(devname); + + /* + * We dont show any errors if this fails, as this is called + * rather often + */ + if (fd != -1) { + ioctl(fd, SOUND_MIXER_READ_DEVMASK, &devs); + if ((devs & SOUND_MASK_PCM) && (oss_cfg.use_master == 0)) + cmd = SOUND_MIXER_READ_PCM; + else if ((devs & SOUND_MASK_VOLUME) && (oss_cfg.use_master == 1)) + cmd = SOUND_MIXER_READ_VOLUME; + else { + close(fd); + return; + } + ioctl(fd, cmd, &v); + *r = (v & 0xFF00) >> 8; + *l = (v & 0x00FF); + close(fd); + } +} + +void +oss_set_volume(int l, int r) +{ + int fd, v, devs; + long cmd; + gchar *devname; + + devname = get_mixer_device(); + fd = open(devname, O_RDONLY); + + if (fd != -1) { + ioctl(fd, SOUND_MIXER_READ_DEVMASK, &devs); + if ((devs & SOUND_MASK_PCM) && (oss_cfg.use_master == 0)) + cmd = SOUND_MIXER_WRITE_PCM; + else if ((devs & SOUND_MASK_VOLUME) && (oss_cfg.use_master == 1)) + cmd = SOUND_MIXER_WRITE_VOLUME; + else { + close(fd); + return; + } + v = (r << 8) | l; + ioctl(fd, cmd, &v); + close(fd); + } + else + g_warning("oss_set_volume(): Failed to open mixer device (%s): %s", + devname, strerror(errno)); + g_free(devname); +}