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
);