/*
 *******************************************************************************
 *  Copyright (c) 2010-2022 VATICS(KNERON) Inc. All rights reserved.
 *
 *  +-----------------------------------------------------------------+
 *  | THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY ONLY BE USED |
 *  | AND COPIED IN ACCORDANCE WITH THE TERMS AND CONDITIONS OF SUCH  |
 *  | A LICENSE AND WITH THE INCLUSION OF THE THIS COPY RIGHT NOTICE. |
 *  | THIS SOFTWARE OR ANY OTHER COPIES OF THIS SOFTWARE MAY NOT BE   |
 *  | PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY OTHER PERSON. THE   |
 *  | OWNERSHIP AND TITLE OF THIS SOFTWARE IS NOT TRANSFERRED.        |
 *  |                                                                 |
 *  | THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT   |
 *  | ANY PRIOR NOTICE AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY |
 *  | VATICS(KNERON) INC.                                             |
 *  +-----------------------------------------------------------------+
 *
 *******************************************************************************
 */
#include <audio_vol_ctrl.h>
#include <string.h>
#include <math.h>

//#define VTCS_AUDIO_CTRL

typedef struct
{
	int (*has_volume)(snd_mixer_elem_t *elem);
	int (*get_volume_range)(snd_mixer_elem_t *elem, long *min, long *max);
	int (*set_volume_all)(snd_mixer_elem_t *elem, long value);
	int (*set_volume)(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, long value);
	int (*get_volume)(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, long *value);

	int (*get_dB_range)(snd_mixer_elem_t *elem, long *min, long *max);
	int (*set_dB_all)(snd_mixer_elem_t *elem, long value, int dir);
	int (*set_dB)(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, long value, int dir);
	int (*get_dB)(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, long *value);

	int (*has_switch)(snd_mixer_elem_t *elem);
	int (*set_switch_all)(snd_mixer_elem_t *elem, int value);
	int (*set_switch)(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, int value);
	int (*get_switch)(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, int *value);
} mixer_ops_t;

static mixer_ops_t mixer_ops_funcs[2] = {
	{
		.has_volume = snd_mixer_selem_has_playback_volume,
		.get_volume_range = snd_mixer_selem_get_playback_volume_range,
		.set_volume_all = snd_mixer_selem_set_playback_volume_all,
		.set_volume = snd_mixer_selem_set_playback_volume,
		.get_volume = snd_mixer_selem_get_playback_volume,
		.get_dB_range = snd_mixer_selem_get_playback_dB_range,
		.set_dB_all = snd_mixer_selem_set_playback_dB_all,
		.set_dB = snd_mixer_selem_set_playback_dB,
		.get_dB = snd_mixer_selem_get_playback_dB,
		.has_switch = snd_mixer_selem_has_playback_switch,
		.set_switch_all = snd_mixer_selem_set_playback_switch_all,
		.set_switch = snd_mixer_selem_set_playback_switch,
		.get_switch = snd_mixer_selem_get_playback_switch
	},
	{
		.has_volume = snd_mixer_selem_has_capture_volume,
		.get_volume_range = snd_mixer_selem_get_capture_volume_range,
		.set_volume_all = snd_mixer_selem_set_capture_volume_all,
		.set_volume = snd_mixer_selem_set_capture_volume,
		.get_volume = snd_mixer_selem_get_capture_volume,
		.get_dB_range = snd_mixer_selem_get_capture_dB_range,
		.set_dB_all = snd_mixer_selem_set_capture_dB_all,
		.set_dB = snd_mixer_selem_set_capture_dB,
		.get_dB = snd_mixer_selem_get_capture_dB,
		.has_switch = snd_mixer_selem_has_capture_switch,
		.set_switch_all = snd_mixer_selem_set_capture_switch_all,
		.set_switch = snd_mixer_selem_set_capture_switch,
		.get_switch = snd_mixer_selem_get_capture_switch
	}
};

/**
 * @brief Function to help to find a element.
 *
 * @param[in] handle The handle for ALSA mixer.
 * @param[in] config The configuration for mixer.
 * @return NULL: Failed, otherwise: element.
 */
static snd_mixer_elem_t* mixer_find_elem_helper(snd_mixer_t *handle, const ATK_AUDIO_SCTRL_CONFIG_T *config)
{
	if((handle == NULL) || (config == NULL))
	{
		return NULL;
	}

	snd_mixer_selem_id_t *sid = NULL;
	const char *card = (config->szCard) ? (config->szCard) : "default";
	int err = 0;

	// Attach an HCTL specified with the CTL device name to an opened mixer.
	if((err = snd_mixer_attach(handle, card)) < 0)
	{
		fprintf(stderr, "[%s, %s]: Can't attach control. %s\n", __FILE__, __func__, snd_strerror(err));
		return NULL;
	}
	// Register mixer simple element class.
	if((err = snd_mixer_selem_register(handle, NULL, NULL)) < 0)
	{
		fprintf(stderr, "[%s, %s]: Can't register mixer. %s\n", __FILE__, __func__, snd_strerror(err));
		return NULL;
	}
	// Load a mixer elements.
	if((err = snd_mixer_load(handle)) < 0)
	{
		fprintf(stderr, "[%s, %s]: Can't load mixer elements. %s\n", __FILE__, __func__, snd_strerror(err));
		return NULL;
	}

	snd_mixer_selem_id_alloca(&sid);
	// Set index part of a mixer simple element identifier.
	snd_mixer_selem_id_set_index(sid, config->dwSelemIndex);
	// Set name part of a mixer simple element identifier.
	snd_mixer_selem_id_set_name(sid, ((config->szSelemName) ? (config->szSelemName) : "Master"));

	// Find a mixer simple element.
	return snd_mixer_find_selem(handle, sid);
}

/**
 * @brief Function to convert the volume from one range to another range.
 *
 * @param[in] from_vol The volume need to be converted.
 * @param[in] from_vol_max The max volume of original range.
 * @param[in] from_vol_min The min volume of original range.
 * @param[in] to_vol_max The max volume of new range.
 * @param[in] to_vol_min The min volume of new range.
 * @return The new volume in the new range ('to' range).
 */
static long convert_range_vol(long from_vol, long from_vol_max, long from_vol_min, long to_vol_max, long to_vol_min)
{
	if((from_vol >= from_vol_max) || (from_vol_max == from_vol_min))
	{
		return to_vol_max - 1;
	}

	if(from_vol <= from_vol_min)
	{
		return 0;
	}

	return (((float)((from_vol - from_vol_min)*(to_vol_max - to_vol_min - 2 )) / (float)(from_vol_max - from_vol_min) + to_vol_min) + 1);

}

