Exemplo n.º 1
0
double getnormvolume(snd_mixer_elem_t *elem) {
    long min=0, max=0, value = 0;
	double normalized=0, min_norm = 0;
    int err;

	err = snd_mixer_selem_get_playback_dB_range(elem, &min, &max);
    if (err < 0 || min >= max) {
        err = snd_mixer_selem_get_playback_volume_range(elem, &min, &max);
        if (err < 0 || min == max)
            return 0;

        err = snd_mixer_selem_get_playback_volume(elem, channel, &value);
        if (err < 0)
            return 0;

        return (value - min) / (double)(max - min);
    }

	err = snd_mixer_selem_get_playback_dB(elem, channel, &value);
    if (err < 0)
        return 0;

    if (use_linear_dB_scale(min, max))
        return (value - min) / (double)(max - min);

	normalized = exp10((value - max) / 6000.0);
	if (min != SND_CTL_TLV_DB_GAIN_MUTE) {
		min_norm = exp10((min - max) / 6000.0);
		normalized = (normalized - min_norm) / (1 - min_norm);
	}

	return normalized;
}
Exemplo n.º 2
0
/**
 * Adjusts the current volume and sends a notification (if enabled).
 *
 * @param vol new volume value
 * @param dir select direction (-1 = accurate or first bellow, 0 = accurate,
 * 1 = accurate or first above)
 * @param notify whether to send notification
 * @return 0 on success otherwise negative error code
 */
