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

Reply via email to