int ATK_Audio_SCtrl_SetVolume(const ATK_AUDIO_SCTRL_CONFIG_T *ptConfig, long lVol)
{
	int err = 0;
	snd_mixer_t *handle = NULL;
	snd_mixer_elem_t *elem = NULL;
	long vol_min = 0;
	long vol_max = 0;

	if((ptConfig == NULL) || (lVol < 0) || (lVol > 100))
	{
		return -1;
	}

	if((err = snd_mixer_open(&handle, 0)) < 0)
	{
		fprintf(stderr, "[%s, %s]: Can't open mixer. %s\n", __FILE__, __func__, snd_strerror(err));
		return -1;
	}

	elem = mixer_find_elem_helper(handle, ptConfig);
	if(elem == NULL)
	{
		snd_mixer_close(handle);
		return -1;
	}

	int stream_idx = ptConfig->eStream;

	if(mixer_ops_funcs[stream_idx].has_volume(elem) == 1)
	{
		mixer_ops_funcs[stream_idx].get_volume_range(elem, &vol_min, &vol_max);
		if((err = mixer_ops_funcs[stream_idx].set_volume_all(elem, convert_range_vol(lVol, 100, 0, vol_max, vol_min))) < 0)
		{
			fprintf(stderr, "[%s, %s]: Can't set volume. %s\n", __FILE__, __func__, snd_strerror(err));
			snd_mixer_close(handle);
			return -1;
		}
	}
	else
	{
		fprintf(stderr, "[%s, %s]: Warning! No volumn control.\n", __FILE__, __func__);
		snd_mixer_close(handle);
		return -2;
	}

	if((err = snd_mixer_close(handle)) < 0)
	{
		fprintf(stderr, "[%s, %s]: Can't close mixer. %s\n", __FILE__, __func__, snd_strerror(err));
	}

	return 0;
}

int ATK_Audio_SCtrl_SetChannelVolume(const ATK_AUDIO_SCTRL_CONFIG_T *ptConfig, snd_mixer_selem_channel_id_t eChannelId, long lVol)
{
	int err = 0;
	snd_mixer_t *handle = NULL;
	snd_mixer_elem_t *elem = NULL;
	long vol_min = 0;
	long vol_max = 0;

	if((ptConfig == NULL) || (lVol < 0) || (lVol > 100))
	{
		return -1;
	}

	if((err = snd_mixer_open(&handle, 0)) < 0)
	{
		fprintf(stderr, "[%s, %s]: Can't open mixer. %s\n", __FILE__, __func__, snd_strerror(err));
		return -1;
	}

	elem = mixer_find_elem_helper(handle, ptConfig);
	if(elem == NULL)
	{
		snd_mixer_close(handle);
		return -1;
	}

	int stream_idx = ptConfig->eStream;

	if(mixer_ops_funcs[stream_idx].has_volume(elem) == 1)
	{
		mixer_ops_funcs[stream_idx].get_volume_range(elem, &vol_min, &vol_max);
		if((err = mixer_ops_funcs[stream_idx].set_volume(elem, eChannelId, convert_range_vol(lVol, 100, 0, vol_max, vol_min))) < 0)
		{
			fprintf(stderr, "[%s, %s]: Can't set volume. %s\n", __FILE__, __func__, snd_strerror(err));
			snd_mixer_close(handle);
			return -1;
		}
	}
	else
	{
		fprintf(stderr, "[%s, %s]: Warning! No volumn control.\n", __FILE__, __func__);
		snd_mixer_close(handle);
		return -2;
	}

	if((err = snd_mixer_close(handle)) < 0)
	{
		fprintf(stderr, "[%s, %s]: Can't close mixer. %s\n", __FILE__, __func__, snd_strerror(err));
	}

	return 0;
}

int ATK_Audio_SCtrl_GetChannelVolume(const ATK_AUDIO_SCTRL_CONFIG_T *ptConfig, snd_mixer_selem_channel_id_t eChannelId, long *plVol)
{
	int err = 0;
	snd_mixer_t *handle = NULL;
	snd_mixer_elem_t *elem = NULL;
	long vol_min = 0;
	long vol_max = 0;
	long hw_vol = 0;

	if((ptConfig == NULL) || (plVol == NULL))
	{
		return -1;
	}

	if((err = snd_mixer_open(&handle, 0)) < 0)
	{
		fprintf(stderr, "[%s, %s]: Can't open mixer. %s\n", __FILE__, __func__, snd_strerror(err));
		return -1;
	}

	elem = mixer_find_elem_helper(handle, ptConfig);
	if(elem == NULL)
	{
		snd_mixer_close(handle);
		return -1;
	}

	int stream_idx = ptConfig->eStream;

	if(mixer_ops_funcs[stream_idx].has_volume(elem) == 1)
	{
		mixer_ops_funcs[stream_idx].get_volume_range(elem, &vol_min, &vol_max);
		if((err = mixer_ops_funcs[stream_idx].get_volume(elem, eChannelId, &hw_vol)) < 0)
		{
			fprintf(stderr, "[%s, %s]: Can't get volume.\n", __FILE__, __func__);
			snd_mixer_close(handle);
			return -1;
		}
		*plVol = convert_range_vol(hw_vol, vol_max, vol_min, 100, 0);
	}
	else
	{
		fprintf(stderr, "[%s, %s]: Warning! No volumn control.\n", __FILE__, __func__);
		snd_mixer_close(handle);
		return -2;
	}

	if((err = snd_mixer_close(handle)) < 0)
	{
		fprintf(stderr, "[%s, %s]: Can't close mixer. %s\n", __FILE__, __func__, snd_strerror(err));
	}

	return 0;
}

int ATK_Audio_SCtrl_SetVolume_dB(const ATK_AUDIO_SCTRL_CONFIG_T *ptConfig, long lVol)
{
	int err = 0;
	snd_mixer_t *handle = NULL;
	snd_mixer_elem_t *elem = NULL;

	if(ptConfig == NULL)
	{
		return -1;
	}

	if((err = snd_mixer_open(&handle, 0)) < 0)
	{
		fprintf(stderr, "[%s, %s]: Can't open mixer. %s\n", __FILE__, __func__, snd_strerror(err));
		return -1;
	}

	elem = mixer_find_elem_helper(handle, ptConfig);
	if(elem == NULL)
	{
		snd_mixer_close(handle);
		return -1;
	}

	int stream_idx = ptConfig->eStream;

	if(mixer_ops_funcs[stream_idx].has_volume(elem) == 1)
	{
		if((err = mixer_ops_funcs[stream_idx].set_dB_all(elem, lVol*100, 0)) < 0)
		{
			fprintf(stderr, "[%s, %s]: Can't set volume in dB. %s\n", __FILE__, __func__, snd_strerror(err));
			snd_mixer_close(handle);
			return -1;
		}
	}
	else
	{
		fprintf(stderr, "[%s, %s]: Warning! No volumn control.\n", __FILE__, __func__);
		snd_mixer_close(handle);
		return -2;
	}

	if((err = snd_mixer_close(handle)) < 0)
	{
		fprintf(stderr, "[%s, %s]: Can't close mixer. %s\n", __FILE__, __func__, snd_strerror(err));
	}

	return 0;
}

