549344d38a
git-svn-id: https://svn.musicpd.org/mpd/trunk@750 09075e82-0dd4-0310-85a5-a0d7c8717e4f
434 lines
9.9 KiB
C
434 lines
9.9 KiB
C
/* the Music Player Daemon (MPD)
|
|
* (c)2003-2004 by Warren Dukes (shank@mercury.chem.pitt.edu)
|
|
* This project's homepage is: http://www.musicpd.org
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
* You should have received a copy of the GNU General Public License
|
|
* 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 "command.h"
|
|
#include "conf.h"
|
|
#include "log.h"
|
|
#include "player.h"
|
|
|
|
#include <math.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <sys/ioctl.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
#ifndef NO_OSS_MIXER
|
|
#include <sys/soundcard.h>
|
|
#endif
|
|
#ifdef HAVE_ALSA
|
|
#include <alsa/asoundlib.h>
|
|
#endif
|
|
|
|
#define VOLUME_MIXER_TYPE_SOFTWARE 0
|
|
#define VOLUME_MIXER_TYPE_OSS 1
|
|
#define VOLUME_MIXER_TYPE_ALSA 2
|
|
|
|
#define VOLUME_MIXER_SOFTWARE_DEFAULT ""
|
|
#define VOLUME_MIXER_OSS_DEFAULT "/dev/mixer"
|
|
#define VOLUME_MIXER_ALSA_DEFAULT "default"
|
|
#define VOLUME_MIXER_ALSA_CONTROL_DEFAULT "Master"
|
|
|
|
int volume_mixerType = VOLUME_MIXER_TYPE_SOFTWARE;
|
|
char * volume_mixerDevice;
|
|
|
|
int volume_softwareSet = 100;
|
|
|
|
#ifndef NO_OSS_MIXER
|
|
int volume_ossFd;
|
|
int volume_ossControl = SOUND_MIXER_VOLUME;
|
|
#endif
|
|
|
|
#ifdef HAVE_ALSA
|
|
snd_mixer_t * volume_alsaMixerHandle = NULL;
|
|
snd_mixer_elem_t * volume_alsaElem;
|
|
long volume_alsaMin;
|
|
long volume_alsaMax;
|
|
int volume_alsaSet = -1;
|
|
#endif
|
|
|
|
#ifndef NO_OSS_MIXER
|
|
int prepOssMixer(char * device) {
|
|
int devmask = 0;
|
|
|
|
if((volume_ossFd = open(device,O_RDONLY))<0) {
|
|
ERROR("unable to open oss mixer \"%s\"\n",device);
|
|
return -1;
|
|
}
|
|
|
|
if(getConf()[CONF_MIXER_CONTROL]) {
|
|
char * labels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS;
|
|
char * dup;
|
|
int i,j;
|
|
|
|
if(ioctl(volume_ossFd,SOUND_MIXER_READ_DEVMASK,&devmask)<0) {
|
|
ERROR("errors getting read_devmask for oss mixer\n");
|
|
close(volume_ossFd);
|
|
return -1;
|
|
}
|
|
|
|
for(i=0;i<SOUND_MIXER_NRDEVICES;i++) {
|
|
dup = strdup(labels[i]);
|
|
/* eliminate spaces at the end */
|
|
j = strlen(dup)-1;
|
|
while(j>=0 && dup[j]==' ') dup[j--] = '\0';
|
|
if(strcasecmp(dup,getConf()[CONF_MIXER_CONTROL])==0) {
|
|
free(dup);
|
|
break;
|
|
}
|
|
free(dup);
|
|
}
|
|
|
|
if(i>=SOUND_MIXER_NRDEVICES) {
|
|
ERROR("mixer control \"%s\" not found\n",
|
|
getConf()[CONF_MIXER_CONTROL]);
|
|
close(volume_ossFd);
|
|
return -1;
|
|
}
|
|
else if(!( ( 1 << i ) & devmask )) {
|
|
ERROR("mixer control \"%s\" not usable\n",
|
|
getConf()[CONF_MIXER_CONTROL]);
|
|
close(volume_ossFd);
|
|
return -1;
|
|
}
|
|
|
|
volume_ossControl = i;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void closeOssMixer() {
|
|
close(volume_ossFd);
|
|
}
|
|
|
|
int getOssVolumeLevel() {
|
|
int left, right, level;
|
|
|
|
if(ioctl(volume_ossFd,MIXER_READ(volume_ossControl),&level) < 0) {
|
|
ERROR("unable to read volume\n");
|
|
return -1;
|
|
}
|
|
|
|
left = level & 0xff;
|
|
right = (level & 0xff00) >> 8;
|
|
|
|
if(left!=right) {
|
|
ERROR("volume for left and right is not the same, \"%i\" and "
|
|
"\"%i\"\n",left,right);
|
|
}
|
|
|
|
return left;
|
|
}
|
|
|
|
int changeOssVolumeLevel(FILE * fp, int change, int rel) {
|
|
int current;
|
|
int new;
|
|
int level;
|
|
|
|
if (rel) {
|
|
if((current = getOssVolumeLevel()) < 0) {
|
|
myfprintf(fp,"%s problem getting current volume\n",
|
|
COMMAND_RESPOND_ERROR);
|
|
return -1;
|
|
}
|
|
|
|
new = current+change;
|
|
}
|
|
else 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) {
|
|
myfprintf(fp,"%s problems setting volume\n",COMMAND_RESPOND_ERROR);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
#ifdef HAVE_ALSA
|
|
int prepAlsaMixer(char * card) {
|
|
int err;
|
|
snd_mixer_elem_t * elem;
|
|
char * controlName = VOLUME_MIXER_ALSA_CONTROL_DEFAULT;
|
|
|
|
if((err = snd_mixer_open(&volume_alsaMixerHandle,0))<0) {
|
|
ERROR("problems opening alsa mixer: %s\n",snd_strerror(err));
|
|
return -1;
|
|
}
|
|
|
|
if((err = snd_mixer_attach(volume_alsaMixerHandle,card))<0) {
|
|
snd_mixer_close(volume_alsaMixerHandle);
|
|
ERROR("problems problems attaching alsa mixer: %s\n",
|
|
snd_strerror(err));
|
|
return -1;
|
|
}
|
|
|
|
if((err = snd_mixer_selem_register(volume_alsaMixerHandle,NULL,NULL))<0) {
|
|
snd_mixer_close(volume_alsaMixerHandle);
|
|
ERROR("problems snd_mixer_selem_register'ing: %s\n",
|
|
snd_strerror(err));
|
|
return -1;
|
|
}
|
|
|
|
if((err = snd_mixer_load(volume_alsaMixerHandle))<0) {
|
|
snd_mixer_close(volume_alsaMixerHandle);
|
|
ERROR("problems snd_mixer_selem_register'ing: %s\n",
|
|
snd_strerror(err));
|
|
return -1;
|
|
}
|
|
|
|
elem = snd_mixer_first_elem(volume_alsaMixerHandle);
|
|
if(getConf()[CONF_MIXER_CONTROL]) {
|
|
controlName = getConf()[CONF_MIXER_CONTROL];
|
|
}
|
|
|
|
while(elem) {
|
|
if(snd_mixer_elem_get_type(elem)==SND_MIXER_ELEM_SIMPLE) {
|
|
if(strcasecmp(controlName,
|
|
snd_mixer_selem_get_name(elem))==0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
|
|
ERROR("can't find alsa mixer_control \"%s\"\n",controlName);
|
|
|
|
snd_mixer_close(volume_alsaMixerHandle);
|
|
return -1;
|
|
}
|
|
|
|
void closeAlsaMixer() {
|
|
snd_mixer_close(volume_alsaMixerHandle);
|
|
}
|
|
|
|
int getAlsaVolumeLevel() {
|
|
int ret;
|
|
long level;
|
|
long max = volume_alsaMax;
|
|
long min = volume_alsaMin;
|
|
int err;
|
|
|
|
snd_mixer_handle_events(volume_alsaMixerHandle);
|
|
|
|
if((err = snd_mixer_selem_get_playback_volume(volume_alsaElem,
|
|
SND_MIXER_SCHN_FRONT_LEFT,&level))<0) {
|
|
ERROR("problems getting alsa volume: %s\n",snd_strerror(err));
|
|
return -1;
|
|
}
|
|
|
|
snd_mixer_selem_get_playback_volume(volume_alsaElem,
|
|
SND_MIXER_SCHN_FRONT_LEFT,&level);
|
|
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;
|
|
}
|
|
|
|
int changeAlsaVolumeLevel(FILE * fp, int change, int rel) {
|
|
float vol;
|
|
long level;
|
|
long test;
|
|
long max = volume_alsaMax;
|
|
long min = volume_alsaMin;
|
|
int err;
|
|
|
|
snd_mixer_handle_events(volume_alsaMixerHandle);
|
|
|
|
if((err = snd_mixer_selem_get_playback_volume(volume_alsaElem,
|
|
SND_MIXER_SCHN_FRONT_LEFT,&level))<0) {
|
|
myfprintf(fp,"%s problems getting volume\n",
|
|
COMMAND_RESPOND_ERROR);
|
|
ERROR("problems getting alsa volume: %s\n",snd_strerror(err));
|
|
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) {
|
|
myfprintf(fp,"%s problems setting volume\n",
|
|
COMMAND_RESPOND_ERROR);
|
|
ERROR("problems setting alsa volume: %s\n",snd_strerror(err));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
int prepMixer(char * device) {
|
|
switch(volume_mixerType) {
|
|
#ifdef HAVE_ALSA
|
|
case VOLUME_MIXER_TYPE_ALSA:
|
|
return prepAlsaMixer(device);
|
|
#endif
|
|
#ifndef NO_OSS_MIXER
|
|
case VOLUME_MIXER_TYPE_OSS:
|
|
return prepOssMixer(device);
|
|
#endif
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void finishVolume() {
|
|
switch(volume_mixerType) {
|
|
#ifdef HAVE_ALSA
|
|
case VOLUME_MIXER_TYPE_ALSA:
|
|
closeAlsaMixer();
|
|
break;
|
|
#endif
|
|
#ifndef NO_OSS_MIXER
|
|
case VOLUME_MIXER_TYPE_OSS:
|
|
closeOssMixer();
|
|
break;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void initVolume() {
|
|
if(0);
|
|
#ifdef HAVE_ALSA
|
|
else if(strcmp((getConf())[CONF_MIXER_TYPE],VOLUME_MIXER_ALSA)==0) {
|
|
volume_mixerType = VOLUME_MIXER_TYPE_ALSA;
|
|
volume_mixerDevice = VOLUME_MIXER_ALSA_DEFAULT;
|
|
}
|
|
#endif
|
|
#ifndef NO_OSS_MIXER
|
|
else if(strcmp((getConf())[CONF_MIXER_TYPE],VOLUME_MIXER_OSS)==0) {
|
|
volume_mixerType = VOLUME_MIXER_TYPE_OSS;
|
|
volume_mixerDevice = VOLUME_MIXER_OSS_DEFAULT;
|
|
}
|
|
#endif
|
|
else if(strcmp((getConf())[CONF_MIXER_TYPE],VOLUME_MIXER_SOFTWARE)==0) {
|
|
volume_mixerType = VOLUME_MIXER_TYPE_SOFTWARE;
|
|
volume_mixerDevice = VOLUME_MIXER_SOFTWARE_DEFAULT;
|
|
}
|
|
else {
|
|
ERROR("unknown mixer type: %s\n",(getConf())[CONF_MIXER_TYPE]);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
if(strlen((getConf())[CONF_MIXER_DEVICE])) {
|
|
volume_mixerDevice = (getConf())[CONF_MIXER_DEVICE];
|
|
}
|
|
}
|
|
|
|
void openVolumeDevice() {
|
|
if(prepMixer(volume_mixerDevice)<0) {
|
|
ERROR("using software volume\n");
|
|
volume_mixerType = VOLUME_MIXER_TYPE_SOFTWARE;
|
|
}
|
|
}
|
|
|
|
int getSoftwareVolume() {
|
|
return volume_softwareSet;
|
|
}
|
|
|
|
int getVolumeLevel() {
|
|
switch(volume_mixerType) {
|
|
#ifdef HAVE_ALSA
|
|
case VOLUME_MIXER_TYPE_ALSA:
|
|
return getAlsaVolumeLevel();
|
|
#endif
|
|
#ifndef NO_OSS_MIXER
|
|
case VOLUME_MIXER_TYPE_OSS:
|
|
return getOssVolumeLevel();
|
|
#endif
|
|
case VOLUME_MIXER_TYPE_SOFTWARE:
|
|
return getSoftwareVolume();
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
int changeSoftwareVolume(FILE * fp, int change, int rel) {
|
|
int new = change;
|
|
|
|
if(rel) new+=volume_softwareSet;
|
|
|
|
if(new>100) new = 100;
|
|
else if(new<0) new = 0;
|
|
|
|
volume_softwareSet = new;
|
|
|
|
/*new = 100.0*(exp(new/50.0)-1)/(M_E*M_E-1)+0.5;*/
|
|
if(new>=100) new = 1000;
|
|
else if(new<=0) new = 0;
|
|
else new = 1000.0*(exp(new/25.0)-1)/(54.5981500331F-1)+0.5;
|
|
|
|
setPlayerSoftwareVolume(new);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int changeVolumeLevel(FILE * fp, int change, int rel) {
|
|
switch(volume_mixerType) {
|
|
#ifdef HAVE_ALSA
|
|
case VOLUME_MIXER_TYPE_ALSA:
|
|
return changeAlsaVolumeLevel(fp,change,rel);
|
|
#endif
|
|
#ifndef NO_OSS_MIXER
|
|
case VOLUME_MIXER_TYPE_OSS:
|
|
return changeOssVolumeLevel(fp,change,rel);
|
|
#endif
|
|
case VOLUME_MIXER_TYPE_SOFTWARE:
|
|
return changeSoftwareVolume(fp,change,rel);
|
|
default:
|
|
myfprintf(fp,"%s no volume support!\n",COMMAND_RESPOND_ERROR);
|
|
return -1;
|
|
}
|
|
}
|
|
/* vim:set shiftwidth=4 tabstop=8 expandtab: */
|