Initial revision 6759
[qball-mpd.git] / src / .svn / text-base / volume.c.svn-base
blob59e8b550c650db1b9536bb0f86b44b274a1e19d7
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
4  *
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.
9  *
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
17  */
18 #include "volume.h"
20 #include "command.h"
21 #include "conf.h"
22 #include "log.h"
23 #include "player.h"
24 #include "state_file.h"
25 #include "gcc.h"
26 #include "utils.h"
28 #include <math.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <sys/ioctl.h>
33 #include <fcntl.h>
34 #include <errno.h>
35 #ifdef HAVE_OSS
36 #include <sys/soundcard.h>
37 #endif
38 #ifdef HAVE_ALSA
39 #include <alsa/asoundlib.h>
40 #endif
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: "
52 #ifdef HAVE_OSS
53 #define VOLUME_MIXER_TYPE_DEFAULT               VOLUME_MIXER_TYPE_OSS
54 #define VOLUME_MIXER_DEVICE_DEFAULT             VOLUME_MIXER_OSS_DEFAULT
55 #else
56 #ifdef HAVE_ALSA
57 #define VOLUME_MIXER_TYPE_DEFAULT               VOLUME_MIXER_TYPE_ALSA
58 #define VOLUME_MIXER_DEVICE_DEFAULT             VOLUME_MIXER_ALSA_DEFAULT
59 #else
60 #define VOLUME_MIXER_TYPE_DEFAULT               VOLUME_MIXER_TYPE_SOFTWARE
61 #define VOLUME_MIXER_DEVICE_DEFAULT             VOLUME_MIXER_SOFTWARE_DEFAULT
62 #endif
63 #endif
65 static int volume_mixerType = VOLUME_MIXER_TYPE_DEFAULT;
66 static char *volume_mixerDevice = VOLUME_MIXER_DEVICE_DEFAULT;
68 static int volume_softwareSet = 100;
70 #ifdef HAVE_OSS
71 static int volume_ossFd = -1;
72 static int volume_ossControl = SOUND_MIXER_PCM;
73 #endif
75 #ifdef HAVE_ALSA
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;
81 #endif
83 #ifdef HAVE_OSS
85 static void closeOssMixer(void)
87         while (close(volume_ossFd) && errno == EINTR) ;
88         volume_ossFd = -1;
91 static int prepOssMixer(char *device)
93         ConfigParam *param;
95         if ((volume_ossFd = open(device, O_RDONLY)) < 0) {
96                 WARNING("unable to open oss mixer \"%s\"\n", device);
97                 return -1;
98         }
100         param = getConfigParam(CONF_MIXER_CONTROL);
102         if (param) {
103                 char *labels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS;
104                 char *dup;
105                 int i, j;
106                 int devmask = 0;
108                 if (ioctl(volume_ossFd, SOUND_MIXER_READ_DEVMASK, &devmask) < 0) {
109                         WARNING("errors getting read_devmask for oss mixer\n");
110                         closeOssMixer();
111                         return -1;
112                 }
114                 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
115                         dup = xstrdup(labels[i]);
116                         /* eliminate spaces at the end */
117                         j = strlen(dup) - 1;
118                         while (j >= 0 && dup[j] == ' ')
119                                 dup[j--] = '\0';
120                         if (strcasecmp(dup, param->value) == 0) {
121                                 free(dup);
122                                 break;
123                         }
124                         free(dup);
125                 }
127                 if (i >= SOUND_MIXER_NRDEVICES) {
128                         WARNING("mixer control \"%s\" not found at line %i\n",
129                                 param->value, param->line);
130                         closeOssMixer();
131                         return -1;
132                 } else if (!((1 << i) & devmask)) {
133                         WARNING("mixer control \"%s\" not usable at line %i\n",
134                                 param->value, param->line);
135                         closeOssMixer();
136                         return -1;
137                 }
139                 volume_ossControl = i;
140         }
142         return 0;
145 static int ensure_oss_open(void)
147         if ((volume_ossFd < 0 && prepOssMixer(volume_mixerDevice) < 0))
148                 return -1;
149         return 0;
152 static int getOssVolumeLevel(void)
154         int left, right, level;
156         if (ensure_oss_open() < 0)
157                 return -1;
159         if (ioctl(volume_ossFd, MIXER_READ(volume_ossControl), &level) < 0) {
160                 closeOssMixer();
161                 WARNING("unable to read volume\n");
162                 return -1;
163         }
165         left = level & 0xff;
166         right = (level & 0xff00) >> 8;
168         if (left != right) {
169                 WARNING("volume for left and right is not the same, \"%i\" and "
170                         "\"%i\"\n", left, right);
171         }
173         return left;
176 static int changeOssVolumeLevel(int fd, int change, int rel)
178         int current;
179         int new;
180         int level;
182         if (rel) {
183                 if ((current = getOssVolumeLevel()) < 0) {
184                         commandError(fd, ACK_ERROR_SYSTEM,
185                                      "problem getting current volume");
186                         return -1;
187                 }
189                 new = current + change;
190         } else {
191                 if (ensure_oss_open() < 0)
192                         return -1;
193                 new = change;
194         }
196         if (new < 0)
197                 new = 0;
198         else if (new > 100)
199                 new = 100;
201         level = (new << 8) + new;
203         if (ioctl(volume_ossFd, MIXER_WRITE(volume_ossControl), &level) < 0) {
204                 closeOssMixer();
205                 commandError(fd, ACK_ERROR_SYSTEM, "problems setting volume");
206                 return -1;
207         }
209         return 0;
211 #endif
213 #ifdef HAVE_ALSA
214 static void closeAlsaMixer(void)
216         snd_mixer_close(volume_alsaMixerHandle);
217         volume_alsaMixerHandle = NULL;
220 static int prepAlsaMixer(char *card)
222         int err;
223         snd_mixer_elem_t *elem;
224         char *controlName = VOLUME_MIXER_ALSA_CONTROL_DEFAULT;
225         ConfigParam *param;
227         err = snd_mixer_open(&volume_alsaMixerHandle, 0);
228         snd_config_update_free_global();
229         if (err < 0) {
230                 WARNING("problems opening alsa mixer: %s\n", snd_strerror(err));
231                 return -1;
232         }
234         if ((err = snd_mixer_attach(volume_alsaMixerHandle, card)) < 0) {
235                 closeAlsaMixer();
236                 WARNING("problems attaching alsa mixer: %s\n",
237                         snd_strerror(err));
238                 return -1;
239         }
241         if ((err =
242              snd_mixer_selem_register(volume_alsaMixerHandle, NULL,
243                                       NULL)) < 0) {
244                 closeAlsaMixer();
245                 WARNING("problems snd_mixer_selem_register'ing: %s\n",
246                         snd_strerror(err));
247                 return -1;
248         }
250         if ((err = snd_mixer_load(volume_alsaMixerHandle)) < 0) {
251                 closeAlsaMixer();
252                 WARNING("problems snd_mixer_selem_register'ing: %s\n",
253                         snd_strerror(err));
254                 return -1;
255         }
257         elem = snd_mixer_first_elem(volume_alsaMixerHandle);
259         param = getConfigParam(CONF_MIXER_CONTROL);
261         if (param) {
262                 controlName = param->value;
263         }
265         while (elem) {
266                 if (snd_mixer_elem_get_type(elem) == SND_MIXER_ELEM_SIMPLE) {
267                         if (strcasecmp(controlName,
268                                        snd_mixer_selem_get_name(elem)) == 0) {
269                                 break;
270                         }
271                 }
272                 elem = snd_mixer_elem_next(elem);
273         }
275         if (elem) {
276                 volume_alsaElem = elem;
277                 snd_mixer_selem_get_playback_volume_range(volume_alsaElem,
278                                                           &volume_alsaMin,
279                                                           &volume_alsaMax);
280                 return 0;
281         }
283         WARNING("can't find alsa mixer_control \"%s\"\n", controlName);
285         closeAlsaMixer();
286         return -1;
289 static int prep_alsa_get_level(long *level)
291         const char *cmd;
292         int err;
294         if (!volume_alsaMixerHandle && prepAlsaMixer(volume_mixerDevice) < 0)
295                 return -1;
297         if ((err = snd_mixer_handle_events(volume_alsaMixerHandle)) < 0) {
298                 cmd = "handle_events";
299                 goto error;
300         }
301         if ((err = snd_mixer_selem_get_playback_volume(volume_alsaElem,
302                                                        SND_MIXER_SCHN_FRONT_LEFT,
303                                                        level)) < 0) {
304                 cmd = "selem_get_playback_volume";
305                 goto error;
306         }
307         return 0;
309 error:
310         WARNING("problems getting alsa volume: %s (snd_mixer_%s)\n",
311                 snd_strerror(err), cmd);
312         closeAlsaMixer();
313         return -1;
316 static int getAlsaVolumeLevel(void)
318         int ret;
319         long level;
320         long max = volume_alsaMax;
321         long min = volume_alsaMin;
323         if (prep_alsa_get_level(&level) < 0)
324                 return -1;
326         ret = ((volume_alsaSet / 100.0) * (max - min) + min) + 0.5;
327         if (volume_alsaSet > 0 && ret == level) {
328                 ret = volume_alsaSet;
329         } else
330                 ret = (int)(100 * (((float)(level - min)) / (max - min)) + 0.5);
332         return ret;
335 static int changeAlsaVolumeLevel(int fd, int change, int rel)
337         float vol;
338         long level;
339         long test;
340         long max = volume_alsaMax;
341         long min = volume_alsaMin;
342         int err;
344         if (prep_alsa_get_level(&level) < 0)
345                 return -1;
347         if (rel) {
348                 test = ((volume_alsaSet / 100.0) * (max - min) + min) + 0.5;
349                 if (volume_alsaSet >= 0 && level == test) {
350                         vol = volume_alsaSet;
351                 } else
352                         vol = 100.0 * (((float)(level - min)) / (max - min));
353                 vol += change;
354         } else
355                 vol = change;
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;
365         if ((err =
366              snd_mixer_selem_set_playback_volume_all(volume_alsaElem,
367                                                      level)) < 0) {
368                 commandError(fd, ACK_ERROR_SYSTEM, "problems setting volume");
369                 WARNING("problems setting alsa volume: %s\n",
370                         snd_strerror(err));
371                 closeAlsaMixer();
372                 return -1;
373         }
375         return 0;
377 #endif
379 static int prepMixer(char *device)
381         switch (volume_mixerType) {
382 #ifdef HAVE_ALSA
383         case VOLUME_MIXER_TYPE_ALSA:
384                 return prepAlsaMixer(device);
385 #endif
386 #ifdef HAVE_OSS
387         case VOLUME_MIXER_TYPE_OSS:
388                 return prepOssMixer(device);
389 #endif
390         }
392         return 0;
395 void finishVolume(void)
397         switch (volume_mixerType) {
398 #ifdef HAVE_ALSA
399         case VOLUME_MIXER_TYPE_ALSA:
400                 closeAlsaMixer();
401                 break;
402 #endif
403 #ifdef HAVE_OSS
404         case VOLUME_MIXER_TYPE_OSS:
405                 closeOssMixer();
406                 break;
407 #endif
408         }
411 void initVolume(void)
413         ConfigParam *param = getConfigParam(CONF_MIXER_TYPE);
415         if (param) {
416                 if (0) ;
417 #ifdef HAVE_ALSA
418                 else if (strcmp(param->value, VOLUME_MIXER_ALSA) == 0) {
419                         volume_mixerType = VOLUME_MIXER_TYPE_ALSA;
420                         volume_mixerDevice = VOLUME_MIXER_ALSA_DEFAULT;
421                 }
422 #endif
423 #ifdef HAVE_OSS
424                 else if (strcmp(param->value, VOLUME_MIXER_OSS) == 0) {
425                         volume_mixerType = VOLUME_MIXER_TYPE_OSS;
426                         volume_mixerDevice = VOLUME_MIXER_OSS_DEFAULT;
427                 }
428 #endif
429                 else if (strcmp(param->value, VOLUME_MIXER_SOFTWARE) == 0) {
430                         volume_mixerType = VOLUME_MIXER_TYPE_SOFTWARE;
431                         volume_mixerDevice = VOLUME_MIXER_SOFTWARE_DEFAULT;
432                 } else {
433                         FATAL("unknown mixer type %s at line %i\n",
434                               param->value, param->line);
435                 }
436         }
438         param = getConfigParam(CONF_MIXER_DEVICE);
440         if (param) {
441                 volume_mixerDevice = param->value;
442         }
445 void openVolumeDevice(void)
447         if (prepMixer(volume_mixerDevice) < 0) {
448                 WARNING("using software volume\n");
449                 volume_mixerType = VOLUME_MIXER_TYPE_SOFTWARE;
450         }
453 static int getSoftwareVolume(void)
455         return volume_softwareSet;
458 int getVolumeLevel(void)
460         switch (volume_mixerType) {
461 #ifdef HAVE_ALSA
462         case VOLUME_MIXER_TYPE_ALSA:
463                 return getAlsaVolumeLevel();
464 #endif
465 #ifdef HAVE_OSS
466         case VOLUME_MIXER_TYPE_OSS:
467                 return getOssVolumeLevel();
468 #endif
469         case VOLUME_MIXER_TYPE_SOFTWARE:
470                 return getSoftwareVolume();
471         default:
472                 return -1;
473         }
476 static int changeSoftwareVolume(int fd, int change, int rel)
478         int new = change;
480         if (rel)
481                 new += volume_softwareSet;
483         if (new > 100)
484                 new = 100;
485         else if (new < 0)
486                 new = 0;
488         volume_softwareSet = new;
490         /*new = 100.0*(exp(new/50.0)-1)/(M_E*M_E-1)+0.5; */
491         if (new >= 100)
492                 new = 1000;
493         else if (new <= 0)
494                 new = 0;
495         else
496                 new =
497                     1000.0 * (exp(new / 25.0) - 1) / (54.5981500331F - 1) + 0.5;
499         setPlayerSoftwareVolume(new);
501         return 0;
504 int changeVolumeLevel(int fd, int change, int rel)
506         switch (volume_mixerType) {
507 #ifdef HAVE_ALSA
508         case VOLUME_MIXER_TYPE_ALSA:
509                 return changeAlsaVolumeLevel(fd, change, rel);
510 #endif
511 #ifdef HAVE_OSS
512         case VOLUME_MIXER_TYPE_OSS:
513                 return changeOssVolumeLevel(fd, change, rel);
514 #endif
515         case VOLUME_MIXER_TYPE_SOFTWARE:
516                 return changeSoftwareVolume(fd, change, rel);
517         default:
518                 return 0;
519                 break;
520         }
523 void read_sw_volume_state(FILE *fp)
525         /* strlen(SW_VOLUME_STATE) + strlen('100') + '\0' */
526         #define bufsize 16
527         char buf[bufsize];
528         const size_t len = strlen(SW_VOLUME_STATE);
529         char *end = NULL;
530         long int sv;
532         if (volume_mixerType != VOLUME_MIXER_TYPE_SOFTWARE)
533                 return;
534         while (myFgets(buf, bufsize, fp)) {
535                 if (strncmp(buf, SW_VOLUME_STATE, len))
536                         continue;
537                 sv = strtol(buf + len, &end, 10);
538                 if (mpd_likely(!*end))
539                         changeSoftwareVolume(STDERR_FILENO, sv, 0);
540                 else
541                         ERROR("Can't parse software volume: %s\n", buf);
542                 return;
543         }
544         #undef bufsize
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);