int ATK_Audio_SCtrl_SetChannelVolume_dB(const ATK_AUDIO_SCTRL_CONFIG_T *ptConfig, snd_mixer_selem_channel_id_t eChannelId, long lVol)
{
	int err = 0;
	snd_mixer_t *handle = NULL;
	snd_mixer_elem_t *elem = NULL;

	if(ptConfig == NULL)
	{
		return -1;
	}

	if((err = snd_mixer_open(&handle, 0)) < 0)
	{
		fprintf(stderr, "[%s, %s]: Can't open mixer. %s\n", __FILE__, __func__, snd_strerror(err));
		return -1;
	}

	elem = mixer_find_elem_helper(handle, ptConfig);
	if(elem == NULL)
	{
		snd_mixer_close(handle);
		return -1;
	}

	int stream_idx = ptConfig->eStream;

	if(mixer_ops_funcs[stream_idx].has_volume(elem) == 1)
	{
		if((err = mixer_ops_funcs[stream_idx].set_dB(elem, eChannelId, lVol*100, 0)) < 0)
		{
			fprintf(stderr, "[%s, %s]: Can't set volume. %s\n", __FILE__, __func__, snd_strerror(err));
			snd_mixer_close(handle);
			return -1;
		}
	}
	else
	{
		fprintf(stderr, "[%s, %s]: Warning! No volumn control.\n", __FILE__, __func__);
		snd_mixer_close(handle);
		return -2;
	}

	if((err = snd_mixer_close(handle)) < 0)
	{
		fprintf(stderr, "[%s, %s]: Can't close mixer. %s\n", __FILE__, __func__, snd_strerror(err));
	}

	return 0;
}

int ATK_Audio_SCtrl_GetChannelVolume_dB(const ATK_AUDIO_SCTRL_CONFIG_T *ptConfig, snd_mixer_selem_channel_id_t eChannelId, long *plVol)
{
	int err = 0;
	snd_mixer_t *handle = NULL;
	snd_mixer_elem_t *elem = NULL;

	if((ptConfig == NULL) || (plVol == NULL))
	{
		return -1;
	}

	if((err = snd_mixer_open(&handle, 0)) < 0)
	{
		fprintf(stderr, "[%s, %s]: Can't open mixer. %s\n", __FILE__, __func__, snd_strerror(err));
		return -1;
	}

	elem = mixer_find_elem_helper(handle, ptConfig);
	if(elem == NULL)
	{
		snd_mixer_close(handle);
		return -1;
	}

	int stream_idx = ptConfig->eStream;

	if(mixer_ops_funcs[stream_idx].has_volume(elem) == 1)
	{
		if((err = mixer_ops_funcs[stream_idx].get_dB(elem, eChannelId, plVol)) < 0)
		{
			fprintf(stderr, "[%s, %s]: Can't get volume.\n", __FILE__, __func__);
			snd_mixer_close(handle);
			return -1;
		}

		*plVol /= 100;
	}
	else
	{
		fprintf(stderr, "[%s, %s]: Warning! No volumn control.\n", __FILE__, __func__);
		snd_mixer_close(handle);
		return -2;
	}

	if((err = snd_mixer_close(handle)) < 0)
	{
		fprintf(stderr, "[%s, %s]: Can't close mixer. %s\n", __FILE__, __func__, snd_strerror(err));
	}

	return 0;
}

int ATK_Audio_SCtrl_GetVolume_Range_dB(const ATK_AUDIO_SCTRL_CONFIG_T *ptConfig, long *plMin, long *plMax)
{
	int err = 0;
	snd_mixer_t *handle = NULL;
	snd_mixer_elem_t *elem = NULL;

	if((ptConfig == NULL) || (plMin == NULL) || (plMax == NULL))
	{
		return -1;
	}

	if((err = snd_mixer_open(&handle, 0)) < 0)
	{
		fprintf(stderr, "[%s, %s]: Can't open mixer. %s\n", __FILE__, __func__, snd_strerror(err));
		return -1;
	}

	elem = mixer_find_elem_helper(handle, ptConfig);
	if(elem == NULL)
	{
		snd_mixer_close(handle);
		return -1;
	}

	int stream_idx = ptConfig->eStream;

	if(mixer_ops_funcs[stream_idx].has_volume(elem) == 1)
	{
		if((err = mixer_ops_funcs[stream_idx].get_dB_range(elem, plMin, plMax)) < 0)
		{
			fprintf(stderr, "[%s, %s]: Can't get volume.\n", __FILE__, __func__);
			snd_mixer_close(handle);
			return -1;
		}

		*plMin /= 100;
		*plMax /= 100;
	}
	else
	{
		fprintf(stderr, "[%s, %s]: Warning! No volumn control.\n", __FILE__, __func__);
		snd_mixer_close(handle);
		return -2;
	}

	if((err = snd_mixer_close(handle)) < 0)
	{
		fprintf(stderr, "[%s, %s]: Can't close mixer. %s\n", __FILE__, __func__, snd_strerror(err));
	}

	return 0;
}

int ATK_Audio_SCtrl_SetSwitch(const ATK_AUDIO_SCTRL_CONFIG_T *ptConfig, int bIsOn)
{
	int err = 0;
	snd_mixer_t *handle = NULL;
	snd_mixer_elem_t *elem = NULL;

	if(ptConfig == NULL)
	{
		return -1;
	}

	if((err = snd_mixer_open(&handle, 0)) < 0)
	{
		fprintf(stderr, "[%s, %s]: Can't open mixer. %s\n", __FILE__, __func__, snd_strerror(err));
		return -1;
	}

	elem = mixer_find_elem_helper(handle, ptConfig);
	if(elem == NULL)
	{
		snd_mixer_close(handle);
		return -1;
	}

	int stream_idx = ptConfig->eStream;

	if(mixer_ops_funcs[stream_idx].has_switch(elem) == 1)
	{
		if((err = mixer_ops_funcs[stream_idx].set_switch_all(elem, bIsOn)) < 0)
		{
			fprintf(stderr, "[%s, %s]: Can't turn on or off. %s\n", __FILE__, __func__, snd_strerror(err));
			snd_mixer_close(handle);
			return -1;
		}
	}
	else
	{
		fprintf(stderr, "[%s, %s]: Warning! No volumn control.\n", __FILE__, __func__);
		snd_mixer_close(handle);
		return -2;
	}

	if((err = snd_mixer_close(handle)) < 0)
	{
		fprintf(stderr, "[%s, %s]: Can't close mixer. %s\n", __FILE__, __func__, snd_strerror(err));
	}

	return 0;
}

