On Mon, Aug 31, 2009 at 11:25:19PM -0400, Ryan Flannery wrote: > Hi Jacob, > > Many thanks for your comments. They've saved me at least a couple of > hours of digging (though prompted some more!) > If you wouldn't mind and have the time, I have a few questions... > > On Mon, Aug 31, 2009 at 6:43 PM, Jacob Meuser<jake...@sdf.lonestar.org> wrote: > > couple issues here ... > > > > ? /* find the "master" device */ > > ? v->master_idx = -1; > > ? devinfo.index = 0; > > ? while (ioctl(v->dev_fd, AUDIO_MIXER_DEVINFO, &devinfo) >= 0) > > ? { ?if (strncmp(devinfo.label.name, "master", 7) == 0) > > ? ? ? ? v->master_idx = devinfo.index; > > > > ? ? ?devinfo.index++; > > ? } > > > > ? if (v->master_idx == -1) > > ? ? ?errx(1, "failed to find \"master\" mixer device for volume init"); > > > > you should make sure you get outputs.master as opposed to possibly > > record.master, or some other "master". ?technically speaking, a device > > is free to make up whatever class it wants. ?you have to find the > > "outputs" class index, and make sure that "master"'s mixer_class equals > > the "outputs" class index. > > > D'oh! Thanks for that. This is why (i think) it was failing for Edd > and (possibly) Brynet. > > > > > > ? /* Volume values are stored as 8-bit values, however not all values are > > ? ?* supported (i.e. fewer than 256 different volume levels may be > > ? ?* supported). ?As such, to accurately determine the percentages shown > > ? ?* in the display, we have to find the maximum settings for this > > ? ?* device. > > > > you could use the delta: devinfo.un.v.delta. ?that is the step size > > of the hardware. ?that is, you have to move that many volume units to > > affect a change in the hardware. ?you could use this to determine > > the largest value ... > > Many thanks, this is much easier. > > > > > ?So, we... > > ? ?* ? ?1. read the current volume values > > ? ?* ? ?2. set the volume to the max possible (255) > > > > 255 -> AUDIO_MAX_GAIN > > > > ? ?* ? ?3. read the volume values and see what the max is for this device > > ? ?* ? ?4. reset the volume to what was read in step 1 so we don't muck > > ? ?* ? ? ? with the users' volume. > > ? ?*/ > > > > > > ? /* read current volume */ > > ? v->info.dev = v->master_idx; > > ? v->info.type = AUDIO_MIXER_VALUE; > > ? if (ioctl(v->dev_fd, AUDIO_MIXER_READ, &(v->info)) < 0) > > ? ? ?err(1, "ioctl AUDIO_MIXER_READ"); > > > > that fails because on many devices because num_channels is not set > > correctly. ?in sys/dev/ic/ac97.c: > > > > ac97_mixer_get_port(struct ac86_codec_if *codec_if, mixer_ctrl_t *cp) > > { > > ? ? ? ?struct ac97_softc *as = (struct ac97_softc *)codec_if; > > ? ? ? ?struct ac97_source_info *si = &as->source_info[cp->dev]; > > ... > > ? ? ? ?case AUDIO_MIXER_VALUE: > > ? ? ? ?{ > > ? ? ? ? ? ? ? ?const struct audio_mixer_value *value = si->info; > > ? ? ? ? ? ? ? ?u_int16_t ?l, r; > > > > ? ? ? ? ? ? ? ?if ((cp->un.value.num_channels <= 0) || > > ? ? ? ? ? ? ? ? ? ?(cp->un.value.num_channels > value->num_channels)) > > ? ? ? ? ? ? ? ? ? ? ? ?return (EINVAL); > > > > here, value->num_channels == devinfo.un.v.num_channels ... > > Hmm, I'm not sure I understand. Let me see if this is what you're > saying (and what i'm getting from ac97.c). > > Currently, I ioctl with AUDIO_MIXER_READ, and the mixer_ctrl_t filled > would (I assumed) always have num_channels set to 1 or "other" (which > I always assume other would be >= 2), and I tried to handle like > this... > > if (v->info.un.value.num_channels == 1) > v->left = v->right = v->info.un.value.level[AUDIO_MIXER_LEVEL_MONO]; > else > { v->left = v->info.un.value.level[AUDIO_MIXER_LEVEL_LEFT]; > v->right = v->info.un.value.level[AUDIO_MIXER_LEVEL_RIGHT]; > } > > But you're saying (and from the source i can see), that it may return > either 0 channels or ... something greater than values->num_channels, > in which case the ioctl returns EINVAL. > > In such a case, what would the appropriate method be for querying > volume level? Or would this represent a situation where this cannot > be queried (lack of hardware or hardware support)? > Or am I lost in left-field here... admittedly I need to look more into this.
for AUDIO_MIXER_READ on a AUDIO_MIXER_VALUE type device, you have to fill in the number of channels in the mixer_ctrl_t. you can get the number of channels from AUDIO_MIXER_DEVINFO: mixer_devinfo_t devinfo; mixer_ctrl_t ctrl; devinfo.index = v->master_idx; ioctl(v->dev_fd, AUDIO_MIXER_DEVINFO, &devinfo); bzero(ctrl); ctrl.dev = devinfo.index; ctrl.type = AUDIO_MIXER_VALUE; ctrl.un.value.num_channels = devinfo.un.v.num_channels; ioctl(v->dev_fd, AUDIO_MIXER_READ, &ctrl); below are completely untested patches against the tarball. maybe they will give more hints ;) -- jake...@sdf.lonestar.org SDF Public Access UNIX System - http://sdf.lonestar.org --- stats.h.orig Mon Aug 31 19:01:31 2009 +++ stats.h Mon Aug 31 18:55:01 2009 @@ -54,6 +54,7 @@ typedef struct volume_info { mixer_ctrl_t info; int max; /* max possible value (not always 255) */ + int nchan; int left; int right; } volume_info_t; --- stats_collect.c.orig Mon Aug 31 18:13:00 2009 +++ stats_collect.c Mon Aug 31 19:01:18 2009 @@ -19,10 +19,32 @@ extern int ncpu; +int +check_dev(int fd, int class, char *name) +{ + mixer_devinfo_t devinfo; + + if (class < 0 || name == NULL) + return(-1); + + devinfo.index = 0; + while (ioctl(fd, AUDIO_MIXER_DEVINFO, &devinfo) >= 0) + { + if ((devinfo.type == AUDIO_MIXER_VALUE) && + (devinfo.mixer_class == class) && + (strncmp(devinfo.label.name, name, MAX_AUDIO_DEV_LEN) == 0)) + return(devinfo.index); + + devinfo.index++; + } + return(-1); +} + void init_volume(volume_info_t *v) { mixer_devinfo_t devinfo; + int oclass_idx, iclass_idx; /* We need read and write capabilities for the init (see comment below). * Before we're finished, we close this fd and then re-open as read- @@ -32,63 +54,41 @@ init_volume(volume_info_t *v) if (v->dev_fd < 0) err(1, "/dev/mixer"); - /* find the "master" device */ - v->master_idx = -1; + /* find the outputs and inputs classes */ + oclass_idx = iclass_idx = -1; devinfo.index = 0; while (ioctl(v->dev_fd, AUDIO_MIXER_DEVINFO, &devinfo) >= 0) - { if (strncmp(devinfo.label.name, "master", 7) == 0) - v->master_idx = devinfo.index; - + { + if (devinfo.tye != AUDIO_MIXER_CLASS) + continue; + if (strncmp(devinfo.label.name, AudioCoutputs, MAX_AUDIO_DEV_LEN) == 0) + oclass_idx = devinfo.index; + else if (strncmp(devinfo.label.name, AudioCinputs, MAX_AUDIO_DEV_LEN) == 0) + iclass_idx = devinfo.index; devinfo.index++; } + /* find the "master" device */ + v->master_idx = check_dev(v->dev_fd, oclass_idx, AudioNmaster); + if (v->master_idx == -1) { + v->master_idx = check_dev(v->dev_fd, iclass_idx, AudioNdac); + if (v->master_idx == -1) { + v->master_idx = check_dev(v->dev_fd, oclass_idx, AudioNdac); + if (v->master_idx == -1) { + v->master_idx = check_dev(v->dev_fd, oclass_idx, AudioNoutput); + } + } + } if (v->master_idx == -1) errx(1, "failed to find \"master\" mixer device for volume init"); + devinfo.index = v->master_idx; + if (ioctl(v->dev_fd, AUDIO_MIXER_DEVINFO, &devinfo) == -1) + err("AUDIO_MIXER_DEVINFO"); - /* Volume values are stored as 8-bit values, however not all values are - * supported (i.e. fewer than 256 different volume levels may be - * supported). As such, to accurately determine the percentages shown - * in the display, we have to find the maximum settings for this - * device. So, we... - * 1. read the current volume values - * 2. set the volume to the max possible (255) - * 3. read the volume values and see what the max is for this device - * 4. reset the volume to what was read in step 1 so we don't muck - * with the users' volume. - */ + v->max = AUDIO_MAX_GAIN - (AUDIO_MAX_GAIN % devinfo.un.v.delta); + v->nchan = devinfo.un.v.num_channels; - /* read current volume */ - v->info.dev = v->master_idx; - v->info.type = AUDIO_MIXER_VALUE; - if (ioctl(v->dev_fd, AUDIO_MIXER_READ, &(v->info)) < 0) - err(1, "ioctl AUDIO_MIXER_READ"); - - if (v->info.un.value.num_channels == 1) - v->left = v->right = v->info.un.value.level[AUDIO_MIXER_LEVEL_MONO]; - else - { v->left = v->info.un.value.level[AUDIO_MIXER_LEVEL_LEFT]; - v->right = v->info.un.value.level[AUDIO_MIXER_LEVEL_RIGHT]; - } - - /* now set the volume to the max possible */ - v->info.un.value.level[AUDIO_MIXER_LEVEL_LEFT] = 255; - v->info.un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = 255; - if (ioctl(v->dev_fd, AUDIO_MIXER_WRITE, &(v->info)) < 0) - err(1, "ioctl AUDIO_MIXER_WRITE"); - - /* read what the max value is */ - if (ioctl(v->dev_fd, AUDIO_MIXER_READ, &(v->info)) < 0) - err(1, "ioctl AUDIO_MIXER_READ"); - - v->max = v->info.un.value.level[AUDIO_MIXER_LEVEL_MONO]; - - /* reset volume to defaults */ - v->info.un.value.level[AUDIO_MIXER_LEVEL_LEFT] = v->left; - v->info.un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = v->right; - if (ioctl(v->dev_fd, AUDIO_MIXER_WRITE, &(v->info)) < 0) - err(1, "ioctl AUDIO_MIXER_WRITE"); - /* finished... now close the device and reopen as read only */ close(v->dev_fd); v->dev_fd = open("/dev/mixer", O_RDONLY, "r"); @@ -99,12 +99,14 @@ init_volume(volume_info_t *v) void update_volume(volume_info_t *v) { + bzero(v->info); v->info.dev = v->master_idx; v->info.type = AUDIO_MIXER_VALUE; + v->info.un.value.num_channels = v->nchan; if (ioctl(v->dev_fd, AUDIO_MIXER_READ, &(v->info)) < 0) err(1, "ioctl AUDIO_MIXER_READ"); - if (v->info.un.value.num_channels == 1) + if (v->nchan == 1) v->left = v->right = v->info.un.value.level[AUDIO_MIXER_LEVEL_MONO]; else { v->left = v->info.un.value.level[AUDIO_MIXER_LEVEL_LEFT];