int
setvol(int vol, int dir, gboolean notify)
{
    long min = 0, max = 0, value;
    int cur_perc = getvol();
    double dvol = 0.01 * vol;

    int err = snd_mixer_selem_get_playback_dB_range(elem, &min, &max);
    if (err < 0 || min >= max || !normalize_vol()) {
        err = snd_mixer_selem_get_playback_volume_range(elem, &min, &max);
        value = lrint_dir(dvol * (max - min), dir) + min;
        snd_mixer_selem_set_playback_volume_all(elem, value);
        if (enable_noti && notify && cur_perc != getvol())
            do_notify_volume(getvol(), FALSE);
        // intentionally set twice
        return snd_mixer_selem_set_playback_volume_all(elem, value);
    }

    if (use_linear_dB_scale(min, max)) {
        value = lrint_dir(dvol * (max - min), dir) + min;
        return snd_mixer_selem_set_playback_dB_all(elem, value, dir);
    }

    if (min != SND_CTL_TLV_DB_GAIN_MUTE) {
        double min_norm = exp10((min - max) / 6000.0);
        dvol = dvol * (1 - min_norm) + min_norm;
    }

    value = lrint_dir(6000.0 * log10(dvol), dir) + max;
    snd_mixer_selem_set_playback_dB_all(elem, value, dir);
    if (enable_noti && notify && cur_perc != getvol())
        do_notify_volume(getvol(), FALSE);
    // intentionally set twice
    return snd_mixer_selem_set_playback_dB_all(elem, value, dir);
}
Exemplo n.º 3
0
static gint
_j4status_alsa_section_elem_callback(snd_mixer_elem_t *elem, guint mask)
{
    J4statusAlsaSection *section = snd_mixer_elem_get_callback_private(elem);
    if ( mask == SND_CTL_EVENT_MASK_REMOVE )
        return 0;

    if ( mask & SND_CTL_EVENT_MASK_INFO )
    {
        glong min, max;
        int error;
        error = snd_mixer_selem_get_playback_dB_range(elem, &min, &max);
        if ( ( error == 0 ) && ( min < max ) )
            section->use_dB = TRUE;
        else
        {
            section->use_dB = FALSE;
            snd_mixer_selem_get_playback_volume_range(elem, &min, &max);
        }
        section->min = min;
        section->max = max;
    }

    if ( mask & SND_CTL_EVENT_MASK_VALUE )
        _j4status_alsa_section_update(section, elem);

    return 0;
}
Exemplo n.º 4
0
static int init(int argc, char **argv) {
  const char *str;
  int value;

  int hardware_mixer = 0;

  config.audio_backend_latency_offset = 0;           // this is the default for ALSA
  config.audio_backend_buffer_desired_length = 6615; // default for alsa with a software mixer
  
  

  // get settings from settings file first, allow them to be overridden by command line options

  if (config.cfg != NULL) {
    /* Get the desired buffer size setting. */
    if (config_lookup_int(config.cfg, "alsa.audio_backend_buffer_desired_length_software", &value)) {
      if ((value < 0) || (value > 66150))
        die("Invalid alsa audio backend buffer desired length (software) \"%d\". It should be between 0 and "
            "66150, default is 6615",
            value);
      else {
        config.audio_backend_buffer_desired_length = value;
      }
    }
    
    /* Get the latency offset. */
    if (config_lookup_int(config.cfg, "alsa.audio_backend_latency_offset", &value)) {
      if ((value < -66150) || (value > 66150))
        die("Invalid alsa audio backend buffer latency offset \"%d\". It should be between -66150 and +66150, default is 0",
            value);
      else
        config.audio_backend_latency_offset = value;
    }

    /* Get the Output Device Name. */
    if (config_lookup_string(config.cfg, "alsa.output_device", &str)) {
      alsa_out_dev = (char *)str;
    }


    /* Get the Mixer Type setting. */

    if (config_lookup_string(config.cfg, "alsa.mixer_type", &str)) {
      inform("The alsa mixer_type setting is deprecated and has been ignored. FYI, using the \"mixer_control_name\" setting automatically chooses a hardware mixer.");
    }
    

    /* Get the Mixer Device Name. */
    if (config_lookup_string(config.cfg, "alsa.mixer_device", &str)) {
      alsa_mix_dev = (char *)str;
    }

    /* Get the Mixer Control Name. */
    if (config_lookup_string(config.cfg, "alsa.mixer_control_name", &str)) {
      alsa_mix_ctrl = (char *)str;
      hardware_mixer = 1;
    }
  }

  optind = 1; // optind=0 is equivalent to optind=1 plus special behaviour
  argv--;     // so we shift the arguments to satisfy getopt()
  argc++;
  // some platforms apparently require optreset = 1; - which?
  int opt;
  while ((opt = getopt(argc, argv, "d:t:m:c:i:")) > 0) {
    switch (opt) {
    case 'd':
      alsa_out_dev = optarg;
      break;

    case 't':
      inform("The alsa backend -t option is deprecated and has been ignored. FYI, using the -c option automatically chooses a hardware mixer.");
      break;

    case 'm':
      alsa_mix_dev = optarg;
      break;
    case 'c':
      alsa_mix_ctrl = optarg;
      hardware_mixer = 1;
      break;
    case 'i':
      alsa_mix_index = strtol(optarg, NULL, 10);
      break;
    default:
      help();
      die("Invalid audio option -%c specified", opt);
    }
  }

  if (optind < argc)
    die("Invalid audio argument: %s", argv[optind]);
  
  debug(1,"Output device name is \"%s\".",alsa_out_dev);

  if (!hardware_mixer)
    return 0;

  if (alsa_mix_dev == NULL)
    alsa_mix_dev = alsa_out_dev;

  int ret = 0;
  
  snd_mixer_selem_id_alloca(&alsa_mix_sid);
  snd_mixer_selem_id_set_index(alsa_mix_sid, alsa_mix_index);
  snd_mixer_selem_id_set_name(alsa_mix_sid, alsa_mix_ctrl);

  if ((snd_mixer_open(&alsa_mix_handle, 0)) < 0)
    die("Failed to open mixer");
  debug(1,"Mixer device name is \"%s\".",alsa_mix_dev);
  if ((snd_mixer_attach(alsa_mix_handle, alsa_mix_dev)) < 0)
    die("Failed to attach mixer");
  if ((snd_mixer_selem_register(alsa_mix_handle, NULL, NULL)) < 0)
    die("Failed to register mixer element");

  ret = snd_mixer_load(alsa_mix_handle);
  if (ret < 0)
    die("Failed to load mixer element");
  
  debug(1,"Mixer Control name is \"%s\".",alsa_mix_ctrl);
    
  alsa_mix_elem = snd_mixer_find_selem(alsa_mix_handle, alsa_mix_sid);    
  if (!alsa_mix_elem)
    die("Failed to find mixer element");

 
  if (snd_mixer_selem_get_playback_volume_range(alsa_mix_elem, &alsa_mix_minv, &alsa_mix_maxv) < 0)
    debug(1, "Can't read mixer's [linear] min and max volumes.");
  else {
    if (snd_mixer_selem_get_playback_dB_range (alsa_mix_elem, &alsa_mix_mindb, &alsa_mix_maxdb) == 0) {

      audio_alsa.volume = &volume; // insert the volume function now we know it can do dB stuff
      audio_alsa.parameters = &parameters; // likewise the parameters stuff
      if (alsa_mix_mindb == SND_CTL_TLV_DB_GAIN_MUTE) {
        // Raspberry Pi does this
        debug(1, "Lowest dB value is a mute.");
        if (snd_mixer_selem_ask_playback_vol_dB(alsa_mix_elem, alsa_mix_minv + 1,
                                                &alsa_mix_mindb) == 0)
          debug(1, "Can't get dB value corresponding to a \"volume\" of 1.");
      }
      debug(1, "Hardware mixer has dB volume from %f to %f.", (1.0 * alsa_mix_mindb) / 100.0,
            (1.0 * alsa_mix_maxdb) / 100.0);
      if (config.volume_range_db) {
        long suggested_alsa_min_db = alsa_mix_maxdb - (long)trunc(config.volume_range_db*100);
        if (suggested_alsa_min_db > alsa_mix_mindb)
          alsa_mix_mindb = suggested_alsa_min_db;
        else
          inform("The volume_range_db setting, %f is greater than the native range of the mixer %f, so it is ignored.",config.volume_range_db,(alsa_mix_maxdb-alsa_mix_mindb)/100.0);
      }else{

        if(config.isset_volume_min_db){
          long suggested_alsa_min_db = (long) trunc(config.volume_min_db * 100);
          if(suggested_alsa_min_db > alsa_mix_mindb){
            alsa_mix_mindb = suggested_alsa_min_db; 
          }
        }

        if(config.isset_volume_max_db){
          long suggested_alsa_max_db = (long) trunc(config.volume_max_db * 100);
          if(suggested_alsa_max_db < alsa_mix_maxdb){
            alsa_mix_maxdb = suggested_alsa_max_db;
          }
        }
        
      }
    } else {
      // use the linear scale and do the db conversion ourselves
      debug(1, "note: the hardware mixer specified -- \"%s\" -- does not have a dB volume scale, so it can't be used.",alsa_mix_ctrl);
      /*
      debug(1, "Min and max volumes are %d and %d.",alsa_mix_minv,alsa_mix_maxv);
      alsa_mix_maxdb = 0;
      if ((alsa_mix_maxv!=0) && (alsa_mix_minv!=0))
        alsa_mix_mindb = -20*100*(log10(alsa_mix_maxv*1.0)-log10(alsa_mix_minv*1.0));
      else if (alsa_mix_maxv!=0)
        alsa_mix_mindb = -20*100*log10(alsa_mix_maxv*1.0);
      audio_alsa.volume = &linear_volume; // insert the linear volume function
      audio_alsa.parameters = &parameters; // likewise the parameters stuff
      debug(1,"Max and min dB calculated are %d and %d.",alsa_mix_maxdb,alsa_mix_mindb);
      */
     }
  }
  if (snd_mixer_selem_has_playback_switch(alsa_mix_elem)) {
    has_mute = 1;
    debug(1, "Has mute ability.");
  }
  return 0;
}
Exemplo n.º 5
0
static void set_mixer(const char *device, const char *mixer, int mixer_index, bool setmax, float ldB, float rdB) {
	int err;
	long nleft, nright;
	long min, max;
	snd_mixer_t *handle;
	snd_mixer_selem_id_t *sid;
	snd_mixer_elem_t* elem;

	if ((err = snd_mixer_open(&handle, 0)) < 0) {
		LOG_ERROR("open error: %s", snd_strerror(err));
		return;
	}
	if ((err = snd_mixer_attach(handle, device)) < 0) {
		LOG_ERROR("attach error: %s", snd_strerror(err));
		snd_mixer_close(handle);
		return;
	}
	if ((err = snd_mixer_selem_register(handle, NULL, NULL)) < 0) {
		LOG_ERROR("register error: %s", snd_strerror(err));
		snd_mixer_close(handle);
		return;
	}
	if ((err = snd_mixer_load(handle)) < 0) {
		LOG_ERROR("load error: %s", snd_strerror(err));
		snd_mixer_close(handle);
		return;
	}

	snd_mixer_selem_id_alloca(&sid);

	snd_mixer_selem_id_set_index(sid, mixer_index);
	snd_mixer_selem_id_set_name(sid, mixer);

	if ((elem = snd_mixer_find_selem(handle, sid)) == NULL) {
		LOG_ERROR("error find selem %s", mixer);
		snd_mixer_close(handle);
		return;
	}

	if (snd_mixer_selem_has_playback_switch(elem)) {
		snd_mixer_selem_set_playback_switch_all(elem, 1); // unmute
	}

	err = snd_mixer_selem_get_playback_dB_range(elem, &min, &max);

	if (err < 0 || max - min < 1000) {
		// unable to get db range or range is less than 10dB - ignore and set using raw values
		if ((err = snd_mixer_selem_get_playback_volume_range(elem, &min, &max)) < 0) {
			LOG_ERROR("unable to get volume raw range");
		} else {
			long lraw, rraw;
			if (setmax) {
				lraw = rraw = max;
			} else {
				lraw = ((ldB > -MINVOL_DB ? MINVOL_DB + floor(ldB) : 0) / MINVOL_DB * (max-min)) + min;
				rraw = ((rdB > -MINVOL_DB ? MINVOL_DB + floor(rdB) : 0) / MINVOL_DB * (max-min)) + min;
			}
			LOG_DEBUG("setting vol raw [%ld..%ld]", min, max);
			if ((err = snd_mixer_selem_set_playback_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, lraw)) < 0) {
				LOG_ERROR("error setting left volume: %s", snd_strerror(err));
			}
			if ((err = snd_mixer_selem_set_playback_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, rraw)) < 0) {
				LOG_ERROR("error setting right volume: %s", snd_strerror(err));
			}
		}
	} else {
		// set db directly
		LOG_DEBUG("setting vol dB [%ld..%ld]", min, max);
		if (setmax) {
			// set to 0dB if available as this should be max volume for music recored at max pcm values
			if (max >= 0 && min <= 0) {
				ldB = rdB = 0;
			} else {
				ldB = rdB = max;
			}
		}
		if ((err = snd_mixer_selem_set_playback_dB(elem, SND_MIXER_SCHN_FRONT_LEFT, 100 * ldB, 1)) < 0) {
			LOG_ERROR("error setting left volume: %s", snd_strerror(err));
		}
		if ((err = snd_mixer_selem_set_playback_dB(elem, SND_MIXER_SCHN_FRONT_RIGHT, 100 * rdB, 1)) < 0) {
			LOG_ERROR("error setting right volume: %s", snd_strerror(err));
		}
	}

	if ((err = snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, &nleft)) < 0) {
		LOG_ERROR("error getting left vol: %s", snd_strerror(err));
	}
	if ((err = snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, &nright)) < 0) {
		LOG_ERROR("error getting right vol: %s", snd_strerror(err));
	}

	LOG_DEBUG("%s left: %3.1fdB -> %ld right: %3.1fdB -> %ld", mixer, ldB, nleft, rdB, nright);

	snd_mixer_close(handle);
}