int ATK_Audio_SCtrl_SetChannelSwitch(const ATK_AUDIO_SCTRL_CONFIG_T *ptConfig, snd_mixer_selem_channel_id_t eChannelId, int bIsOn)
{
	int err = 0;
	snd_mixer_t *handle = NULL;
	snd_mixer_elem_t *elem = NULL;

	if(ptConfig == NULL)
	{
		return -1;
	}

	if((err = snd_mixer_open(&handle, 0)) < 0)
	{
		fprintf(stderr, "[%s, %s]: Can't open mixer. %s\n", __FILE__, __func__, snd_strerror(err));
		return -1;
	}

	elem = mixer_find_elem_helper(handle, ptConfig);
	if(elem == NULL)
	{
		snd_mixer_close(handle);
		return -1;
	}

	int stream_idx = ptConfig->eStream;

	if(mixer_ops_funcs[stream_idx].has_switch(elem) == 1)
	{
		if((err = mixer_ops_funcs[stream_idx].set_switch(elem, eChannelId, bIsOn)) < 0)
		{
			fprintf(stderr, "[%s, %s]: Can't mute. %s\n", __FILE__, __func__, snd_strerror(err));
			snd_mixer_close(handle);
			return -1;
		}
	}
	else
	{
		fprintf(stderr, "[%s, %s]: Warning! No volumn control.\n", __FILE__, __func__);
		snd_mixer_close(handle);
		return -2;
	}

	if((err = snd_mixer_close(handle)) < 0)
	{
		fprintf(stderr, "[%s, %s]: Can't close mixer. %s\n", __FILE__, __func__, snd_strerror(err));
	}

	return 0;
}

int ATK_Audio_SCtrl_GetChannelSwitch(const ATK_AUDIO_SCTRL_CONFIG_T *ptConfig, snd_mixer_selem_channel_id_t eChannelId, int *pbIsOn)
{
	int err = 0;
	snd_mixer_t *handle = NULL;
	snd_mixer_elem_t *elem = NULL;

	if((ptConfig == NULL) || (pbIsOn == NULL))
	{
		return -1;
	}

	if((err = snd_mixer_open(&handle, 0)) < 0)
	{
		fprintf(stderr, "[%s, %s]: Can't open mixer. %s\n", __FILE__, __func__, snd_strerror(err));
		return -1;
	}

	elem = mixer_find_elem_helper(handle, ptConfig);
	if(elem == NULL)
	{
		snd_mixer_close(handle);
		return -1;
	}

	int stream_idx = ptConfig->eStream;

	if(mixer_ops_funcs[stream_idx].has_switch(elem) == 1)
	{
		if((err = mixer_ops_funcs[stream_idx].get_switch(elem, eChannelId, pbIsOn)) < 0)
		{
			fprintf(stderr, "[%s, %s]: Can't mute. %s\n", __FILE__, __func__, snd_strerror(err));
			snd_mixer_close(handle);
			return -1;
		}
	}
	else
	{
		fprintf(stderr, "[%s, %s]: Warning! No volumn control.\n", __FILE__, __func__);
		snd_mixer_close(handle);
		return -2;
	}

	if((err = snd_mixer_close(handle)) < 0)
	{
		fprintf(stderr, "[%s, %s]: Can't close mixer. %s\n", __FILE__, __func__, snd_strerror(err));
	}

	return 0;
}

int ATK_Audio_SCtrl_Set_Enumerated(const ATK_AUDIO_SCTRL_CONFIG_T *ptConfig, snd_mixer_selem_channel_id_t eChannelId, const char *szEnumStr)
{
	int err = 0;
	snd_mixer_t *handle = NULL;
	snd_mixer_elem_t *elem = NULL;

	if((ptConfig == NULL) || (szEnumStr == NULL))
	{
		return -1;
	}

	if((err = snd_mixer_open(&handle, 0)) < 0)
	{
		fprintf(stderr, "[%s, %s]: Can't open mixer. %s\n", __FILE__, __func__, snd_strerror(err));
		return -1;
	}

	elem = mixer_find_elem_helper(handle, ptConfig);
	if(elem == NULL)
	{
		snd_mixer_close(handle);
		return -1;
	}

	if(snd_mixer_selem_is_enumerated(elem))
	{
		int items = snd_mixer_selem_get_enum_items(elem);
		if(items < 0)
		{
			snd_mixer_close(handle);
			return -1;
		}

		char name[128] = {'\0'};
		for(int i = 0; i < items; ++i)
		{
			if(snd_mixer_selem_get_enum_item_name(elem, i, sizeof(name)-1, name) < 0)
			{
				continue;
			}
			if(strcmp(name, szEnumStr) == 0)
			{
				if((err = snd_mixer_selem_set_enum_item(elem, eChannelId, i)) < 0)
				{
					fprintf(stderr, "[%s, %s]: Can't set enumerated value. %s\n", __FILE__, __func__, snd_strerror(err));
					snd_mixer_close(handle);
					return -1;
				}

				snd_mixer_close(handle);
				return 0;
			}
		}
	}

	snd_mixer_close(handle);
	return -1;
}

int ATK_Audio_Ctrl_Set_Int_Or_Bool(const ATK_AUDIO_CTRL_CONFIG_T *ptConfig, unsigned int dwIdx, long lVal)
{
	int err = 0;
	snd_hctl_t *hctl = NULL;
	snd_ctl_elem_id_t *id = NULL;
	snd_hctl_elem_t *elem = NULL;
	snd_ctl_elem_value_t *control = NULL;
	snd_ctl_elem_info_t *info = NULL;
	snd_ctl_elem_type_t type;

	if(ptConfig == NULL)
	{
		return -1;
	}

	if((err = snd_hctl_open(&hctl, (ptConfig->szCard) ? (ptConfig->szCard) : "hw:0", 0)) < 0)
	{
		fprintf(stderr, "[%s, %s]: Can't open hight level control. %s\n", __FILE__, __func__, snd_strerror(err));
		return -1;
	}
	if((err = snd_hctl_load(hctl)) < 0)
	{
		fprintf(stderr, "[%s, %s]: Can't load all elements for hight level control. %s\n", __FILE__, __func__, snd_strerror(err));
		snd_hctl_close(hctl);
		return -1;
	}

	snd_ctl_elem_id_alloca(&id);
	snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER);
	snd_ctl_elem_id_set_name(id, ptConfig->szElemName);

	elem = snd_hctl_find_elem(hctl, id);
	if(elem == NULL)
	{
		fprintf(stderr, "[%s, %s]: Can't find element %s.\n", __FILE__, __func__, ptConfig->szElemName);
		snd_hctl_close(hctl);
		return -1;
	}

	snd_ctl_elem_info_alloca(&info);
	if((err = snd_hctl_elem_info(elem, info)) < 0)
	{
		fprintf(stderr, "[%s, %s]: snd_hctl_elem_info error: %s.\n", __FILE__, __func__, snd_strerror(err));
		snd_hctl_close(hctl);
		return -1;
	}
	type = snd_ctl_elem_info_get_type(info);

	snd_ctl_elem_value_alloca(&control);
	snd_ctl_elem_value_set_id(control, id);

	switch(type)
	{
		case SND_CTL_ELEM_TYPE_BOOLEAN:
			snd_ctl_elem_value_set_boolean(control, dwIdx, lVal);
			break;
		case SND_CTL_ELEM_TYPE_INTEGER:
			snd_ctl_elem_value_set_integer(control, dwIdx, lVal);
			break;
		default:
			// We don't use SND_CTL_ELEM_TYPE_INTEGER64
			fprintf(stderr, "[%s, %s]: Unsupport element type.\n", __FILE__, __func__);
			snd_hctl_close(hctl);
			return -1;
	}

	if((err = snd_hctl_elem_write(elem, control)) < 0)
	{
		fprintf(stderr, "[%s, %s]: Can't write the value to element. %s\n", __FILE__, __func__, snd_strerror(err));
		snd_hctl_close(hctl);
		return -1;
	}

	if((err = snd_hctl_close(hctl)) < 0)
	{
		fprintf(stderr, "[%s, %s]: Can't close hight level control. %s\n", __FILE__, __func__, snd_strerror(err));
	}

	return 0;
}

