基於Alsa lib進行音量調節以及靜音操做

#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <alsa/asoundlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/soundcard.h>

#include "volume_interface.h"

#define DEBUG(x,y...)	//{printf("[ %s : %s : %d] ",__FILE__, __func__, __LINE__); printf(x,##y); printf("\n");}
#define ERROR(x,y...)	{printf("[ %s : %s : %d] ",__FILE__, __func__, __LINE__); printf(x,##y); printf("\n");}

static char *alsa_mix_dev = "default";
static char *alsa_mix_headphone_ctrl = "Headphone";
static char *alsa_mix_digital_ctrl = "Digital";

static void volume_init(char *alsa_mix_ctrl, snd_mixer_t **alsa_mix_handle_p, snd_mixer_elem_t **alsa_mix_elem_p)
{
	int alsa_mix_index = 0;
	snd_mixer_selem_id_t *alsa_mix_sid = NULL;

	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_p, 0)) < 0)
		DEBUG ("Failed to open mixer");
	if ((snd_mixer_attach(*alsa_mix_handle_p, alsa_mix_dev)) < 0)
		DEBUG ("Failed to attach mixer");
	if ((snd_mixer_selem_register(*alsa_mix_handle_p, NULL, NULL)) < 0)
		DEBUG ("Failed to register mixer element");

	if (snd_mixer_load(*alsa_mix_handle_p) < 0)
		DEBUG ("Failed to load mixer element");
	*alsa_mix_elem_p = snd_mixer_find_selem(*alsa_mix_handle_p, alsa_mix_sid);
	if (!*alsa_mix_elem_p)
		DEBUG ("Failed to find mixer element");
	if (snd_mixer_selem_set_playback_volume_range (*alsa_mix_elem_p, 0, 100) < 0)
		DEBUG("Failed to set playback volume range");
}

static void volume_uninit(snd_mixer_t *alsa_mix_handle)
{
	if(alsa_mix_handle){
		snd_mixer_close(alsa_mix_handle);
	}
}

int volume_get(void)
{
	long ll, lr, vol;
	snd_mixer_t *alsa_mix_headphone_handle = NULL;
	snd_mixer_elem_t *alsa_mix_headphone_elem = NULL;

	//Headphone channel
	volume_init(alsa_mix_headphone_ctrl, &alsa_mix_headphone_handle, &alsa_mix_headphone_elem);
	//處理事件
	snd_mixer_handle_events(alsa_mix_headphone_handle);
	//左聲道
	snd_mixer_selem_get_playback_volume(alsa_mix_headphone_elem, SND_MIXER_SCHN_FRONT_LEFT, &ll);
	//右聲道
	snd_mixer_selem_get_playback_volume(alsa_mix_headphone_elem, SND_MIXER_SCHN_FRONT_RIGHT, &lr);
	volume_uninit(alsa_mix_headphone_handle);

	/*
	//Digital channel
	snd_mixer_t *alsa_mix_digital_handle = NULL;
	snd_mixer_elem_t *alsa_mix_digital_elem = NULL;
	volume_init(alsa_mix_digital_ctrl, &alsa_mix_digital_handle, &alsa_mix_digital_elem);
	//處理事件
	snd_mixer_handle_events(alsa_mix_digital_handle);
	//左聲道
	snd_mixer_selem_get_playback_volume(alsa_mix_digital_elem, SND_MIXER_SCHN_FRONT_LEFT, &ll);
	//右聲道
	snd_mixer_selem_get_playback_volume(alsa_mix_digital_elem, SND_MIXER_SCHN_FRONT_RIGHT, &lr);
	volume_uninit(alsa_mix_digital_handle);
	*/

	vol = (int)((ll + lr) >> 1);
	//DEBUG("volume_get vol %d, ll %ld, lr %ld", vol, ll, lr);
	return vol;
}

int volume_set(int vol, volume_domain domain)
{
	snd_mixer_t *alsa_mix_headphone_handle = NULL;
	snd_mixer_t *alsa_mix_digital_handle = NULL;
	snd_mixer_elem_t *alsa_mix_headphone_elem = NULL;
	snd_mixer_elem_t *alsa_mix_digital_elem = NULL;

	if(vol > 100)
		vol = 100;
	if(vol < 0)
		vol = 0;

	if(vol == 0){
		volume_mute();
	}else{
		volume_unmute();
	}

	//Headphone channel
	volume_init(alsa_mix_headphone_ctrl, &alsa_mix_headphone_handle, &alsa_mix_headphone_elem);
	//左音量
	snd_mixer_selem_set_playback_volume(alsa_mix_headphone_elem, SND_MIXER_SCHN_FRONT_LEFT, vol);
	//右音量
	snd_mixer_selem_set_playback_volume(alsa_mix_headphone_elem, SND_MIXER_SCHN_FRONT_RIGHT, vol);
	volume_uninit(alsa_mix_headphone_handle);

	//Digital channel
	//Digital channel 修改量減半,防止音量太大失真
	volume_init(alsa_mix_digital_ctrl, &alsa_mix_digital_handle, &alsa_mix_digital_elem);
	//左音量
	snd_mixer_selem_set_playback_volume(alsa_mix_digital_elem, SND_MIXER_SCHN_FRONT_LEFT, vol / 2);
	//右音量
	snd_mixer_selem_set_playback_volume(alsa_mix_digital_elem, SND_MIXER_SCHN_FRONT_RIGHT, vol / 2);
	volume_uninit(alsa_mix_digital_handle);
	return 0;
}

int set_volume_mute_value(int val)
{
	int err = 0;
	char *card = alsa_mix_dev;
	snd_ctl_t *handle = NULL;
	snd_ctl_elem_value_t *ctl_elem_value;

	if (handle == NULL && (err = snd_ctl_open(&handle, card, 0)) < 0) {
		ERROR("Control %s open error: %s", card, snd_strerror(err));
		return err;
	}

	if((err = snd_ctl_elem_value_malloc (&ctl_elem_value)) < 0){
		ERROR("Control %s open error: %s", card, snd_strerror(err));
		snd_ctl_close(handle);
		handle = NULL;
		return err;
	}

	snd_ctl_elem_value_set_numid (ctl_elem_value, 11);
	snd_ctl_elem_value_set_interface (ctl_elem_value, SND_CTL_ELEM_IFACE_MIXER);
	snd_ctl_elem_value_set_name (ctl_elem_value, "Digital Playback mute");
	snd_ctl_elem_value_set_integer(ctl_elem_value, 0, val);

	if ((err = snd_ctl_elem_write(handle, ctl_elem_value)) < 0) {
		ERROR("Control %s element write error: %s", card, snd_strerror(err));
	}

	snd_ctl_close(handle);
	handle = NULL;
	snd_ctl_elem_value_free (ctl_elem_value);
	return err;
}

int volume_mute()
{
	return set_volume_mute_value(1);
}

int volume_unmute()
{
	return set_volume_mute_value(0);
}