diff options
author | Daniel Caujolle-Bert <f1rmb@users.sourceforge.net> | 2001-10-01 23:04:57 +0000 |
---|---|---|
committer | Daniel Caujolle-Bert <f1rmb@users.sourceforge.net> | 2001-10-01 23:04:57 +0000 |
commit | c48149133f36b24fb87924209a950681be0727c7 (patch) | |
tree | 18a1126eeaecf6a6310fb9d74e91a531b2c62f40 /src/audio_out | |
parent | 9d7e1140adde62d0959af667474a54470d98968a (diff) | |
download | xine-lib-c48149133f36b24fb87924209a950681be0727c7.tar.gz xine-lib-c48149133f36b24fb87924209a950681be0727c7.tar.bz2 |
Add simple mixer control in xine-engine/ao plugins. Fixed some missings
in audio_decoder. xine-ui warn at compile time due of a #warning i
added, i will remove it pretty soon.
CVS patchset: 715
CVS date: 2001/10/01 23:04:57
Diffstat (limited to 'src/audio_out')
-rw-r--r-- | src/audio_out/audio_alsa_out.c | 315 | ||||
-rw-r--r-- | src/audio_out/audio_oss_out.c | 220 |
2 files changed, 490 insertions, 45 deletions
diff --git a/src/audio_out/audio_alsa_out.c b/src/audio_out/audio_alsa_out.c index e887b277d..0ec7de759 100644 --- a/src/audio_out/audio_alsa_out.c +++ b/src/audio_out/audio_alsa_out.c @@ -26,7 +26,7 @@ * (c) 2001 James Courtier-Dutton <James@superbug.demon.co.uk> * * - * $Id: audio_alsa_out.c,v 1.28 2001/09/14 20:44:01 jcdutton Exp $ + * $Id: audio_alsa_out.c,v 1.29 2001/10/01 23:04:57 f1rmb Exp $ */ #ifdef HAVE_CONFIG_H @@ -40,7 +40,8 @@ #include <stdlib.h> #include <fcntl.h> #include <math.h> -#include <sys/asoundlib.h> +#include <alloca.h> +#include <alsa/asoundlib.h> #include <sys/ioctl.h> #include <inttypes.h> @@ -75,11 +76,18 @@ #define AO_OUT_ALSA_IFACE_VERSION 2 -#define GAP_TOLERANCE 5000 +#define GAP_TOLERANCE 5000 + +#define MIXER_MASK_LEFT (1 << 0) +#define MIXER_MASK_RIGHT (1 << 1) +#define MIXER_MASK_STEREO (MIXER_MASK_LEFT|MIXER_MASK_RIGHT) typedef struct alsa_driver_s { ao_driver_t ao_driver; + + config_values_t *config; + char audio_default_device[20]; char audio_front_device[20]; char audio_surround40_device[20]; @@ -97,9 +105,46 @@ typedef struct alsa_driver_s { uint32_t bytes_per_frame; uint32_t bytes_in_buffer; /* number of bytes writen to audio hardware */ + struct { + char *name; + snd_mixer_t *handle; + snd_mixer_elem_t *elem; + long min; + long max; + long left_vol; + long right_vol; + int mute; + } mixer; + } alsa_driver_t; - static snd_output_t *jcd_out; +static snd_output_t *jcd_out; + +static int ao_alsa_get_percent_from_volume(long val, long min, long max) +{ + int range = max - min; + int tmp; + + if (range == 0) + return 0; + val -= min; + tmp = rint((double)val / (double)range * 100); + return tmp; +} + +static long ao_alsa_get_volume_from_percent(int val, long min, long max) +{ + int range = max - min; + long tmp; + + if (range == 0) + return 0; + val -= min; + tmp = (long) ((range * val) / 100); + return tmp; +} + + /* * open the audio device for writing to */ @@ -402,44 +447,283 @@ static uint32_t ao_alsa_get_capabilities (ao_driver_t *this_gen) { static void ao_alsa_exit(ao_driver_t *this_gen) { alsa_driver_t *this = (alsa_driver_t *) this_gen; + + config_values_t *config = this->config; + + config->set_int (config, "mixer_volume", + (((ao_alsa_get_percent_from_volume(this->mixer.left_vol, + this->mixer.min, this->mixer.max)) + + (ao_alsa_get_percent_from_volume(this->mixer.right_vol, + this->mixer.min, this->mixer.max))) /2)); + config->save(config); + if (this->audio_fd) snd_pcm_close(this->audio_fd); free (this); } -static int ao_alsa_get_property (ao_driver_t *this, int property) { +static int ao_alsa_get_property (ao_driver_t *this_gen, int property) { + alsa_driver_t *this = (alsa_driver_t *) this_gen; + int err; - /* FIXME: implement some properties switch(property) { case AO_PROP_MIXER_VOL: - break; case AO_PROP_PCM_VOL: + if(this->mixer.elem) { + if((err = snd_mixer_selem_get_playback_volume(this->mixer.elem, SND_MIXER_SCHN_FRONT_LEFT, + &this->mixer.left_vol)) < 0) { + printf("snd_mixer_selem_get_playback_volume(): %s\n", snd_strerror(err)); + goto __done; + } + + if((err = snd_mixer_selem_get_playback_volume(this->mixer.elem, SND_MIXER_SCHN_FRONT_RIGHT, + &this->mixer.right_vol)) < 0) { + printf("snd_mixer_selem_get_playback_volume(): %s\n", snd_strerror(err)); + goto __done; + } + + __done: + return (((ao_alsa_get_percent_from_volume(this->mixer.left_vol, + this->mixer.min, this->mixer.max)) + + (ao_alsa_get_percent_from_volume(this->mixer.right_vol, + this->mixer.min, this->mixer.max))) /2); + } break; + case AO_PROP_MUTE_VOL: + return (this->mixer.mute) ? 1 : 0; break; } - */ + return 0; } /* * */ -static int ao_alsa_set_property (ao_driver_t *this, int property, int value) { +static int ao_alsa_set_property (ao_driver_t *this_gen, int property, int value) { + alsa_driver_t *this = (alsa_driver_t *) this_gen; + int err; - /* FIXME: Implement property support. switch(property) { case AO_PROP_MIXER_VOL: - break; case AO_PROP_PCM_VOL: + if(this->mixer.elem) { + + this->mixer.left_vol = this->mixer.right_vol = ao_alsa_get_volume_from_percent(value, this->mixer.min, this->mixer.max); + + if((err = snd_mixer_selem_set_playback_volume(this->mixer.elem, SND_MIXER_SCHN_FRONT_LEFT, + this->mixer.left_vol)) < 0) { + printf("snd_mixer_selem_get_playback_volume(): %s\n", snd_strerror(err)); + return ~value; + } + + if((err = snd_mixer_selem_set_playback_volume(this->mixer.elem, SND_MIXER_SCHN_FRONT_RIGHT, + this->mixer.right_vol)) < 0) { + printf("snd_mixer_selem_get_playback_volume(): %s\n", snd_strerror(err)); + return ~value; + } + + return value; + } break; + case AO_PROP_MUTE_VOL: + if(this->mixer.elem) { + int sw; + int old_mute = this->mixer.mute; + + this->mixer.mute = (value) ? MIXER_MASK_STEREO : 0; + + if ((this->mixer.mute != old_mute) + && snd_mixer_selem_has_playback_switch(this->mixer.elem)) { + if (snd_mixer_selem_has_playback_switch_joined(this->mixer.elem)) { + snd_mixer_selem_get_playback_switch(this->mixer.elem, SND_MIXER_SCHN_FRONT_LEFT, &sw); + snd_mixer_selem_set_playback_switch_all(this->mixer.elem, !sw); + } else { + if (this->mixer.mute & MIXER_MASK_LEFT) { + snd_mixer_selem_get_playback_switch(this->mixer.elem, SND_MIXER_SCHN_FRONT_LEFT, &sw); + snd_mixer_selem_set_playback_switch(this->mixer.elem, SND_MIXER_SCHN_FRONT_LEFT, !sw); + } + if (SND_MIXER_SCHN_FRONT_RIGHT != SND_MIXER_SCHN_UNKNOWN && + (this->mixer.mute & MIXER_MASK_RIGHT)) { + snd_mixer_selem_get_playback_switch(this->mixer.elem, SND_MIXER_SCHN_FRONT_RIGHT, &sw); + snd_mixer_selem_set_playback_switch(this->mixer.elem, SND_MIXER_SCHN_FRONT_RIGHT, !sw); + } + } + } + + return value; + } + + return ~value; break; } - */ return ~value; } +static void ao_alsa_mixer_init(ao_driver_t *this_gen) { + alsa_driver_t *this = (alsa_driver_t *) this_gen; + config_values_t *config = this->config; + snd_ctl_card_info_t *hw_info; + snd_ctl_t *ctl_handle; + int err; + void *mixer_sid; + snd_mixer_elem_t *elem; + int mixer_n_selems = 0; + snd_mixer_selem_id_t *sid; + int loop = 0; + int found; + int sw; + + snd_ctl_card_info_alloca(&hw_info); + + if ((err = snd_ctl_open (&ctl_handle, this->audio_default_device, 0)) < 0) { + printf("snd_ctl_open(): %s\n", snd_strerror(err)); + return; + } + + if ((err = snd_ctl_card_info (ctl_handle, hw_info)) < 0) { + printf("snd_ctl_card_info(): %s\n", snd_strerror(err)); + snd_ctl_close(ctl_handle); + return; + } + + snd_ctl_close (ctl_handle); + + /* + * Open mixer device + */ + if ((err = snd_mixer_open (&this->mixer.handle, 0)) < 0) { + printf("snd_mixer_open(): %s\n", snd_strerror(err)); + return; + } + + if ((err = snd_mixer_attach (this->mixer.handle, this->audio_default_device)) < 0) { + printf("snd_mixer_attach(): %s\n", snd_strerror(err)); + snd_mixer_close(this->mixer.handle); + return; + } + + if ((err = snd_mixer_selem_register (this->mixer.handle, NULL, NULL)) < 0) { + printf("snd_mixer_selem_register(): %s\n", snd_strerror(err)); + snd_mixer_close(this->mixer.handle); + return; + } + + // snd_mixer_set_callback (mixer_handle, mixer_event); + + if ((err = snd_mixer_load (this->mixer.handle)) < 0) { + printf("snd_mixer_load(): %s\n", snd_strerror(err)); + snd_mixer_close(this->mixer.handle); + return; + } + + mixer_sid = alloca(snd_mixer_selem_id_sizeof() * snd_mixer_get_count(this->mixer.handle)); + if (mixer_sid == NULL) { + printf("alloca() failed: %s\n", strerror(errno)); + snd_mixer_close(this->mixer.handle); + return; + } + + __again: + + found = 0; + mixer_n_selems = 0; + for (elem = snd_mixer_first_elem(this->mixer.handle); elem; elem = snd_mixer_elem_next(elem)) { + sid = (snd_mixer_selem_id_t *)(((char *)mixer_sid) + snd_mixer_selem_id_sizeof() * mixer_n_selems); + + if (!snd_mixer_selem_is_active(elem)) + continue; + + snd_mixer_selem_get_id(elem, sid); + mixer_n_selems++; + + if(!strcmp((snd_mixer_selem_get_name(elem)), this->mixer.name)) { + + // printf("found %s\n", snd_mixer_selem_get_name(elem)); + + this->mixer.elem = elem; + + snd_mixer_selem_get_playback_volume_range(this->mixer.elem, + &this->mixer.min, &this->mixer.max); + if((err = snd_mixer_selem_get_playback_volume(this->mixer.elem, SND_MIXER_SCHN_FRONT_LEFT, + &this->mixer.left_vol)) < 0) { + printf("snd_mixer_selem_get_playback_volume(): %s\n", snd_strerror(err)); + snd_mixer_close(this->mixer.handle); + return; + } + + if((err = snd_mixer_selem_get_playback_volume(this->mixer.elem, SND_MIXER_SCHN_FRONT_RIGHT, + &this->mixer.right_vol)) < 0) { + printf("snd_mixer_selem_get_playback_volume(): %s\n", snd_strerror(err)); + snd_mixer_close(this->mixer.handle); + return; + } + + /* Channels mute */ + this->mixer.mute = 0; + if(snd_mixer_selem_has_playback_switch(this->mixer.elem)) { + + if (snd_mixer_selem_has_playback_switch_joined(this->mixer.elem)) { + snd_mixer_selem_get_playback_switch(this->mixer.elem, SND_MIXER_SCHN_FRONT_LEFT, &sw); + if(!sw) + this->mixer.mute = MIXER_MASK_STEREO; + } + else { + if (this->mixer.mute & MIXER_MASK_LEFT) { + snd_mixer_selem_get_playback_switch(this->mixer.elem, SND_MIXER_SCHN_FRONT_LEFT, &sw); + if(!sw) + this->mixer.mute |= MIXER_MASK_LEFT; + } + if (SND_MIXER_SCHN_FRONT_RIGHT != SND_MIXER_SCHN_UNKNOWN && + (this->mixer.mute & MIXER_MASK_RIGHT)) { + snd_mixer_selem_get_playback_switch(this->mixer.elem, SND_MIXER_SCHN_FRONT_RIGHT, &sw); + if(!sw) + this->mixer.mute |= MIXER_MASK_RIGHT; + } + } + + this->capabilities |= AO_CAP_MUTE_VOL; + } + + found++; + + goto __mixer_found; + } + } + + if(loop) + goto __mixer_found; /* Yes, untrue but... ;-) */ + + if(!strcmp(this->mixer.name, "PCM")) { + config->set_str(config, "mixer_name", "Master"); + loop++; + } + else { + config->set_str(config, "mixer_name", "PCM"); + } + + config->save(config); + + this->mixer.name = config->lookup_str(config, "mixer_name", "PCM"); + + goto __again; + + __mixer_found: + + /* + * Ugly: yes[*] no[ ] + */ + if(found) { + if(!strcmp(this->mixer.name, "Master")) + this->capabilities |= AO_CAP_MIXER_VOL; + else + this->capabilities |= AO_CAP_PCM_VOL; + } + +} + ao_driver_t *init_audio_out_plugin (config_values_t *config) { alsa_driver_t *this; @@ -514,7 +798,12 @@ ao_driver_t *init_audio_out_plugin (config_values_t *config) { this->capabilities |= AO_CAP_MODE_AC5; } printf("audio_alsa_out: Capabilities 0x%X\n",this->capabilities); - + + this->config = config; + + this->mixer.name = config->lookup_str(config, "mixer_name", "PCM"); + ao_alsa_mixer_init(&this->ao_driver); + this->ao_driver.get_capabilities = ao_alsa_get_capabilities; this->ao_driver.get_property = ao_alsa_get_property; this->ao_driver.set_property = ao_alsa_set_property; diff --git a/src/audio_out/audio_oss_out.c b/src/audio_out/audio_oss_out.c index b176cfb14..cdf1dcf44 100644 --- a/src/audio_out/audio_oss_out.c +++ b/src/audio_out/audio_oss_out.c @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * - * $Id: audio_oss_out.c,v 1.40 2001/09/30 23:12:05 heikos Exp $ + * $Id: audio_oss_out.c,v 1.41 2001/10/01 23:04:57 f1rmb Exp $ * * 20-8-2001 First implementation of Audio sync and Audio driver separation. * Copyright (C) 2001 James Courtier-Dutton James@superbug.demon.co.uk @@ -100,21 +100,31 @@ static int use_getodelay = 0; typedef struct oss_driver_s { - ao_driver_t ao_driver; - char audio_dev[20]; - int audio_fd; - int capabilities; - int mode; - - int32_t output_sample_rate, input_sample_rate; - uint32_t num_channels; - uint32_t bits_per_sample; - uint32_t bytes_per_frame; - uint32_t bytes_in_buffer; /* number of bytes writen to audio hardware */ + ao_driver_t ao_driver; + char audio_dev[20]; + int audio_fd; + int capabilities; + int mode; + + config_values_t *config; + + int32_t output_sample_rate, input_sample_rate; + uint32_t num_channels; + uint32_t bits_per_sample; + uint32_t bytes_per_frame; + uint32_t bytes_in_buffer; /* number of bytes writen to audio hardware */ - int audio_started; - int audio_has_realtime; /* OSS driver supports real-time */ - int static_delay; /* estimated delay for non-realtime drivers */ + int audio_started; + int audio_has_realtime; /* OSS driver supports real-time */ + int static_delay; /* estimated delay for non-realtime drivers */ + + struct { + char *name; + int prop; + int volume; + int mute; + } mixer; + } oss_driver_t; /* @@ -368,6 +378,10 @@ static uint32_t ao_oss_get_capabilities (ao_driver_t *this_gen) { static void ao_oss_exit(ao_driver_t *this_gen) { oss_driver_t *this = (oss_driver_t *) this_gen; + config_values_t *config = this->config; + + config->set_int (config, "mixer_volume", this->mixer.volume); + config->save(config); if (this->audio_fd != -1) close(this->audio_fd); @@ -375,41 +389,128 @@ static void ao_oss_exit(ao_driver_t *this_gen) free (this); } -/* - * - */ static int ao_oss_get_property (ao_driver_t *this_gen, int property) { - oss_driver_t *this = (oss_driver_t *) this; + oss_driver_t *this = (oss_driver_t *) this_gen; + int mixer_fd; + int audio_devs; - /* FIXME: implement some properties switch(property) { - case AO_PROP_MIXER_VOL: - break; case AO_PROP_PCM_VOL: + case AO_PROP_MIXER_VOL: + mixer_fd = open(this->mixer.name, O_RDONLY); + if(mixer_fd != -1) { + int cmd = 0; + int v; + + ioctl(mixer_fd, SOUND_MIXER_READ_DEVMASK, &audio_devs); + + if(audio_devs & SOUND_MASK_PCM) + cmd = SOUND_MIXER_READ_PCM; + else if(audio_devs & SOUND_MASK_VOLUME) + cmd = SOUND_MIXER_READ_VOLUME; + else { + close(mixer_fd); + return 0; + } + ioctl(mixer_fd, cmd, &v); + this->mixer.volume = (((v & 0xFF00) >> 8) + (v & 0x00FF)) / 2; + close(mixer_fd); + } + else + printf("%s(): open() %s failed: %s\n", + __FUNCTION__, this->mixer.name, strerror(errno)); + + return this->mixer.volume; break; + case AO_PROP_MUTE_VOL: + return this->mixer.mute; break; } - */ + return 0; } -/* - * - */ static int ao_oss_set_property (ao_driver_t *this_gen, int property, int value) { - oss_driver_t *this = (oss_driver_t *) this; + oss_driver_t *this = (oss_driver_t *) this_gen; + int mixer_fd; + int audio_devs; - /* FIXME: Implement property support. switch(property) { - case AO_PROP_MIXER_VOL: - break; case AO_PROP_PCM_VOL: + case AO_PROP_MIXER_VOL: + if(!this->mixer.mute) { + + mixer_fd = open(this->mixer.name, O_RDONLY); + + if(mixer_fd != -1) { + int cmd = 0; + int v; + + ioctl(mixer_fd, SOUND_MIXER_READ_DEVMASK, &audio_devs); + + if(audio_devs & SOUND_MASK_PCM) + cmd = SOUND_MIXER_WRITE_PCM; + else if(audio_devs & SOUND_MASK_VOLUME) + cmd = SOUND_MIXER_WRITE_VOLUME; + else { + close(mixer_fd); + return ~value; + } + v = (value << 8) | value; + ioctl(mixer_fd, cmd, &v); + close(mixer_fd); + + if(!this->mixer.mute) + this->mixer.volume = value; + + } + else + printf("%s(): open() %s failed: %s\n", + __FUNCTION__, this->mixer.name, strerror(errno)); + } + else + this->mixer.volume = value; + + return this->mixer.volume; break; + case AO_PROP_MUTE_VOL: + this->mixer.mute = (value) ? 1 : 0; + + if(this->mixer.mute) { + + mixer_fd = open(this->mixer.name, O_RDONLY); + + if(mixer_fd != -1) { + int cmd = 0; + int v = 0; + + ioctl(mixer_fd, SOUND_MIXER_READ_DEVMASK, &audio_devs); + + if(audio_devs & SOUND_MASK_PCM) + cmd = SOUND_MIXER_WRITE_PCM; + else if(audio_devs & SOUND_MASK_VOLUME) + cmd = SOUND_MIXER_WRITE_VOLUME; + else { + close(mixer_fd); + return ~value; + } + + ioctl(mixer_fd, cmd, &v); + close(mixer_fd); + + } + else + printf("%s(): open() %s failed: %s\n", + __FUNCTION__, this->mixer.name, strerror(errno)); + } + else + (void) ao_oss_set_property(&this->ao_driver, this->mixer.prop, this->mixer.volume); + + return value; break; } - */ return ~value; } @@ -428,7 +529,7 @@ ao_driver_t *init_audio_out_plugin (config_values_t *config) { int devnum; int audio_fd; int num_channels, status, arg; - + this = (oss_driver_t *) malloc (sizeof (oss_driver_t)); /* @@ -574,7 +675,61 @@ ao_driver_t *init_audio_out_plugin (config_values_t *config) { } printf ("\n"); + + /* + * Mixer initialisation. + */ + __again: + this->mixer.name = config->lookup_str(config, "mixer_name", "/dev/mixer"); + { + int mixer_fd; + int audio_devs; + + mixer_fd = open(this->mixer.name, O_RDONLY); + + if(mixer_fd != -1) { + ioctl(mixer_fd, SOUND_MIXER_READ_DEVMASK, &audio_devs); + + if(audio_devs & SOUND_MASK_PCM) { + this->capabilities |= AO_CAP_PCM_VOL; + this->mixer.prop = AO_PROP_PCM_VOL; + } + else if(audio_devs & SOUND_MASK_VOLUME) { + this->capabilities |= AO_CAP_MIXER_VOL; + this->mixer.prop = AO_PROP_MIXER_VOL; + } + + /* + * This is obsolete in Linux kernel OSS + * implementation, so this will certainly doesn't work. + * So we just simulate the mute stuff + */ + /* + if(audio_devs & SOUND_MASK_MUTE) + this->capabilities |= AO_CAP_MUTE_VOL; + */ + this->capabilities |= AO_CAP_MUTE_VOL; + + } + else { + if(strcmp(this->mixer.name, "/dev/mixer")) { + config->set_str(config, "mixer_name", "/dev/mixer"); + config->save(config); + goto __again; + } + else + printf("%s(): open() %s failed: %s\n", + __FUNCTION__, this->mixer.name, strerror(errno)); + } + + this->mixer.mute = 0; + this->mixer.volume = ao_oss_get_property (&this->ao_driver, this->mixer.prop); + this->mixer.volume = config->lookup_int (config, "mixer_volume", 50); + + (void) ao_oss_set_property(&this->ao_driver, this->mixer.prop, this->mixer.volume); + + } close (audio_fd); this->output_sample_rate = 0; @@ -582,6 +737,7 @@ ao_driver_t *init_audio_out_plugin (config_values_t *config) { this->static_delay = config->lookup_int (config, "oss_static_delay", 1000); + this->config = config; this->ao_driver.get_capabilities = ao_oss_get_capabilities; this->ao_driver.get_property = ao_oss_get_property; this->ao_driver.set_property = ao_oss_set_property; |