static int atk_audio_set_capture_volume(long vol, int mode)
{
	ATK_AUDIO_SCTRL_CONFIG_T audio_config;
	memset(&audio_config, 0, sizeof(ATK_AUDIO_SCTRL_CONFIG_T));

	typedef int (*set_vol_func_ptr)(const ATK_AUDIO_SCTRL_CONFIG_T *config, long vol);
	set_vol_func_ptr set_func = (mode) ? ATK_Audio_SCtrl_SetVolume_dB : ATK_Audio_SCtrl_SetVolume;

#ifdef VTCS_AUDIO_CTRL
	int ret = 0;
	int ret2 = 0;

	audio_config.eStream = SND_PCM_STREAM_CAPTURE;
	audio_config.szSelemName = "Capture";
	ret = set_func(&audio_config, vol);

	audio_config.eStream = SND_PCM_STREAM_CAPTURE;
	audio_config.szSelemName = "Input";
	ret2 = set_func(&audio_config, vol);

	return ((ret == 0) || (ret2 == 0)) ? 0 : -1;
#else
	audio_config.eStream = SND_PCM_STREAM_CAPTURE;
	audio_config.szSelemName = "Capture";
	return set_func(&audio_config, vol);
#endif
}

int ATK_Audio_SetCaptureVolume(long lVol)
{
	return atk_audio_set_capture_volume(lVol, 0);
}

int ATK_Audio_SetCaptureVolume_dB(long lVol)
{
	return atk_audio_set_capture_volume(lVol, 1);
}

/*
 * PGA vol  control
 * */
static int atk_audio_set_capture_pga(long vol, int mode)
{
	ATK_AUDIO_SCTRL_CONFIG_T audio_config;
	memset(&audio_config, 0, sizeof(ATK_AUDIO_SCTRL_CONFIG_T));

	typedef int (*set_vol_func_ptr)(const ATK_AUDIO_SCTRL_CONFIG_T *config, long vol);
	set_vol_func_ptr set_func = (mode) ? ATK_Audio_SCtrl_SetVolume_dB : ATK_Audio_SCtrl_SetVolume;

	audio_config.eStream = SND_PCM_STREAM_CAPTURE;
	audio_config.szSelemName = "PGA";

	return set_func(&audio_config, vol);
}

int ATK_Audio_SetCapturePga(long lPga)
{
	return atk_audio_set_capture_pga(lPga, 0);
}

int ATK_Audio_SetCapturePga_dB(long lPga)
{
	return atk_audio_set_capture_pga(lPga, 1);
}
/*
 * PGA ends
 * */
int ATK_Audio_SetCaptureMute(int bIsMute, TK_AUDIO_INPUT_TYPE eType)
{
#ifdef VTCS_AUDIO_CTRL
	int err = 0;
	int err2 = 0;
	int err3 = 0;
	ATK_AUDIO_CTRL_CONFIG_T audio_config;
	memset(&audio_config, 0, sizeof(ATK_AUDIO_CTRL_CONFIG_T));

	audio_config.szElemName = "Mic Mute";
	err = ATK_Audio_Ctrl_Set_Int_Or_Bool(&audio_config, 0, bIsMute);

	err2 = ATK_Audio_SetCaptureVolume((bIsMute) ? 0 : 100);

	audio_config.szElemName = "Left ADC mute";
	err3 = ATK_Audio_Ctrl_Set_Int_Or_Bool(&audio_config, 0, bIsMute);

	return ((err == 0) || (err2 == 0) || (err3 == 0)) ? 0 : -1;
#else
	int err = -1;
	int err2 = 0;

	ATK_AUDIO_SCTRL_CONFIG_T audio_config;
	memset(&audio_config, 0, sizeof(ATK_AUDIO_SCTRL_CONFIG_T));
	audio_config.eStream = SND_PCM_STREAM_CAPTURE;

	//audio_config.szSelemName = "Capture";
	//err = ATK_Audio_SCtrl_SetSwitch(&audio_config, !bIsMute);

	switch(eType)
	{
		case kTKAudioMicIn:
			audio_config.szSelemName = "Mic";
			err2 = ATK_Audio_SCtrl_SetSwitch(&audio_config, !bIsMute);
			break;
		case kTKAudioLineIn:
			audio_config.szSelemName = "Line";
			err2 = ATK_Audio_SCtrl_SetSwitch(&audio_config, !bIsMute);
			break;
		case kTKAudioByPass:
			break;
		default:
			err2 = -1;
			break;
	}

	return ((err == 0) || (err2 == 0)) ? 0 : -1;
#endif
}

int ATK_Audio_InputSelection(TK_AUDIO_INPUT_TYPE eType)
{
#if 1 //def VTCS_AUDIO_CTRL
	ATK_AUDIO_CTRL_CONFIG_T audio_config;
	memset(&audio_config, 0, sizeof(ATK_AUDIO_CTRL_CONFIG_T));

	if(eType != kTKAudioByPass)
	{
		//audio drivers use different "control anme".

		audio_config.szElemName = "Input Select"; //Pesaro built-in audio codec.
		// Line in: 0, Mic in (single ended): 2, Mic in (differential): 4
		if (ATK_Audio_Ctrl_Set_Int_Or_Bool(&audio_config, 0, ((eType == kTKAudioMicIn) ? 2 : 0)) == 0)
			return 0;

		//other audio codec for Mozart 3s
		audio_config.szElemName = "Input Type";
		if (ATK_Audio_Ctrl_Set_Int_Or_Bool(&audio_config, 0, ((eType == kTKAudioMicIn) ? 0 : 1)) == 0)
			return 0;

		audio_config.szElemName = "Input Selection";
		if(ATK_Audio_Ctrl_Set_Int_Or_Bool(&audio_config, 0, ((eType == kTKAudioMicIn) ? 2 : 1)) == 0)
			return 0;

		return -1;
	}

	audio_config.szElemName = "Input Selection";
	return ATK_Audio_Ctrl_Set_Int_Or_Bool(&audio_config, 0, 0);
#else
	int err = 0, err2 = 0, err3 = 0, err4 = 0;
	ATK_AUDIO_SCTRL_CONFIG_T audio_config;
	memset(&audio_config, 0, sizeof(ATK_AUDIO_SCTRL_CONFIG_T));

	audio_config.eStream = SND_PCM_STREAM_CAPTURE;

	switch(eType)
	{
		case kTKAudioMicIn:
			audio_config.szSelemName = "Bypass Capture";
			err = ATK_Audio_SCtrl_SetSwitch(&audio_config, 0);
			audio_config.szSelemName = "Line";
			err2 = ATK_Audio_SCtrl_SetSwitch(&audio_config, 0);
			audio_config.szSelemName = "Mic";
			err3 = ATK_Audio_SCtrl_SetSwitch(&audio_config, 1);
			audio_config.szSelemName = "Capture Source";
			err4 = ATK_Audio_SCtrl_Set_Enumerated(&audio_config, 0, "Mic");
			break;
		case kTKAudioLineIn:
			audio_config.szSelemName = "Bypass Capture";
			err = ATK_Audio_SCtrl_SetSwitch(&audio_config, 0);
			audio_config.szSelemName = "Mic";
			err2 = ATK_Audio_SCtrl_SetSwitch(&audio_config, 0);
			audio_config.szSelemName = "Line";
			err3 = ATK_Audio_SCtrl_SetSwitch(&audio_config, 1);
			audio_config.szSelemName = "Capture Source";
			err4 = ATK_Audio_SCtrl_Set_Enumerated(&audio_config, 0, "Line");
			break;
		case kTKAudioByPass:
			audio_config.szSelemName = "Line";
			err = ATK_Audio_SCtrl_SetSwitch(&audio_config, 0);
			audio_config.szSelemName = "Mic";
			err2 = ATK_Audio_SCtrl_SetSwitch(&audio_config, 0);
			audio_config.szSelemName = "Bypass Capture";
			err3 = ATK_Audio_SCtrl_SetSwitch(&audio_config, 1);
			break;
		default:
			return -1;
	}
	return ((err == 0) && (err2 == 0) && (err3 == 0) && (err4 == 0)) ? 0 : -1;
#endif
}

