diff options
Diffstat (limited to 'src/volume.c')
-rw-r--r-- | src/volume.c | 508 |
1 files changed, 89 insertions, 419 deletions
diff --git a/src/volume.c b/src/volume.c index 01976888d..e41d1d0e1 100644 --- a/src/volume.c +++ b/src/volume.c @@ -15,475 +15,141 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - #include "volume.h" + #include "conf.h" #include "player_control.h" #include "utils.h" #include "idle.h" #include "pcm_utils.h" #include "config.h" +#include "audio.h" #include <glib.h> - #include <math.h> #include <string.h> -#ifdef HAVE_OSS -#include <sys/ioctl.h> -#include <sys/soundcard.h> -#endif -#ifdef HAVE_ALSA -#include <alsa/asoundlib.h> -#endif - #undef G_LOG_DOMAIN #define G_LOG_DOMAIN "volume" #define VOLUME_MIXER_TYPE_SOFTWARE 0 -#define VOLUME_MIXER_TYPE_OSS 1 -#define VOLUME_MIXER_TYPE_ALSA 2 +#define VOLUME_MIXER_TYPE_HARDWARE 1 #define VOLUME_MIXER_SOFTWARE_DEFAULT "" -#define VOLUME_MIXER_OSS_DEFAULT "/dev/mixer" -#define VOLUME_MIXER_ALSA_DEFAULT "default" -#define VOLUME_MIXER_ALSA_CONTROL_DEFAULT "PCM" #define SW_VOLUME_STATE "sw_volume: " -#ifdef HAVE_OSS -#define VOLUME_MIXER_TYPE_DEFAULT VOLUME_MIXER_TYPE_OSS -#define VOLUME_MIXER_DEVICE_DEFAULT VOLUME_MIXER_OSS_DEFAULT -#else -#ifdef HAVE_ALSA -#define VOLUME_MIXER_TYPE_DEFAULT VOLUME_MIXER_TYPE_ALSA -#define VOLUME_MIXER_DEVICE_DEFAULT VOLUME_MIXER_ALSA_DEFAULT -#else -#define VOLUME_MIXER_TYPE_DEFAULT VOLUME_MIXER_TYPE_SOFTWARE -#define VOLUME_MIXER_DEVICE_DEFAULT VOLUME_MIXER_SOFTWARE_DEFAULT -#endif -#endif - -static int volume_mixerType = VOLUME_MIXER_TYPE_DEFAULT; -static const char *volume_mixerDevice = VOLUME_MIXER_DEVICE_DEFAULT; - -static int volume_softwareSet = 100; - -#ifdef HAVE_OSS -static int volume_ossFd = -1; -static int volume_ossControl = SOUND_MIXER_PCM; -#endif - -#ifdef HAVE_ALSA -static snd_mixer_t *volume_alsaMixerHandle; -static snd_mixer_elem_t *volume_alsaElem; -static long volume_alsaMin; -static long volume_alsaMax; -static int volume_alsaSet = -1; -#endif - -#ifdef HAVE_OSS - -static void closeOssMixer(void) -{ - while (close(volume_ossFd) && errno == EINTR) ; - volume_ossFd = -1; -} +const struct audio_output_plugin *default_mixer; +static int volume_mixer_type = VOLUME_MIXER_TYPE_HARDWARE; +static int volume_software_set = 100; -static int -oss_find_mixer(const char *name) +void volume_finish(void) { - const char *labels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS; - size_t name_length = strlen(name); - - for (unsigned i = 0; i < SOUND_MIXER_NRDEVICES; i++) { - if (strncasecmp(name, labels[i], name_length) == 0 && - (labels[i][name_length] == 0 || - labels[i][name_length] == ' ')) - return i; - } - - return -1; } -static int prepOssMixer(const char *device) +static void +mixer_reconfigure(char *driver) { - ConfigParam *param; + ConfigParam *newparam, *param; - if ((volume_ossFd = open(device, O_RDONLY)) < 0) { - g_warning("unable to open oss mixer \"%s\"", device); - return -1; - } + //create parameter list + newparam = newConfigParam(NULL, -1); - if ((param = getConfigParam(CONF_MIXER_CONTROL))) { - int i; - int devmask = 0; - - if (ioctl(volume_ossFd, SOUND_MIXER_READ_DEVMASK, &devmask) < 0) { - g_warning("errors getting read_devmask for oss mixer"); - closeOssMixer(); - return -1; - } - - i = oss_find_mixer(param->value); - - if (i < 0) { - g_warning("mixer control \"%s\" not found at line %i", - param->value, param->line); - closeOssMixer(); - return -1; - } else if (!((1 << i) & devmask)) { - g_warning("mixer control \"%s\" not usable at line %i", - param->value, param->line); - closeOssMixer(); - return -1; - } - - volume_ossControl = i; - } - - return 0; -} - -static int ensure_oss_open(void) -{ - if ((volume_ossFd < 0 && prepOssMixer(volume_mixerDevice) < 0)) - return -1; - return 0; -} - -static int getOssVolumeLevel(void) -{ - int left, right, level; - - if (ensure_oss_open() < 0) - return -1; - - if (ioctl(volume_ossFd, MIXER_READ(volume_ossControl), &level) < 0) { - closeOssMixer(); - g_warning("unable to read volume"); - return -1; - } - - left = level & 0xff; - right = (level & 0xff00) >> 8; - - if (left != right) { - g_warning("volume for left and right is not the same, \"%i\" " - "and \"%i\"", left, right); - } - - return left; -} - -static int changeOssVolumeLevel(int change, int rel) -{ - int current; - int new; - int level; - - if (rel) { - if ((current = getOssVolumeLevel()) < 0) - return -1; - - new = current + change; - } else { - if (ensure_oss_open() < 0) - return -1; - new = change; - } - - if (new < 0) - new = 0; - else if (new > 100) - new = 100; - - level = (new << 8) + new; - - if (ioctl(volume_ossFd, MIXER_WRITE(volume_ossControl), &level) < 0) { - closeOssMixer(); - return -1; - } - - return 0; -} -#endif - -#ifdef HAVE_ALSA -static void closeAlsaMixer(void) -{ - snd_mixer_close(volume_alsaMixerHandle); - volume_alsaMixerHandle = NULL; -} - -static int prepAlsaMixer(const char *card) -{ - int err; - snd_mixer_elem_t *elem; - const char *controlName = VOLUME_MIXER_ALSA_CONTROL_DEFAULT; - ConfigParam *param; - - err = snd_mixer_open(&volume_alsaMixerHandle, 0); - snd_config_update_free_global(); - if (err < 0) { - g_warning("problems opening alsa mixer: %s", - snd_strerror(err)); - return -1; - } - - if ((err = snd_mixer_attach(volume_alsaMixerHandle, card)) < 0) { - closeAlsaMixer(); - g_warning("problems attaching alsa mixer: %s", - snd_strerror(err)); - return -1; - } - - if ((err = - snd_mixer_selem_register(volume_alsaMixerHandle, NULL, - NULL)) < 0) { - closeAlsaMixer(); - g_warning("problems snd_mixer_selem_register'ing: %s", - snd_strerror(err)); - return -1; - } - - if ((err = snd_mixer_load(volume_alsaMixerHandle)) < 0) { - closeAlsaMixer(); - g_warning("problems snd_mixer_selem_register'ing: %s", - snd_strerror(err)); - return -1; + param = getConfigParam(CONF_MIXER_DEVICE); + if (param) { + g_warning("deprecated option mixer_device found, translating to %s config section\n", driver); + addBlockParam(newparam, "mix_device", param->value, -1); } - - elem = snd_mixer_first_elem(volume_alsaMixerHandle); - param = getConfigParam(CONF_MIXER_CONTROL); - if (param) { - controlName = param->value; + g_warning("deprecated option mixer_control found, translating to %s config section\n", driver); + addBlockParam(newparam, "mix_control", param->value, -1); } - - while (elem) { - if (snd_mixer_elem_get_type(elem) == SND_MIXER_ELEM_SIMPLE) { - if (strcasecmp(controlName, - snd_mixer_selem_get_name(elem)) == 0) { - break; - } + if (newparam->numberOfBlockParams > 0) { + //call configure method of corrensponding mixer + if (!mixer_configure_legacy(driver, newparam)) { + g_error("Using mixer_type '%s' with not enabled %s output", driver, driver); } - elem = snd_mixer_elem_next(elem); - } - - if (elem) { - volume_alsaElem = elem; - snd_mixer_selem_get_playback_volume_range(volume_alsaElem, - &volume_alsaMin, - &volume_alsaMax); - return 0; } - - g_warning("can't find alsa mixer_control \"%s\"", controlName); - - closeAlsaMixer(); - return -1; } -static int prep_alsa_get_level(long *level) -{ - const char *cmd; - int err; - - if (!volume_alsaMixerHandle && prepAlsaMixer(volume_mixerDevice) < 0) - return -1; - - if ((err = snd_mixer_handle_events(volume_alsaMixerHandle)) < 0) { - cmd = "handle_events"; - goto error; - } - if ((err = snd_mixer_selem_get_playback_volume(volume_alsaElem, - SND_MIXER_SCHN_FRONT_LEFT, - level)) < 0) { - cmd = "selem_get_playback_volume"; - goto error; - } - return 0; - -error: - g_warning("problems getting alsa volume: %s (snd_mixer_%s)", - snd_strerror(err), cmd); - closeAlsaMixer(); - return -1; -} - -static int getAlsaVolumeLevel(void) -{ - int ret; - long level; - long max = volume_alsaMax; - long min = volume_alsaMin; - - if (prep_alsa_get_level(&level) < 0) - return -1; - - ret = ((volume_alsaSet / 100.0) * (max - min) + min) + 0.5; - if (volume_alsaSet > 0 && ret == level) { - ret = volume_alsaSet; - } else - ret = (int)(100 * (((float)(level - min)) / (max - min)) + 0.5); - - return ret; -} - -static int changeAlsaVolumeLevel(int change, int rel) -{ - float vol; - long level; - long test; - long max = volume_alsaMax; - long min = volume_alsaMin; - int err; - - if (prep_alsa_get_level(&level) < 0) - return -1; - - if (rel) { - test = ((volume_alsaSet / 100.0) * (max - min) + min) + 0.5; - if (volume_alsaSet >= 0 && level == test) { - vol = volume_alsaSet; - } else - vol = 100.0 * (((float)(level - min)) / (max - min)); - vol += change; - } else - vol = change; - - volume_alsaSet = vol + 0.5; - volume_alsaSet = volume_alsaSet > 100 ? 100 : - (volume_alsaSet < 0 ? 0 : volume_alsaSet); - - level = (long)(((vol / 100.0) * (max - min) + min) + 0.5); - level = level > max ? max : level; - level = level < min ? min : level; - - if ((err = - snd_mixer_selem_set_playback_volume_all(volume_alsaElem, - level)) < 0) { - g_warning("problems setting alsa volume: %s", - snd_strerror(err)); - closeAlsaMixer(); - return -1; - } - - return 0; -} -#endif - -static int prepMixer(const char *device) -{ - switch (volume_mixerType) { -#ifdef HAVE_ALSA - case VOLUME_MIXER_TYPE_ALSA: - return prepAlsaMixer(device); -#endif -#ifdef HAVE_OSS - case VOLUME_MIXER_TYPE_OSS: - return prepOssMixer(device); -#endif - } - - return 0; -} - -void finishVolume(void) -{ - switch (volume_mixerType) { -#ifdef HAVE_ALSA - case VOLUME_MIXER_TYPE_ALSA: - closeAlsaMixer(); - break; -#endif -#ifdef HAVE_OSS - case VOLUME_MIXER_TYPE_OSS: - closeOssMixer(); - break; -#endif - } -} - -void initVolume(void) +void volume_init(void) { ConfigParam *param = getConfigParam(CONF_MIXER_TYPE); - + //hw mixing is by default if (param) { - if (0) ; -#ifdef HAVE_ALSA - else if (strcmp(param->value, VOLUME_MIXER_ALSA) == 0) { - volume_mixerType = VOLUME_MIXER_TYPE_ALSA; - volume_mixerDevice = VOLUME_MIXER_ALSA_DEFAULT; - } -#endif -#ifdef HAVE_OSS - else if (strcmp(param->value, VOLUME_MIXER_OSS) == 0) { - volume_mixerType = VOLUME_MIXER_TYPE_OSS; - volume_mixerDevice = VOLUME_MIXER_OSS_DEFAULT; - } -#endif - else if (strcmp(param->value, VOLUME_MIXER_SOFTWARE) == 0) { - volume_mixerType = VOLUME_MIXER_TYPE_SOFTWARE; - volume_mixerDevice = VOLUME_MIXER_SOFTWARE_DEFAULT; + if (strcmp(param->value, VOLUME_MIXER_SOFTWARE) == 0) { + volume_mixer_type = VOLUME_MIXER_TYPE_SOFTWARE; + } else if (strcmp(param->value, VOLUME_MIXER_HARDWARE) == 0) { + //nothing to do } else { - g_error("unknown mixer type %s at line %i", - param->value, param->line); + //fallback to old config behaviour + if (strcmp(param->value, VOLUME_MIXER_OSS) == 0) { + mixer_reconfigure(param->value); + } else if (strcmp(param->value, VOLUME_MIXER_ALSA) == 0) { + mixer_reconfigure(param->value); + } else { + g_error("unknown mixer type %s at line %i\n", + param->value, param->line); + } } } - - param = getConfigParam(CONF_MIXER_DEVICE); - - if (param) { - volume_mixerDevice = param->value; - } } -void openVolumeDevice(void) +static int hardware_volume_get(void) { - if (prepMixer(volume_mixerDevice) < 0) { - g_message("using software volume"); - volume_mixerType = VOLUME_MIXER_TYPE_SOFTWARE; + int device, count; + int volume, volume_total, volume_ok; + + volume_total = 0; + volume_ok = 0; + + count = audio_output_count(); + for (device=0; device<count ;device++) { + if (mixer_control_getvol(device, &volume)) { + g_debug("device %d: volume: %d\n", device, volume); + volume_total += volume; + volume_ok++; + } + } + if (volume_ok > 0) { + //return average + return volume_total / volume_ok; + } else { + return -1; } } -static int getSoftwareVolume(void) +static int software_volume_get(void) { - return volume_softwareSet; + return volume_software_set; } -int getVolumeLevel(void) +int volume_level_get(void) { - switch (volume_mixerType) { -#ifdef HAVE_ALSA - case VOLUME_MIXER_TYPE_ALSA: - return getAlsaVolumeLevel(); -#endif -#ifdef HAVE_OSS - case VOLUME_MIXER_TYPE_OSS: - return getOssVolumeLevel(); -#endif + switch (volume_mixer_type) { case VOLUME_MIXER_TYPE_SOFTWARE: - return getSoftwareVolume(); + return software_volume_get(); + case VOLUME_MIXER_TYPE_HARDWARE: + return hardware_volume_get(); default: return -1; } + return -1; } -static int changeSoftwareVolume(int change, int rel) +static int software_volume_change(int change, int rel) { int new = change; if (rel) - new += volume_softwareSet; + new += volume_software_set; if (new > 100) new = 100; else if (new < 0) new = 0; - volume_softwareSet = new; + volume_software_set = new; /*new = 100.0*(exp(new/50.0)-1)/(M_E*M_E-1)+0.5; */ if (new >= 100) @@ -499,21 +165,26 @@ static int changeSoftwareVolume(int change, int rel) return 0; } -int changeVolumeLevel(int change, int rel) +static int hardware_volume_change(int change, int rel) +{ + int device, count; + + count = audio_output_count(); + for (device=0; device<count ;device++) { + mixer_control_setvol(device, change, rel); + } + return 0; +} + +int volume_level_change(int change, int rel) { idle_add(IDLE_MIXER); - switch (volume_mixerType) { -#ifdef HAVE_ALSA - case VOLUME_MIXER_TYPE_ALSA: - return changeAlsaVolumeLevel(change, rel); -#endif -#ifdef HAVE_OSS - case VOLUME_MIXER_TYPE_OSS: - return changeOssVolumeLevel(change, rel); -#endif + switch (volume_mixer_type) { + case VOLUME_MIXER_TYPE_HARDWARE: + return hardware_volume_change(change, rel); case VOLUME_MIXER_TYPE_SOFTWARE: - return changeSoftwareVolume(change, rel); + return software_volume_change(change, rel); default: return 0; } @@ -525,7 +196,7 @@ void read_sw_volume_state(FILE *fp) char *end = NULL; long int sv; - if (volume_mixerType != VOLUME_MIXER_TYPE_SOFTWARE) + if (volume_mixer_type != VOLUME_MIXER_TYPE_SOFTWARE) return; while (fgets(buf, sizeof(buf), fp)) { if (!g_str_has_prefix(buf, SW_VOLUME_STATE)) @@ -534,16 +205,15 @@ void read_sw_volume_state(FILE *fp) g_strchomp(buf); sv = strtol(buf + strlen(SW_VOLUME_STATE), &end, 10); if (G_LIKELY(!*end)) - changeSoftwareVolume(sv, 0); + software_volume_change(sv, 0); else - g_warning("Can't parse software volume: %s", buf); + g_warning("Can't parse software volume: %s\n", buf); return; } } void save_sw_volume_state(FILE *fp) { - if (volume_mixerType == VOLUME_MIXER_TYPE_SOFTWARE) - fprintf(fp, SW_VOLUME_STATE "%d\n", volume_softwareSet); + if (volume_mixer_type == VOLUME_MIXER_TYPE_SOFTWARE) + fprintf(fp, SW_VOLUME_STATE "%d\n", volume_software_set); } - |