aboutsummaryrefslogtreecommitdiffstats
path: root/src/volume.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/volume.c')
-rw-r--r--src/volume.c508
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);
}
-