static int atk_audio_set_playback_volume(long vol, int mode)
{
	int err = 0, err2 = 0;

	typedef int (*set_vol_func_ptr)(const ATK_AUDIO_SCTRL_CONFIG_T *config, long vol);
	set_vol_func_ptr set_func = (mode) ? ATK_Audio_SCtrl_SetVolume_dB : ATK_Audio_SCtrl_SetVolume;

	ATK_AUDIO_SCTRL_CONFIG_T audio_config;
	memset(&audio_config, 0, sizeof(ATK_AUDIO_SCTRL_CONFIG_T));

	audio_config.eStream = SND_PCM_STREAM_PLAYBACK;

	audio_config.szSelemName = "Master";
	err = set_func(&audio_config, vol);

	audio_config.szSelemName = "Playback";
	err2 = set_func(&audio_config, vol);

	return ((err == 0) || (err2 == 0)) ? 0 : -1;
}

int ATK_Audio_SetPlaybackVolume(long lVol)
{
	return atk_audio_set_playback_volume(lVol, 0);
}

int ATK_Audio_SetPlaybackVolume_dB(long lVol)
{
	return atk_audio_set_playback_volume(lVol, 1);
}

int ATK_Audio_SetPlaybackMute(int bIsMute)
{
	int err = 0, err2 = 0;

	ATK_AUDIO_SCTRL_CONFIG_T audio_config;
	memset(&audio_config, 0, sizeof(ATK_AUDIO_SCTRL_CONFIG_T));

	audio_config.eStream = SND_PCM_STREAM_PLAYBACK;

	audio_config.szSelemName = "Master";
	err = ATK_Audio_SCtrl_SetSwitch(&audio_config, !bIsMute);

	audio_config.szSelemName = "Playback";
	err2 = ATK_Audio_SCtrl_SetSwitch(&audio_config, !bIsMute);

	return ((err == 0) || (err2 == 0)) ? 0 : -1;
}

#define LEVEL_BASIC		(1<<0)
/* Fuction to convert from volume to percentage. val = volume */

static int convert_prange(int val, int min, int max)
{
	int range = max - min;
	int tmp;

	if (range == 0)
		return 0;
	val -= min;
	tmp = rint((double)val/(double)range * 100);
	return tmp;
}

/* Function to convert from percentage to volume. val = percentage */
#define convert_prange1(val, min, max) \
	ceil((val) * ((max) - (min)) * 0.01 + (min))

static const char *get_percent(int val, int min, int max)
{
	static char str[32];
	int p;
	
	p = convert_prange(val, min, max);
	sprintf(str, "%i [%i%%]", val, p);
	return str;
}

static void print_dB(long dB)
{
	printf("%li.%02lidB", dB / 100, (dB < 0 ? -dB : dB) % 100);
}

