Beispiel #1
0
static void selem_free(snd_mixer_elem_t *elem)
{
	selem_none_t *simple = snd_mixer_elem_get_private(elem);
	assert(snd_mixer_elem_get_type(elem) == SND_MIXER_ELEM_SIMPLE);
	if (simple->selem.id)
		snd_mixer_selem_id_free(simple->selem.id);
	/* free db range information */
	free(simple->str[0].db_info);
	free(simple->str[1].db_info);
	free(simple);
}
Beispiel #2
0
static int simple_update(snd_mixer_elem_t *melem)
{
	selem_none_t *simple;
	unsigned int caps, pchannels, cchannels;
	long pmin, pmax, cmin, cmax;
	selem_ctl_t *ctl;
	const char *name;

	caps = 0;
	pchannels = 0;
	pmin = LONG_MAX;
	pmax = LONG_MIN;
	cchannels = 0;
	cmin = LONG_MAX;
	cmax = LONG_MIN;
	assert(snd_mixer_elem_get_type(melem) == SND_MIXER_ELEM_SIMPLE);
	simple = snd_mixer_elem_get_private(melem);
	name = snd_mixer_selem_get_name(melem);
	ctl = &simple->ctls[CTL_SINGLE];
	if (ctl->elem) {
		pchannels = cchannels = ctl->values;
		if (ctl->type == SND_CTL_ELEM_TYPE_INTEGER) {
			caps |= SM_CAP_GVOLUME;
			pmin = cmin = ctl->min;
			pmax = cmax = ctl->max;
		} else
			caps |= SM_CAP_GSWITCH;
	}
	ctl = &simple->ctls[CTL_GLOBAL_SWITCH];
	if (ctl->elem) {
		if (pchannels < ctl->values)
			pchannels = ctl->values;
		if (cchannels < ctl->values)
			cchannels = ctl->values;
		caps |= SM_CAP_GSWITCH;
	}
	ctl = &simple->ctls[CTL_GLOBAL_ROUTE];
	if (ctl->elem) {
		if (pchannels < ctl->values)
			pchannels = ctl->values;
		if (cchannels < ctl->values)
			cchannels = ctl->values;
		caps |= SM_CAP_GSWITCH;
	}
	ctl = &simple->ctls[CTL_GLOBAL_VOLUME];
	if (ctl->elem) {
		if (pchannels < ctl->values)
			pchannels = ctl->values;
		if (pmin > ctl->min)
			pmin = ctl->min;
		if (pmax < ctl->max)
			pmax = ctl->max;
		if (cchannels < ctl->values)
			cchannels = ctl->values;
		if (cmin > ctl->min)
			cmin = ctl->min;
		if (cmax < ctl->max)
			cmax = ctl->max;
		caps |= SM_CAP_GVOLUME;
	}
	ctl = &simple->ctls[CTL_PLAYBACK_SWITCH];
	if (ctl->elem) {
		if (pchannels < ctl->values)
			pchannels = ctl->values;
		caps |= SM_CAP_PSWITCH;
		caps &= ~SM_CAP_GSWITCH;
	}
	ctl = &simple->ctls[CTL_PLAYBACK_ROUTE];
	if (ctl->elem) {
		if (pchannels < ctl->values)
			pchannels = ctl->values;
		caps |= SM_CAP_PSWITCH;
		caps &= ~SM_CAP_GSWITCH;
	}
	ctl = &simple->ctls[CTL_CAPTURE_SWITCH];
	if (ctl->elem) {
		if (cchannels < ctl->values)
			cchannels = ctl->values;
		caps |= SM_CAP_CSWITCH;
		caps &= ~SM_CAP_GSWITCH;
	}
	ctl = &simple->ctls[CTL_CAPTURE_ROUTE];
	if (ctl->elem) {
		if (cchannels < ctl->values)
			cchannels = ctl->values;
		caps |= SM_CAP_CSWITCH;
		caps &= ~SM_CAP_GSWITCH;
	}
	ctl = &simple->ctls[CTL_PLAYBACK_VOLUME];
	if (ctl->elem) {
		if (pchannels < ctl->values)
			pchannels = ctl->values;
		if (pmin > ctl->min)
			pmin = ctl->min;
		if (pmax < ctl->max)
			pmax = ctl->max;
		caps |= SM_CAP_PVOLUME;
		caps &= ~SM_CAP_GVOLUME;
	}
	ctl = &simple->ctls[CTL_CAPTURE_VOLUME];
	if (ctl->elem) {
		if (cchannels < ctl->values)
			cchannels = ctl->values;
		if (cmin > ctl->min)
			cmin = ctl->min;
		if (cmax < ctl->max)
			cmax = ctl->max;
		caps |= SM_CAP_CVOLUME;
		caps &= ~SM_CAP_GVOLUME;
	}
	ctl = &simple->ctls[CTL_CAPTURE_SOURCE];
	if (ctl->elem) {
		if (cchannels < ctl->values)
			cchannels = ctl->values;
		caps |= SM_CAP_CSWITCH | SM_CAP_CSWITCH_EXCL;
		caps &= ~SM_CAP_GSWITCH;
	}
	ctl = &simple->ctls[CTL_GLOBAL_ENUM];
	if (ctl->elem) {
		if (pchannels < ctl->values)
			pchannels = ctl->values;
		caps |= SM_CAP_PENUM | SM_CAP_CENUM;
	}
	ctl = &simple->ctls[CTL_PLAYBACK_ENUM];
	if (ctl->elem) {
		if (pchannels < ctl->values)
			pchannels = ctl->values;
		caps |= SM_CAP_PENUM;
	}
	ctl = &simple->ctls[CTL_CAPTURE_ENUM];
	if (ctl->elem) {
		if (pchannels < ctl->values)
			pchannels = ctl->values;
		caps |= SM_CAP_CENUM;
	}
	if (pchannels > 32)
		pchannels = 32;
	if (cchannels > 32)
		cchannels = 32;
	if (caps & (SM_CAP_GSWITCH|SM_CAP_PSWITCH))
		caps |= SM_CAP_PSWITCH_JOIN;
	if (caps & (SM_CAP_GVOLUME|SM_CAP_PVOLUME))
		caps |= SM_CAP_PVOLUME_JOIN;
	if (caps & (SM_CAP_GSWITCH|SM_CAP_CSWITCH))
		caps |= SM_CAP_CSWITCH_JOIN;
	if (caps & (SM_CAP_GVOLUME|SM_CAP_CVOLUME))
		caps |= SM_CAP_CVOLUME_JOIN;
	if (pchannels > 1 || cchannels > 1) {
		if (simple->ctls[CTL_SINGLE].elem &&
		    simple->ctls[CTL_SINGLE].values > 1) {
			if (caps & SM_CAP_GSWITCH)
				caps &= ~(SM_CAP_PSWITCH_JOIN|SM_CAP_CSWITCH_JOIN);
			else
				caps &= ~(SM_CAP_PVOLUME_JOIN|SM_CAP_CVOLUME_JOIN);
		}
		if (simple->ctls[CTL_GLOBAL_ROUTE].elem ||
		    (simple->ctls[CTL_GLOBAL_SWITCH].elem &&
		     simple->ctls[CTL_GLOBAL_SWITCH].values > 1)) {
			caps &= ~(SM_CAP_PSWITCH_JOIN|SM_CAP_CSWITCH_JOIN);
		}
		if (simple->ctls[CTL_GLOBAL_VOLUME].elem &&
		    simple->ctls[CTL_GLOBAL_VOLUME].values > 1) {
			caps &= ~(SM_CAP_PVOLUME_JOIN|SM_CAP_CVOLUME_JOIN);
		}
	}
	if (pchannels > 1) {
		if (simple->ctls[CTL_PLAYBACK_ROUTE].elem ||
		    (simple->ctls[CTL_PLAYBACK_SWITCH].elem &&
		     simple->ctls[CTL_PLAYBACK_SWITCH].values > 1)) {
			caps &= ~SM_CAP_PSWITCH_JOIN;
		}
		if (simple->ctls[CTL_PLAYBACK_VOLUME].elem &&
		    simple->ctls[CTL_PLAYBACK_VOLUME].values > 1) {
			caps &= ~SM_CAP_PVOLUME_JOIN;
		}
	}
	if (cchannels > 1) {
		if (simple->ctls[CTL_CAPTURE_ROUTE].elem ||
		    (simple->ctls[CTL_CAPTURE_SWITCH].elem &&
		     simple->ctls[CTL_CAPTURE_SWITCH].values > 1) ||
		    (simple->ctls[CTL_CAPTURE_SOURCE].elem &&
		     simple->ctls[CTL_CAPTURE_SOURCE].values > 1)) {
			caps &= ~SM_CAP_CSWITCH_JOIN;
		}
		if (simple->ctls[CTL_CAPTURE_VOLUME].elem &&
		    simple->ctls[CTL_CAPTURE_VOLUME].values > 1) {
			caps &= ~SM_CAP_CVOLUME_JOIN;
		}
	}

	/* exceptions */
	if ((caps & (SM_CAP_GSWITCH|SM_CAP_PSWITCH|SM_CAP_CSWITCH)) &&
	    (caps & (SM_CAP_GSWITCH|SM_CAP_PSWITCH|SM_CAP_CSWITCH)) == (caps & SM_CAP_GSWITCH)) {
		caps &= ~(SM_CAP_GSWITCH|SM_CAP_CSWITCH_JOIN|SM_CAP_CSWITCH_EXCL);
		caps |= SM_CAP_PSWITCH;
	}

	if ((caps & SM_CAP_GSWITCH) &&
	    (caps & (SM_CAP_PSWITCH|SM_CAP_CSWITCH)) == 0)
		caps |= SM_CAP_PSWITCH|SM_CAP_CSWITCH;

	if ((caps & SM_CAP_GVOLUME) &&
	    (caps & (SM_CAP_PVOLUME|SM_CAP_CVOLUME)) == 0)
		caps |= SM_CAP_PVOLUME|SM_CAP_CVOLUME;

	simple->selem.caps = caps;
	simple->str[SM_PLAY].channels = pchannels;
	if (!simple->str[SM_PLAY].range) {
		simple->str[SM_PLAY].min = pmin != LONG_MAX ? pmin : 0;
		simple->str[SM_PLAY].max = pmax != LONG_MIN ? pmax : 0;
	}
	simple->str[SM_CAPT].channels = cchannels;
	if (!simple->str[SM_CAPT].range) {
		simple->str[SM_CAPT].min = cmin != LONG_MAX ? cmin : 0;
		simple->str[SM_CAPT].max = cmax != LONG_MIN ? cmax : 0;
	}
	return 0;
}	   
Beispiel #3
0
static int selem_write_main(snd_mixer_elem_t *elem)
{
	selem_none_t *s;
	unsigned int idx;
	int err;

	assert(snd_mixer_elem_get_type(elem) == SND_MIXER_ELEM_SIMPLE);
	s = snd_mixer_elem_get_private(elem);

	if (s->ctls[CTL_GLOBAL_ENUM].elem)
		return elem_write_enum(s);

	if (s->ctls[CTL_PLAYBACK_ENUM].elem)
		return elem_write_enum(s);

	if (s->ctls[CTL_CAPTURE_ENUM].elem)
		return elem_write_enum(s);

	if (s->ctls[CTL_SINGLE].elem) {
		if (s->ctls[CTL_SINGLE].type == SND_CTL_ELEM_TYPE_INTEGER)
			err = elem_write_volume(s, SM_PLAY, CTL_SINGLE);
		else
			err = elem_write_switch(s, SM_PLAY, CTL_SINGLE);
		if (err < 0)
			return err;
	}
	if (s->ctls[CTL_GLOBAL_VOLUME].elem) {
		err = elem_write_volume(s, SM_PLAY, CTL_GLOBAL_VOLUME);
		if (err < 0)
			return err;
	}
	if (s->ctls[CTL_GLOBAL_SWITCH].elem) {
		if (s->ctls[CTL_PLAYBACK_SWITCH].elem && s->ctls[CTL_CAPTURE_SWITCH].elem)
			err = elem_write_switch_constant(s, CTL_GLOBAL_SWITCH, 1);
		else
			err = elem_write_switch(s, SM_PLAY, CTL_GLOBAL_SWITCH);
		if (err < 0)
			return err;
	}
	if (s->ctls[CTL_PLAYBACK_VOLUME].elem) {
		err = elem_write_volume(s, SM_PLAY, CTL_PLAYBACK_VOLUME);
		if (err < 0)
			return err;
	}
	if (s->ctls[CTL_PLAYBACK_SWITCH].elem) {
		err = elem_write_switch(s, SM_PLAY, CTL_PLAYBACK_SWITCH);
		if (err < 0)
			return err;
	}
	if (s->ctls[CTL_PLAYBACK_ROUTE].elem) {
		err = elem_write_route(s, SM_PLAY, CTL_PLAYBACK_ROUTE);
		if (err < 0)
			return err;
	}
	if (s->ctls[CTL_CAPTURE_VOLUME].elem) {
		err = elem_write_volume(s, SM_CAPT, CTL_CAPTURE_VOLUME);
		if (err < 0)
			return err;
	}
	if (s->ctls[CTL_CAPTURE_SWITCH].elem) {
		err = elem_write_switch(s, SM_CAPT, CTL_CAPTURE_SWITCH);
		if (err < 0)
			return err;
	}
	if (s->ctls[CTL_CAPTURE_ROUTE].elem) {
		err = elem_write_route(s, SM_CAPT, CTL_CAPTURE_ROUTE);
		if (err < 0)
			return err;
	}
	if (s->ctls[CTL_CAPTURE_SOURCE].elem) {
		snd_ctl_elem_value_t *ctl;
		selem_ctl_t *c = &s->ctls[CTL_CAPTURE_SOURCE];
		snd_ctl_elem_value_alloca(&ctl);
		if ((err = snd_hctl_elem_read(c->elem, ctl)) < 0)
			return err;
		for (idx = 0; idx < c->values; idx++) {
			if (s->str[SM_CAPT].sw & (1 << idx))
				snd_ctl_elem_value_set_enumerated(ctl, idx, s->capture_item);
		}
		if ((err = snd_hctl_elem_write(c->elem, ctl)) < 0)
			return err;
		/* update the element, don't remove */
		err = selem_read(elem);
		if (err < 0)
			return err;
	}
	return 0;
}
Beispiel #4
0
static int selem_read(snd_mixer_elem_t *elem)
{
	selem_none_t *s;
	unsigned int idx;
	int err = 0;
	long pvol[32], cvol[32];
	unsigned int psw, csw;

	assert(snd_mixer_elem_get_type(elem) == SND_MIXER_ELEM_SIMPLE);
	s = snd_mixer_elem_get_private(elem);

	memcpy(pvol, s->str[SM_PLAY].vol, sizeof(pvol));
	memset(&s->str[SM_PLAY].vol, 0, sizeof(s->str[SM_PLAY].vol));
	psw = s->str[SM_PLAY].sw;
	s->str[SM_PLAY].sw = ~0U;
	memcpy(cvol, s->str[SM_CAPT].vol, sizeof(cvol));
	memset(&s->str[SM_CAPT].vol, 0, sizeof(s->str[SM_CAPT].vol));
	csw = s->str[SM_CAPT].sw;
	s->str[SM_CAPT].sw = ~0U;

	if (s->ctls[CTL_GLOBAL_ENUM].elem) {
		err = elem_read_enum(s);
		if (err < 0)
			return err;
		goto __skip_cswitch;
	}

	if (s->ctls[CTL_CAPTURE_ENUM].elem) {
		err = elem_read_enum(s);
		if (err < 0)
			return err;
		goto __skip_cswitch;
	}

	if (s->ctls[CTL_PLAYBACK_ENUM].elem) {
		err = elem_read_enum(s);
		if (err < 0)
			return err;
		goto __skip_cswitch;
	}


	if (s->ctls[CTL_PLAYBACK_VOLUME].elem)
		err = elem_read_volume(s, SM_PLAY, CTL_PLAYBACK_VOLUME);
	else if (s->ctls[CTL_GLOBAL_VOLUME].elem)
		err = elem_read_volume(s, SM_PLAY, CTL_GLOBAL_VOLUME);
	else if (s->ctls[CTL_SINGLE].elem &&
		 s->ctls[CTL_SINGLE].type == SND_CTL_ELEM_TYPE_INTEGER)
		err = elem_read_volume(s, SM_PLAY, CTL_SINGLE);
	if (err < 0)
		return err;

	if ((s->selem.caps & (SM_CAP_GSWITCH|SM_CAP_PSWITCH)) == 0) {
		s->str[SM_PLAY].sw = 0;
		goto __skip_pswitch;
	}
	if (s->ctls[CTL_PLAYBACK_SWITCH].elem) {
		err = elem_read_switch(s, SM_PLAY, CTL_PLAYBACK_SWITCH);
		if (err < 0)
			return err;
	}
	if (s->ctls[CTL_GLOBAL_SWITCH].elem) {
		err = elem_read_switch(s, SM_PLAY, CTL_GLOBAL_SWITCH);
		if (err < 0)
			return err;
	}
	if (s->ctls[CTL_SINGLE].elem &&
	    s->ctls[CTL_SINGLE].type == SND_CTL_ELEM_TYPE_BOOLEAN) {
		err = elem_read_switch(s, SM_PLAY, CTL_SINGLE);
		if (err < 0)
			return err;
	}
	if (s->ctls[CTL_PLAYBACK_ROUTE].elem) {
		err = elem_read_route(s, SM_PLAY, CTL_PLAYBACK_ROUTE);
		if (err < 0)
			return err;
	}
	if (s->ctls[CTL_GLOBAL_ROUTE].elem) {
		err = elem_read_route(s, SM_PLAY, CTL_GLOBAL_ROUTE);
		if (err < 0)
			return err;
	}
      __skip_pswitch:

	if (s->ctls[CTL_CAPTURE_VOLUME].elem)
		err = elem_read_volume(s, SM_CAPT, CTL_CAPTURE_VOLUME);
	else if (s->ctls[CTL_GLOBAL_VOLUME].elem)
		err = elem_read_volume(s, SM_CAPT, CTL_GLOBAL_VOLUME);
	else if (s->ctls[CTL_SINGLE].elem &&
		 s->ctls[CTL_SINGLE].type == SND_CTL_ELEM_TYPE_INTEGER)
		err = elem_read_volume(s, SM_CAPT, CTL_SINGLE);
	if (err < 0)
		return err;

	if ((s->selem.caps & (SM_CAP_GSWITCH|SM_CAP_CSWITCH)) == 0) {
		s->str[SM_CAPT].sw = 0;
		goto __skip_cswitch;
	}
	if (s->ctls[CTL_CAPTURE_SWITCH].elem) {
		err = elem_read_switch(s, SM_CAPT, CTL_CAPTURE_SWITCH);
		if (err < 0)
			return err;
	}
	if (s->ctls[CTL_GLOBAL_SWITCH].elem) {
		err = elem_read_switch(s, SM_CAPT, CTL_GLOBAL_SWITCH);
		if (err < 0)
			return err;
	}
	if (s->ctls[CTL_SINGLE].elem &&
	    s->ctls[CTL_SINGLE].type == SND_CTL_ELEM_TYPE_BOOLEAN) {
		err = elem_read_switch(s, SM_CAPT, CTL_SINGLE);
		if (err < 0)
			return err;
	}
	if (s->ctls[CTL_CAPTURE_ROUTE].elem) {
		err = elem_read_route(s, SM_CAPT, CTL_CAPTURE_ROUTE);
		if (err < 0)
			return err;
	}
	if (s->ctls[CTL_GLOBAL_ROUTE].elem) {
		err = elem_read_route(s, SM_CAPT, CTL_GLOBAL_ROUTE);
		if (err < 0)
			return err;
	}
	if (s->ctls[CTL_CAPTURE_SOURCE].elem) {
		snd_ctl_elem_value_t *ctl;
		selem_ctl_t *c = &s->ctls[CTL_CAPTURE_SOURCE];
		snd_ctl_elem_value_alloca(&ctl);
		err = snd_hctl_elem_read(c->elem, ctl);
		if (err < 0)
			return err;
		for (idx = 0; idx < s->str[SM_CAPT].channels; idx++) {
			unsigned int idx1 = idx;
			if (idx >= c->values)
				idx1 = 0;
			if (snd_ctl_elem_value_get_enumerated(ctl, idx1) != s->capture_item)
				s->str[SM_CAPT].sw &= ~(1 << idx);
		}
	}
      __skip_cswitch:

	if (memcmp(pvol, s->str[SM_PLAY].vol, sizeof(pvol)) ||
	    psw != s->str[SM_PLAY].sw ||
	    memcmp(cvol, s->str[SM_CAPT].vol, sizeof(cvol)) ||
	    csw != s->str[SM_CAPT].sw)
		return 1;
	return 0;
}
Beispiel #5
0
int play_sound(char* filename,int rate,int bits,int channel,int order)
{
        long loops;
        int rc,size,dir;
        snd_pcm_t *handle;
        snd_pcm_hw_params_t *params;
        snd_pcm_uframes_t frames,periodsize;
        snd_mixer_t *mixer;
        snd_mixer_elem_t *pcm_element;

        char *buffer;
        unsigned int val;
        FILE *fp = fopen(filename,"rb");
        rc = snd_pcm_open(&handle,"default",SND_PCM_STREAM_PLAYBACK,0);

        snd_pcm_hw_params_alloca(&params);
        snd_pcm_hw_params_any(handle,params);
        snd_pcm_hw_params_set_access(handle,params,SND_PCM_ACCESS_RW_INTERLEAVED);
        switch(order){
                case 1:
                        snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S16_LE);
                        break;
                case 2:
                        snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S16_BE);
                        break;
                defualt:
                        break;
        }
        snd_pcm_hw_params_set_channels(handle,params,channel);

        val = rate;
        snd_pcm_hw_params_set_rate_near(handle,params,&val,0);
        snd_pcm_hw_params_get_buffer_size_max(params,&frames);
        frames = frames < ALSA_MAX_BUF_SIZE? frames:ALSA_MAX_BUF_SIZE;
        rc = snd_pcm_hw_params_set_buffer_size_near(handle,params,&frames);
        snd_pcm_hw_params_get_period_size_min(params,&periodsize,NULL);
        if(!periodsize){
                periodsize=size/4;
        }
        rc = snd_pcm_hw_params_set_period_size_near(handle,params,&periodsize,NULL);
        rc = snd_pcm_hw_params(handle,params);

        snd_mixer_open(&mixer,0);
        snd_mixer_attach(mixer,"default");
        snd_mixer_selem_register(mixer,NULL,NULL);
        snd_mixer_load(mixer);
        for(pcm_element = snd_mixer_first_elem(mixer);pcm_element;pcm_element=snd_mixer_elem_next(pcm_element))
        {
                if(snd_mixer_elem_get_type(pcm_element)==SND_MIXER_ELEM_SIMPLE && snd_mixer_selem_is_active(pcm_element))
                {
                        if(!strcmp(snd_mixer_selem_get_name(pcm_element),"Master"))
                        {
                                snd_mixer_selem_set_playback_volume_range(pcm_element,0,100);
                                snd_mixer_selem_set_playback_volume_all(pcm_element,(long)100);
                        }
                }
        }

        buffer = (char*)malloc(size);
        while(1)
        {
                rc = fread(buffer,1,size,fp);
                if(0== rc)
                        break;
                while((rc = snd_pcm_writei(handle,buffer,size))<0)
                {
                        usleep(200);
                        if(-EPIPE == rc)
                                snd_pcm_prepare(handle);
                        else if(0 > rc)
                                printf("error fomr writei\n");
                }
        }
        snd_pcm_drain(handle);
        snd_pcm_close(handle);
        free(buffer);
        snd_mixer_close(mixer);
        fclose(fp);
        return 0;
}
Beispiel #6
0
static bool
alsa_mixer_open(struct mixer *data, GError **error_r)
{
	struct alsa_mixer *am = (struct alsa_mixer *)data;
	int err;
	snd_mixer_elem_t *elem;

	am->volume_set = -1;

	err = snd_mixer_open(&am->handle, 0);
	if (err < 0) {
		g_set_error(error_r, alsa_mixer_quark(), err,
			    "snd_mixer_open() failed: %s", snd_strerror(err));
		return false;
	}

	if ((err = snd_mixer_attach(am->handle, am->device)) < 0) {
		alsa_mixer_close(data);
		g_set_error(error_r, alsa_mixer_quark(), err,
			    "failed to attach to %s: %s",
			    am->device, snd_strerror(err));
		return false;
	}

	if ((err = snd_mixer_selem_register(am->handle, NULL,
		    NULL)) < 0) {
		alsa_mixer_close(data);
		g_set_error(error_r, alsa_mixer_quark(), err,
			    "snd_mixer_selem_register() failed: %s",
			    snd_strerror(err));
		return false;
	}

	if ((err = snd_mixer_load(am->handle)) < 0) {
		alsa_mixer_close(data);
		g_set_error(error_r, alsa_mixer_quark(), err,
			    "snd_mixer_load() failed: %s\n",
			    snd_strerror(err));
		return false;
	}

	elem = snd_mixer_first_elem(am->handle);

	while (elem) {
		if (snd_mixer_elem_get_type(elem) == SND_MIXER_ELEM_SIMPLE) {
			if ((g_ascii_strcasecmp(am->control,
						snd_mixer_selem_get_name(elem)) == 0) &&
			    (am->index == snd_mixer_selem_get_index(elem))) {
				break;
			}
		}
		elem = snd_mixer_elem_next(elem);
	}

	if (elem) {
		am->elem = elem;
		snd_mixer_selem_get_playback_volume_range(am->elem,
							  &am->volume_min,
							  &am->volume_max);
		return true;
	}

	alsa_mixer_close(data);
	g_set_error(error_r, alsa_mixer_quark(), 0,
		    "no such mixer control: %s", am->control);
	return false;
}