summaryrefslogtreecommitdiff
path: root/src/audio_out/audio_alsa_out.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/audio_out/audio_alsa_out.c')
-rw-r--r--src/audio_out/audio_alsa_out.c315
1 files changed, 302 insertions, 13 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;