int show_selem(snd_mixer_t *handle, snd_mixer_selem_id_t *id, const char *space, int level)
{
	snd_mixer_selem_channel_id_t chn;
	long pmin = 0, pmax = 0;
	long cmin = 0, cmax = 0;
	long pvol, cvol;
	int psw, csw;
	int pmono, cmono, mono_ok = 0;
	long db;
	snd_mixer_elem_t *elem;
	const char *card = "default";
	
	elem = snd_mixer_find_selem(handle, id);
	if (!elem) {
		printf("Mixer %s simple element not found", card);
		return -ENOENT;
	}

	if (level & LEVEL_BASIC) {
		printf("%sCapabilities:", space);
		if (snd_mixer_selem_has_common_volume(elem)) {
			printf(" volume");
			if (snd_mixer_selem_has_playback_volume_joined(elem))
				printf(" volume-joined");
		} else {
			if (snd_mixer_selem_has_playback_volume(elem)) {
				printf(" pvolume");
				if (snd_mixer_selem_has_playback_volume_joined(elem))
					printf(" pvolume-joined");
			}
			if (snd_mixer_selem_has_capture_volume(elem)) {
				printf(" cvolume");
				if (snd_mixer_selem_has_capture_volume_joined(elem))
					printf(" cvolume-joined");
			}
		}
		if (snd_mixer_selem_has_common_switch(elem)) {
			printf(" switch");
			if (snd_mixer_selem_has_playback_switch_joined(elem))
				printf(" switch-joined");
		} else {
			if (snd_mixer_selem_has_playback_switch(elem)) {
				printf(" pswitch");
				if (snd_mixer_selem_has_playback_switch_joined(elem))
					printf(" pswitch-joined");
			}
			if (snd_mixer_selem_has_capture_switch(elem)) {
				printf(" cswitch");
				if (snd_mixer_selem_has_capture_switch_joined(elem))
					printf(" cswitch-joined");
				if (snd_mixer_selem_has_capture_switch_exclusive(elem))
					printf(" cswitch-exclusive");
			}
		}
		if (snd_mixer_selem_is_enum_playback(elem)) {
			printf(" penum");
		} else if (snd_mixer_selem_is_enum_capture(elem)) {
			printf(" cenum");
		} else if (snd_mixer_selem_is_enumerated(elem)) {
			printf(" enum");
		}
		printf("\n");
		if (snd_mixer_selem_is_enumerated(elem)) {
			int i, items;
			unsigned int idx;
			char itemname[40];
			items = snd_mixer_selem_get_enum_items(elem);
			printf("  Items:");
			for (i = 0; i < items; i++) {
				snd_mixer_selem_get_enum_item_name(elem, i, sizeof(itemname) - 1, itemname);
				printf(" '%s'", itemname);
			}
			printf("\n");
			for (i = 0; !snd_mixer_selem_get_enum_item(elem, i, &idx); i++) {
				snd_mixer_selem_get_enum_item_name(elem, idx, sizeof(itemname) - 1, itemname);
				printf("  Item%d: '%s'\n", i, itemname);
			}
			return 0; /* no more thing to do */
		}
		if (snd_mixer_selem_has_capture_switch_exclusive(elem))
			printf("%sCapture exclusive group: %i\n", space,
			       snd_mixer_selem_get_capture_group(elem));
		if (snd_mixer_selem_has_playback_volume(elem) ||
		    snd_mixer_selem_has_playback_switch(elem)) {
			printf("%sPlayback channels:", space);
			if (snd_mixer_selem_is_playback_mono(elem)) {
				printf(" Mono");
			} else {
				int first = 1;
				for (chn = 0; chn <= SND_MIXER_SCHN_LAST; chn++){
					if (!snd_mixer_selem_has_playback_channel(elem, chn))
						continue;
					if (!first)
						printf(" -");
					printf(" %s", snd_mixer_selem_channel_name(chn));
					first = 0;
				}
			}
			printf("\n");
		}
		if (snd_mixer_selem_has_capture_volume(elem) ||
		    snd_mixer_selem_has_capture_switch(elem)) {
			printf("%sCapture channels:", space);
			if (snd_mixer_selem_is_capture_mono(elem)) {
				printf(" Mono");
			} else {
				int first = 1;
				for (chn = 0; chn <= SND_MIXER_SCHN_LAST; chn++){
					if (!snd_mixer_selem_has_capture_channel(elem, chn))
						continue;
					if (!first)
						printf(" -");
					printf(" %s", snd_mixer_selem_channel_name(chn));
					first = 0;
				}
			}
			printf("\n");
		}
		if (snd_mixer_selem_has_playback_volume(elem) ||
		    snd_mixer_selem_has_capture_volume(elem)) {
			printf("%sLimits:", space);
			if (snd_mixer_selem_has_common_volume(elem)) {
				snd_mixer_selem_get_playback_volume_range(elem, &pmin, &pmax);
				snd_mixer_selem_get_capture_volume_range(elem, &cmin, &cmax);
				printf(" %li - %li", pmin, pmax);
			} else {
				if (snd_mixer_selem_has_playback_volume(elem)) {
					snd_mixer_selem_get_playback_volume_range(elem, &pmin, &pmax);
					printf(" Playback %li - %li", pmin, pmax);
				}
				if (snd_mixer_selem_has_capture_volume(elem)) {
					snd_mixer_selem_get_capture_volume_range(elem, &cmin, &cmax);
					printf(" Capture %li - %li", cmin, cmax);
				}
			}
			printf("\n");
		}
		pmono = snd_mixer_selem_has_playback_channel(elem, SND_MIXER_SCHN_MONO) &&
		        (snd_mixer_selem_is_playback_mono(elem) || 
			 (!snd_mixer_selem_has_playback_volume(elem) &&
			  !snd_mixer_selem_has_playback_switch(elem)));
		cmono = snd_mixer_selem_has_capture_channel(elem, SND_MIXER_SCHN_MONO) &&
		        (snd_mixer_selem_is_capture_mono(elem) || 
			 (!snd_mixer_selem_has_capture_volume(elem) &&
			  !snd_mixer_selem_has_capture_switch(elem)));

		if (pmono || cmono) {
			if (!mono_ok) {
				printf("%s%s:", space, "Mono");
				mono_ok = 1;
			}
			if (snd_mixer_selem_has_common_volume(elem)) {
				snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_MONO, &pvol);
				printf(" %s", get_percent(pvol, pmin, pmax));
				if (!snd_mixer_selem_get_playback_dB(elem, SND_MIXER_SCHN_MONO, &db)) {
					printf(" [");
					print_dB(db);
					printf("]");
				}
			}
			if (snd_mixer_selem_has_common_switch(elem)) {
				snd_mixer_selem_get_playback_switch(elem, SND_MIXER_SCHN_MONO, &psw);
				printf(" [%s]", psw ? "on" : "off");
			}
		}
		if (pmono && snd_mixer_selem_has_playback_channel(elem, SND_MIXER_SCHN_MONO)) {
			int title = 0;
			if (!mono_ok) {
				printf("%s%s:", space, "Mono");
				mono_ok = 1;
			}
			if (!snd_mixer_selem_has_common_volume(elem)) {
				if (snd_mixer_selem_has_playback_volume(elem)) {
					printf(" Playback");
					title = 1;
					snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_MONO, &pvol);
					printf(" %s", get_percent(pvol, pmin, pmax));
					if (!snd_mixer_selem_get_playback_dB(elem, SND_MIXER_SCHN_MONO, &db)) {
						printf(" [");
						print_dB(db);
						printf("]");
					}
				}
			}
			if (!snd_mixer_selem_has_common_switch(elem)) {
				if (snd_mixer_selem_has_playback_switch(elem)) {
					if (!title)
						printf(" Playback");
					snd_mixer_selem_get_playback_switch(elem, SND_MIXER_SCHN_MONO, &psw);
					printf(" [%s]", psw ? "on" : "off");
				}
			}
		}
		if (cmono && snd_mixer_selem_has_capture_channel(elem, SND_MIXER_SCHN_MONO)) {
			int title = 0;
			if (!mono_ok) {
				printf("%s%s:", space, "Mono");
				mono_ok = 1;
			}
			if (!snd_mixer_selem_has_common_volume(elem)) {
				if (snd_mixer_selem_has_capture_volume(elem)) {
					printf(" Capture");
					title = 1;
					snd_mixer_selem_get_capture_volume(elem, SND_MIXER_SCHN_MONO, &cvol);
					printf(" %s", get_percent(cvol, cmin, cmax));
					if (!snd_mixer_selem_get_capture_dB(elem, SND_MIXER_SCHN_MONO, &db)) {
						printf(" [");
						print_dB(db);
						printf("]");
					}
				}
			}
			if (!snd_mixer_selem_has_common_switch(elem)) {
				if (snd_mixer_selem_has_capture_switch(elem)) {
					if (!title)
						printf(" Capture");
					snd_mixer_selem_get_capture_switch(elem, SND_MIXER_SCHN_MONO, &csw);
					printf(" [%s]", csw ? "on" : "off");
				}
			}
		}
		if (pmono || cmono)
			printf("\n");
		if (!pmono || !cmono) {
			for (chn = 0; chn <= SND_MIXER_SCHN_LAST; chn++) {
				if ((pmono || !snd_mixer_selem_has_playback_channel(elem, chn)) &&
				    (cmono || !snd_mixer_selem_has_capture_channel(elem, chn)))
					continue;
				printf("%s%s:", space, snd_mixer_selem_channel_name(chn));
				if (!pmono && !cmono && snd_mixer_selem_has_common_volume(elem)) {
					snd_mixer_selem_get_playback_volume(elem, chn, &pvol);
					printf(" %s", get_percent(pvol, pmin, pmax));
					if (!snd_mixer_selem_get_playback_dB(elem, chn, &db)) {
						printf(" [");
						print_dB(db);
						printf("]");
					}
				}
				if (!pmono && !cmono && snd_mixer_selem_has_common_switch(elem)) {
					snd_mixer_selem_get_playback_switch(elem, chn, &psw);
					printf(" [%s]", psw ? "on" : "off");
				}
				if (!pmono && snd_mixer_selem_has_playback_channel(elem, chn)) {
					int title = 0;
					if (!snd_mixer_selem_has_common_volume(elem)) {
						if (snd_mixer_selem_has_playback_volume(elem)) {
							printf(" Playback");
							title = 1;
							snd_mixer_selem_get_playback_volume(elem, chn, &pvol);
							printf(" %s", get_percent(pvol, pmin, pmax));
							if (!snd_mixer_selem_get_playback_dB(elem, chn, &db)) {
								printf(" [");
								print_dB(db);
								printf("]");
							}
						}
					}
					if (!snd_mixer_selem_has_common_switch(elem)) {
						if (snd_mixer_selem_has_playback_switch(elem)) {
							if (!title)
								printf(" Playback");
							snd_mixer_selem_get_playback_switch(elem, chn, &psw);
							printf(" [%s]", psw ? "on" : "off");
						}
					}
				}
				if (!cmono && snd_mixer_selem_has_capture_channel(elem, chn)) {
					int title = 0;
					if (!snd_mixer_selem_has_common_volume(elem)) {
						if (snd_mixer_selem_has_capture_volume(elem)) {
							printf(" Capture");
							title = 1;
							snd_mixer_selem_get_capture_volume(elem, chn, &cvol);
							printf(" %s", get_percent(cvol, cmin, cmax));
							if (!snd_mixer_selem_get_capture_dB(elem, chn, &db)) {
								printf(" [");
								print_dB(db);
								printf("]");
							}
						}
					}
					if (!snd_mixer_selem_has_common_switch(elem)) {
						if (snd_mixer_selem_has_capture_switch(elem)) {
							if (!title)
								printf(" Capture");
							snd_mixer_selem_get_capture_switch(elem, chn, &csw);
							printf(" [%s]", csw ? "on" : "off");
						}
					}
				}
				printf("\n");
			}
		}
	}
	return 0;
}

