/* ******************************************************************************* * 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 #include #include //#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; }