1 /* the Music Player Daemon (MPD)
2 * Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com)
3 * This project's homepage is: http://www.musicpd.org
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 #include "state_file.h"
32 #include <sys/ioctl.h>
36 #include <sys/soundcard.h>
39 #include <alsa/asoundlib.h>
42 #define VOLUME_MIXER_TYPE_SOFTWARE 0
43 #define VOLUME_MIXER_TYPE_OSS 1
44 #define VOLUME_MIXER_TYPE_ALSA 2
46 #define VOLUME_MIXER_SOFTWARE_DEFAULT ""
47 #define VOLUME_MIXER_OSS_DEFAULT "/dev/mixer"
48 #define VOLUME_MIXER_ALSA_DEFAULT "default"
49 #define VOLUME_MIXER_ALSA_CONTROL_DEFAULT "PCM"
50 #define SW_VOLUME_STATE "sw_volume: "
53 #define VOLUME_MIXER_TYPE_DEFAULT VOLUME_MIXER_TYPE_OSS
54 #define VOLUME_MIXER_DEVICE_DEFAULT VOLUME_MIXER_OSS_DEFAULT
57 #define VOLUME_MIXER_TYPE_DEFAULT VOLUME_MIXER_TYPE_ALSA
58 #define VOLUME_MIXER_DEVICE_DEFAULT VOLUME_MIXER_ALSA_DEFAULT
60 #define VOLUME_MIXER_TYPE_DEFAULT VOLUME_MIXER_TYPE_SOFTWARE
61 #define VOLUME_MIXER_DEVICE_DEFAULT VOLUME_MIXER_SOFTWARE_DEFAULT
65 static int volume_mixerType = VOLUME_MIXER_TYPE_DEFAULT;
66 static char *volume_mixerDevice = VOLUME_MIXER_DEVICE_DEFAULT;
68 static int volume_softwareSet = 100;
71 static int volume_ossFd = -1;
72 static int volume_ossControl = SOUND_MIXER_PCM;
76 static snd_mixer_t *volume_alsaMixerHandle;
77 static snd_mixer_elem_t *volume_alsaElem;
78 static long volume_alsaMin;
79 static long volume_alsaMax;
80 static int volume_alsaSet = -1;
85 static void closeOssMixer(void)
87 while (close(volume_ossFd) && errno == EINTR) ;
91 static int prepOssMixer(char *device)
95 if ((volume_ossFd = open(device, O_RDONLY)) < 0) {
96 WARNING("unable to open oss mixer \"%s\"\n", device);
100 param = getConfigParam(CONF_MIXER_CONTROL);
103 char *labels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS;
108 if (ioctl(volume_ossFd, SOUND_MIXER_READ_DEVMASK, &devmask) < 0) {
109 WARNING("errors getting read_devmask for oss mixer\n");
114 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
115 dup = xstrdup(labels[i]);
116 /* eliminate spaces at the end */
118 while (j >= 0 && dup[j] == ' ')
120 if (strcasecmp(dup, param->value) == 0) {
127 if (i >= SOUND_MIXER_NRDEVICES) {
128 WARNING("mixer control \"%s\" not found at line %i\n",
129 param->value, param->line);
132 } else if (!((1 << i) & devmask)) {
133 WARNING("mixer control \"%s\" not usable at line %i\n",
134 param->value, param->line);
139 volume_ossControl = i;
145 static int ensure_oss_open(void)
147 if ((volume_ossFd < 0 && prepOssMixer(volume_mixerDevice) < 0))
152 static int getOssVolumeLevel(void)
154 int left, right, level;
156 if (ensure_oss_open() < 0)
159 if (ioctl(volume_ossFd, MIXER_READ(volume_ossControl), &level) < 0) {
161 WARNING("unable to read volume\n");
166 right = (level & 0xff00) >> 8;
169 WARNING("volume for left and right is not the same, \"%i\" and "
170 "\"%i\"\n", left, right);
176 static int changeOssVolumeLevel(int fd, int change, int rel)
183 if ((current = getOssVolumeLevel()) < 0) {
184 commandError(fd, ACK_ERROR_SYSTEM,
185 "problem getting current volume");
189 new = current + change;
191 if (ensure_oss_open() < 0)
201 level = (new << 8) + new;
203 if (ioctl(volume_ossFd, MIXER_WRITE(volume_ossControl), &level) < 0) {
205 commandError(fd, ACK_ERROR_SYSTEM, "problems setting volume");
214 static void closeAlsaMixer(void)
216 snd_mixer_close(volume_alsaMixerHandle);
217 volume_alsaMixerHandle = NULL;
220 static int prepAlsaMixer(char *card)
223 snd_mixer_elem_t *elem;
224 char *controlName = VOLUME_MIXER_ALSA_CONTROL_DEFAULT;
227 err = snd_mixer_open(&volume_alsaMixerHandle, 0);
228 snd_config_update_free_global();
230 WARNING("problems opening alsa mixer: %s\n", snd_strerror(err));
234 if ((err = snd_mixer_attach(volume_alsaMixerHandle, card)) < 0) {
236 WARNING("problems attaching alsa mixer: %s\n",
242 snd_mixer_selem_register(volume_alsaMixerHandle, NULL,
245 WARNING("problems snd_mixer_selem_register'ing: %s\n",
250 if ((err = snd_mixer_load(volume_alsaMixerHandle)) < 0) {
252 WARNING("problems snd_mixer_selem_register'ing: %s\n",
257 elem = snd_mixer_first_elem(volume_alsaMixerHandle);
259 param = getConfigParam(CONF_MIXER_CONTROL);
262 controlName = param->value;
266 if (snd_mixer_elem_get_type(elem) == SND_MIXER_ELEM_SIMPLE) {
267 if (strcasecmp(controlName,
268 snd_mixer_selem_get_name(elem)) == 0) {
272 elem = snd_mixer_elem_next(elem);
276 volume_alsaElem = elem;
277 snd_mixer_selem_get_playback_volume_range(volume_alsaElem,
283 WARNING("can't find alsa mixer_control \"%s\"\n", controlName);
289 static int prep_alsa_get_level(long *level)
294 if (!volume_alsaMixerHandle && prepAlsaMixer(volume_mixerDevice) < 0)
297 if ((err = snd_mixer_handle_events(volume_alsaMixerHandle)) < 0) {
298 cmd = "handle_events";
301 if ((err = snd_mixer_selem_get_playback_volume(volume_alsaElem,
302 SND_MIXER_SCHN_FRONT_LEFT,
304 cmd = "selem_get_playback_volume";
310 WARNING("problems getting alsa volume: %s (snd_mixer_%s)\n",
311 snd_strerror(err), cmd);
316 static int getAlsaVolumeLevel(void)
320 long max = volume_alsaMax;
321 long min = volume_alsaMin;
323 if (prep_alsa_get_level(&level) < 0)
326 ret = ((volume_alsaSet / 100.0) * (max - min) + min) + 0.5;
327 if (volume_alsaSet > 0 && ret == level) {
328 ret = volume_alsaSet;
330 ret = (int)(100 * (((float)(level - min)) / (max - min)) + 0.5);
335 static int changeAlsaVolumeLevel(int fd, int change, int rel)
340 long max = volume_alsaMax;
341 long min = volume_alsaMin;
344 if (prep_alsa_get_level(&level) < 0)
348 test = ((volume_alsaSet / 100.0) * (max - min) + min) + 0.5;
349 if (volume_alsaSet >= 0 && level == test) {
350 vol = volume_alsaSet;
352 vol = 100.0 * (((float)(level - min)) / (max - min));
357 volume_alsaSet = vol + 0.5;
358 volume_alsaSet = volume_alsaSet > 100 ? 100 :
359 (volume_alsaSet < 0 ? 0 : volume_alsaSet);
361 level = (long)(((vol / 100.0) * (max - min) + min) + 0.5);
362 level = level > max ? max : level;
363 level = level < min ? min : level;
366 snd_mixer_selem_set_playback_volume_all(volume_alsaElem,
368 commandError(fd, ACK_ERROR_SYSTEM, "problems setting volume");
369 WARNING("problems setting alsa volume: %s\n",
379 static int prepMixer(char *device)
381 switch (volume_mixerType) {
383 case VOLUME_MIXER_TYPE_ALSA:
384 return prepAlsaMixer(device);
387 case VOLUME_MIXER_TYPE_OSS:
388 return prepOssMixer(device);
395 void finishVolume(void)
397 switch (volume_mixerType) {
399 case VOLUME_MIXER_TYPE_ALSA:
404 case VOLUME_MIXER_TYPE_OSS:
411 void initVolume(void)
413 ConfigParam *param = getConfigParam(CONF_MIXER_TYPE);
418 else if (strcmp(param->value, VOLUME_MIXER_ALSA) == 0) {
419 volume_mixerType = VOLUME_MIXER_TYPE_ALSA;
420 volume_mixerDevice = VOLUME_MIXER_ALSA_DEFAULT;
424 else if (strcmp(param->value, VOLUME_MIXER_OSS) == 0) {
425 volume_mixerType = VOLUME_MIXER_TYPE_OSS;
426 volume_mixerDevice = VOLUME_MIXER_OSS_DEFAULT;
429 else if (strcmp(param->value, VOLUME_MIXER_SOFTWARE) == 0) {
430 volume_mixerType = VOLUME_MIXER_TYPE_SOFTWARE;
431 volume_mixerDevice = VOLUME_MIXER_SOFTWARE_DEFAULT;
433 FATAL("unknown mixer type %s at line %i\n",
434 param->value, param->line);
438 param = getConfigParam(CONF_MIXER_DEVICE);
441 volume_mixerDevice = param->value;
445 void openVolumeDevice(void)
447 if (prepMixer(volume_mixerDevice) < 0) {
448 WARNING("using software volume\n");
449 volume_mixerType = VOLUME_MIXER_TYPE_SOFTWARE;
453 static int getSoftwareVolume(void)
455 return volume_softwareSet;
458 int getVolumeLevel(void)
460 switch (volume_mixerType) {
462 case VOLUME_MIXER_TYPE_ALSA:
463 return getAlsaVolumeLevel();
466 case VOLUME_MIXER_TYPE_OSS:
467 return getOssVolumeLevel();
469 case VOLUME_MIXER_TYPE_SOFTWARE:
470 return getSoftwareVolume();
476 static int changeSoftwareVolume(int fd, int change, int rel)
481 new += volume_softwareSet;
488 volume_softwareSet = new;
490 /*new = 100.0*(exp(new/50.0)-1)/(M_E*M_E-1)+0.5; */
497 1000.0 * (exp(new / 25.0) - 1) / (54.5981500331F - 1) + 0.5;
499 setPlayerSoftwareVolume(new);
504 int changeVolumeLevel(int fd, int change, int rel)
506 switch (volume_mixerType) {
508 case VOLUME_MIXER_TYPE_ALSA:
509 return changeAlsaVolumeLevel(fd, change, rel);
512 case VOLUME_MIXER_TYPE_OSS:
513 return changeOssVolumeLevel(fd, change, rel);
515 case VOLUME_MIXER_TYPE_SOFTWARE:
516 return changeSoftwareVolume(fd, change, rel);
523 void read_sw_volume_state(FILE *fp)
525 /* strlen(SW_VOLUME_STATE) + strlen('100') + '\0' */
528 const size_t len = strlen(SW_VOLUME_STATE);
532 if (volume_mixerType != VOLUME_MIXER_TYPE_SOFTWARE)
534 while (myFgets(buf, bufsize, fp)) {
535 if (strncmp(buf, SW_VOLUME_STATE, len))
537 sv = strtol(buf + len, &end, 10);
538 if (mpd_likely(!*end))
539 changeSoftwareVolume(STDERR_FILENO, sv, 0);
541 ERROR("Can't parse software volume: %s\n", buf);
547 void save_sw_volume_state(FILE *fp)
549 if (volume_mixerType == VOLUME_MIXER_TYPE_SOFTWARE)
550 fprintf(fp, SW_VOLUME_STATE "%d\n", volume_softwareSet);