int ATK_Audio_PrintControl()
{
	/* From amixer selems(int level)*/
	int err;
	snd_mixer_t *handle;
	snd_mixer_selem_id_t *sid;
	snd_mixer_elem_t *elem;
	const char *card = "default";
	snd_mixer_selem_id_alloca(&sid);
	
	if ((err = snd_mixer_open(&handle, 0)) < 0) {
		printf("Mixer '%s' open error: %s", card, snd_strerror(err));
		return err;
	}
	if ((err = snd_mixer_attach(handle, card)) < 0) {
		printf("Mixer attach '%s' error: %s", card, snd_strerror(err));
		snd_mixer_close(handle);
		return err;
	}
	if ((err = snd_mixer_selem_register(handle, NULL, NULL)) < 0) {
		printf("Mixer register error: %s", snd_strerror(err));
		snd_mixer_close(handle);
		return err;
	}
	err = snd_mixer_load(handle);
	if (err < 0) {
		printf("Mixer '%s' load error: %s", card, snd_strerror(err));
		snd_mixer_close(handle);
		return err;
	}
	for (elem = snd_mixer_first_elem(handle); elem; elem = snd_mixer_elem_next(elem)) {
		snd_mixer_selem_get_id(elem, sid);
		printf("Mixer control: Index=%i, Name='%s' \n", 
			snd_mixer_selem_id_get_index(sid), snd_mixer_selem_id_get_name(sid));
		show_selem(handle, sid, "  ", 1);
	}
	snd_mixer_close(handle);
	return 0;
}

int ATK_Audio_SetControl(char* pcName, int sdwValue)
{
	int err;
	snd_mixer_t *handle;
	snd_mixer_selem_id_t *sid;
	snd_mixer_elem_t *elem;
	const char *card = "default";
	snd_mixer_selem_id_alloca(&sid);
	
	if ((err = snd_mixer_open(&handle, 0)) < 0) {
		printf("Mixer '%s' open error: %s", card, snd_strerror(err));
		return err;
	}
	if ((err = snd_mixer_attach(handle, card)) < 0) {
		printf("Mixer attach '%s' error: %s", card, snd_strerror(err));
		snd_mixer_close(handle);
		return err;
	}
	if ((err = snd_mixer_selem_register(handle, NULL, NULL)) < 0) {
		printf("Mixer register error: %s", snd_strerror(err));
		snd_mixer_close(handle);
		return err;
	}
	err = snd_mixer_load(handle);
	if (err < 0) {
		printf("Mixer '%s' load error: %s", card, snd_strerror(err));
		snd_mixer_close(handle);
		return err;
	}
	// Set index part of a mixer simple element identifier.
	snd_mixer_selem_id_set_index(sid, 0);
	// Set name part of a mixer simple element identifier.
	snd_mixer_selem_id_set_name(sid, pcName);

	// Find a mixer simple element.
	elem = snd_mixer_find_selem(handle, sid);
	show_selem(handle, sid, "  ", 1);
	//! Try to set value
	if (snd_mixer_selem_has_playback_volume(elem)) {
		//! Playback
		if(!snd_mixer_selem_set_playback_volume_all(elem, sdwValue)) {
			//! Success
			printf("Mixer set %s to %d success!\n", pcName, sdwValue);
		} else
			printf("Mixer set %s to %d fail!\n", pcName, sdwValue);
	} else if(snd_mixer_selem_has_capture_volume(elem)) {
		//! Capture
		if(!snd_mixer_selem_set_capture_volume_all(elem, sdwValue)) {
			//! Success
			printf("Mixer set %s to %d success!\n", pcName, sdwValue);
		} else
			printf("Mixer set %s to %d fail!\n", pcName, sdwValue);
	} else if(snd_mixer_selem_has_playback_switch(elem)) {
		if(!snd_mixer_selem_set_playback_switch_all(elem, sdwValue)) {
			//! Success
			printf("Mixer set %s to %d success!\n", pcName, sdwValue);
		} else
			printf("Mixer set %s to %d fail!\n", pcName, sdwValue);
	} else if(snd_mixer_selem_has_capture_switch(elem)) {
		if(!snd_mixer_selem_set_capture_switch_all(elem, sdwValue)) {
			//! Success
			printf("Mixer set %s to %d success!\n", pcName, sdwValue);
		} else
			printf("Mixer set %s to %d fail!\n", pcName, sdwValue);
	}
	show_selem(handle, sid, "  ", 1);

	snd_mixer_close(handle);
	return -1;
}