/** * Copyright (C) 2003 by Greg Watson * * 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, 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 #include #include #include #include #include #include #include #include #include #include "utils.h" #include "mixer.h" #include static char mixer_channel[64] = "Line Playback Volume"; /* Channel control name */ static char alsa_card[64] = "default"; /* Card name, can be IE default, Audugy2,headset */ static int mixer_vol_min = 0; /* Min volume level for channel */ static int mixer_vol_max = 100; /* Max volume level for channel */ static int mixer_step = 1; /* Incerment value for a channel */ static int mixer_count = 1; /* Number of channels per control IE left/right */ static int mute_old = 50; /* Old volume level before muted */ static int muted = 0; /* Is channel muted */ int mixer_get_volume( void ) { int err, curvol = 0; snd_ctl_elem_id_t *mixer_id; snd_ctl_elem_value_t *mixer_control; snd_ctl_elem_info_t *mixer_info; snd_hctl_t *hctl; snd_hctl_elem_t *elem; /* Allocate structs */ snd_ctl_elem_id_alloca(&mixer_id); snd_ctl_elem_value_alloca(&mixer_control); snd_ctl_elem_info_alloca(&mixer_info); /* default mixer control */ snd_ctl_elem_id_set_interface(mixer_id, SND_CTL_ELEM_IFACE_MIXER); /* NOTE mixer_channel is a char * NOT a int like for OSS */ snd_ctl_elem_id_set_name(mixer_id, mixer_channel); snd_ctl_elem_value_set_id(mixer_control, mixer_id); if ((err = snd_hctl_open(&hctl, alsa_card, 0)) < 0) { fprintf(stderr, "mixer: Can't open mixer control '%s'\n", (char *)mixer_control); return 0; } /* load the hardware control */ if ((err = snd_hctl_load(hctl)) < 0) fprintf(stderr, "mixer: Can't load mixer control '%s'\n", (char *)mixer_control); elem = snd_hctl_find_elem(hctl, mixer_id); if (elem) { /* Note we are only grabbing the first channel so left/right balance will be set together */ snd_hctl_elem_read(elem, mixer_control); curvol = snd_ctl_elem_value_get_integer(mixer_control, 0); } else return 30; snd_hctl_close(hctl); /* Before return value, make it's a current/max percentage */ return rint( (double)curvol / (double)mixer_vol_max * 100); } /* Set the volume by given percent */ int mixer_set_volume( int percentdiff ) { int levelpercentage, realvol; int err, idx; snd_ctl_t *handle; snd_ctl_elem_id_t *mixer_id; snd_ctl_elem_value_t *mixer_control; snd_ctl_elem_info_t *mixer_info; /* Allocate structs */ snd_ctl_elem_id_alloca(&mixer_id); snd_ctl_elem_value_alloca(&mixer_control); snd_ctl_elem_info_alloca(&mixer_info); /* default mixer control */ snd_ctl_elem_id_set_interface(mixer_id, SND_CTL_ELEM_IFACE_MIXER); /* NOTE mixer_channel is a char * NOT a int like for OSS */ snd_ctl_elem_id_set_name(mixer_id, mixer_channel); /* Get the current volume */ levelpercentage = mixer_get_volume(); /* Open the card */ if ((err = snd_ctl_open(&handle, alsa_card, 0)) < 0) { fprintf(stderr, "mixer: Can't open card %s open error: %s\n", alsa_card, snd_strerror(err)); return 0; } /* Set what we want to change */ snd_ctl_elem_info_set_id(mixer_info, mixer_id); if ((err = snd_ctl_elem_info(handle, mixer_info)) < 0) { fprintf(stderr, "mixer: Can't select %s on card %s cinfo error: %s\n", mixer_channel, alsa_card, snd_strerror(err)); return 0; } /* FIXME: Remove it when hctl find works ok !!! */ snd_ctl_elem_info_get_id(mixer_info, mixer_id); /* Get the number of channels (ie 2 = stereo) so we have to set both levels */ snd_ctl_elem_value_set_id(mixer_control, mixer_id); /* Since we don't know how many untis a mixer value can have divide it up and multiply times the target change */ levelpercentage += (percentdiff * rint(100 / mixer_vol_max)); /* Make sure range is vaild */ if( levelpercentage > 100 ) levelpercentage = 100; if( levelpercentage < 0 ) levelpercentage = 0; realvol = rint( (double)levelpercentage / 100 * mixer_vol_max ); for (idx = 0; idx < mixer_count && idx < 128; idx++) snd_ctl_elem_value_set_integer(mixer_control, idx, realvol); /* Write the changes to the card */ if ((err = snd_ctl_elem_write(handle, mixer_control)) < 0) { fprintf(stderr, "mixer: Can't write to control %s element: %s\n", alsa_card, snd_strerror(err)); return 0; } snd_ctl_close(handle); /* report back the changes we made */ return levelpercentage; } /* Mute the set channel */ void mixer_mute( int mute ) { if (mute) { mute_old = mixer_get_volume(); mixer_set_volume(-100); muted = 1; } else { mixer_set_volume( mute_old ); muted = 0; } return; } /* Is channel muted? */ int mixer_ismute( void ) { /* static global muted returned */ return muted; } /* Setup the mixer channel */ void mixer_set_device( const char *devname ) { int err = 0; char *pos; snd_hctl_t *hctl; snd_ctl_t *handle; snd_ctl_elem_id_t *mixer_id; snd_ctl_elem_value_t *mixer_control; snd_ctl_elem_info_t *mixer_info; /* Allocate the global static structs */ snd_ctl_elem_id_alloca(&mixer_id); snd_ctl_elem_value_alloca(&mixer_control); snd_ctl_elem_info_alloca(&mixer_info); /* Process the devname and see if we have card and channel */ pos = strchr(devname, ':'); if (pos != NULL) { /* extract the card ^ to : */ strncpy(alsa_card, devname, strlen(devname) - strlen(pos)); /* Since ALSA uses card refs cards by hw: we need to find the card ID and replace the description string */ int i; i = snd_card_get_index(alsa_card); if (i >= 0 && i < 32) sprintf(alsa_card, "hw:%i", i); else { fprintf(stderr, "mixer: Cannot find card %s, using default.", alsa_card); strcpy(alsa_card, "default"); } /* now the channel */ strncpy(mixer_channel, pos + 1, strlen(pos)); } else strncpy(mixer_channel, devname, strlen(devname) > 64 ? 63 : strlen(devname) ); /* default mixer control */ snd_ctl_elem_id_set_interface(mixer_id, SND_CTL_ELEM_IFACE_MIXER); /* NOTE mixer_channel is a char * NOT a int like for OSS */ snd_ctl_elem_id_set_name(mixer_id, mixer_channel); /* Open the card */ if ((err = snd_ctl_open(&handle, alsa_card, 0)) < 0) { fprintf(stderr, "mixer: Can't open card %s open error: %s\n", alsa_card, snd_strerror(err)); return; } /* Set what we want to change */ snd_ctl_elem_info_set_id(mixer_info, mixer_id); if ((err = snd_ctl_elem_info(handle, mixer_info)) < 0) { fprintf(stderr, "mixer: Can't select %s on card %s cinfo error: %s\n", mixer_channel, alsa_card, snd_strerror(err)); return; } /* Global statics used for most other mixer functions */ mixer_vol_min = snd_ctl_elem_info_get_min(mixer_info); mixer_vol_max = snd_ctl_elem_info_get_max(mixer_info); mixer_step = snd_ctl_elem_info_get_step(mixer_info); mixer_count = snd_ctl_elem_info_get_count(mixer_info); /* Prevent division by zero */ mixer_vol_max = (mixer_vol_max == 0) ? 1 : mixer_vol_max; snd_ctl_close(handle); return; }