static int check_int_elem_props(struct elem_set_trial *trial, snd_ctl_elem_info_t *info) { if (snd_ctl_elem_info_get_min(info) != 0) return -EIO; if (snd_ctl_elem_info_get_max(info) != 99) return -EIO; if (snd_ctl_elem_info_get_step(info) != 1) return -EIO; return 0; }
static int show_control(const char* card, const char *space, snd_hctl_elem_t *elem, int level) { int err; unsigned int item, idx; unsigned int count; snd_ctl_elem_type_t type; snd_ctl_elem_id_t *id; snd_ctl_elem_info_t *info; snd_ctl_elem_value_t *control; snd_ctl_elem_id_alloca(&id); snd_ctl_elem_info_alloca(&info); snd_ctl_elem_value_alloca(&control); if ((err = snd_hctl_elem_info(elem, info)) < 0) { error("Control %s snd_hctl_elem_info error: %s\n", card, snd_strerror(err)); return err; } if (level & LEVEL_ID) { snd_hctl_elem_get_id(elem, id); show_control_id(id); printf("\n"); } count = snd_ctl_elem_info_get_count(info); type = snd_ctl_elem_info_get_type(info); printf("%s; type=%s,access=%s,values=%i", space, control_type(info), control_access(info), count); switch (type) { case SND_CTL_ELEM_TYPE_INTEGER: printf(",min=%li,max=%li,step=%li\n", snd_ctl_elem_info_get_min(info), snd_ctl_elem_info_get_max(info), snd_ctl_elem_info_get_step(info)); break; case SND_CTL_ELEM_TYPE_INTEGER64: printf(",min=%Li,max=%Li,step=%Li\n", snd_ctl_elem_info_get_min64(info), snd_ctl_elem_info_get_max64(info), snd_ctl_elem_info_get_step64(info)); break; case SND_CTL_ELEM_TYPE_ENUMERATED: { unsigned int items = snd_ctl_elem_info_get_items(info); printf(",items=%u\n", items); for (item = 0; item < items; item++) { snd_ctl_elem_info_set_item(info, item); if ((err = snd_hctl_elem_info(elem, info)) < 0) { error("Control %s element info error: %s\n", card, snd_strerror(err)); return err; } printf("%s; Item #%u '%s'\n", space, item, snd_ctl_elem_info_get_item_name(info)); } break; } default: printf("\n"); break; } if (level & LEVEL_BASIC) { if ((err = snd_hctl_elem_read(elem, control)) < 0) { error("Control %s element read error: %s\n", card, snd_strerror(err)); return err; } printf("%s: values=", space); for (idx = 0; idx < count; idx++) { if (idx > 0) printf(","); switch (type) { case SND_CTL_ELEM_TYPE_BOOLEAN: printf("%s", snd_ctl_elem_value_get_boolean(control, idx) ? "on" : "off"); break; case SND_CTL_ELEM_TYPE_INTEGER: printf("%li", snd_ctl_elem_value_get_integer(control, idx)); break; case SND_CTL_ELEM_TYPE_INTEGER64: printf("%Li", snd_ctl_elem_value_get_integer64(control, idx)); break; case SND_CTL_ELEM_TYPE_ENUMERATED: printf("%u", snd_ctl_elem_value_get_enumerated(control, idx)); break; case SND_CTL_ELEM_TYPE_BYTES: printf("0x%02x", snd_ctl_elem_value_get_byte(control, idx)); break; default: printf("?"); break; } } printf("\n"); } return 0; }
static int show_control(const char *space, snd_hctl_elem_t *elem, int level) { int err; unsigned int item, idx, count, *tlv; snd_ctl_elem_type_t type; snd_ctl_elem_id_t *id; snd_ctl_elem_info_t *info; snd_ctl_elem_value_t *control; snd_aes_iec958_t iec958; snd_ctl_elem_id_alloca(&id); snd_ctl_elem_info_alloca(&info); snd_ctl_elem_value_alloca(&control); if ((err = snd_hctl_elem_info(elem, info)) < 0) { error("Control %s snd_hctl_elem_info error: %s\n", card, snd_strerror(err)); return err; } if (level & LEVEL_ID) { snd_hctl_elem_get_id(elem, id); show_control_id(id); printf("\n"); } count = snd_ctl_elem_info_get_count(info); type = snd_ctl_elem_info_get_type(info); printf("%s; type=%s,access=%s,values=%i", space, control_type(info), control_access(info), count); switch (type) { case SND_CTL_ELEM_TYPE_INTEGER: printf(",min=%li,max=%li,step=%li\n", snd_ctl_elem_info_get_min(info), snd_ctl_elem_info_get_max(info), snd_ctl_elem_info_get_step(info)); break; case SND_CTL_ELEM_TYPE_INTEGER64: printf(",min=%Li,max=%Li,step=%Li\n", snd_ctl_elem_info_get_min64(info), snd_ctl_elem_info_get_max64(info), snd_ctl_elem_info_get_step64(info)); break; case SND_CTL_ELEM_TYPE_ENUMERATED: { unsigned int items = snd_ctl_elem_info_get_items(info); printf(",items=%u\n", items); for (item = 0; item < items; item++) { snd_ctl_elem_info_set_item(info, item); if ((err = snd_hctl_elem_info(elem, info)) < 0) { error("Control %s element info error: %s\n", card, snd_strerror(err)); return err; } printf("%s; Item #%u '%s'\n", space, item, snd_ctl_elem_info_get_item_name(info)); } break; } default: printf("\n"); break; } if (level & LEVEL_BASIC) { if (!snd_ctl_elem_info_is_readable(info)) goto __skip_read; if ((err = snd_hctl_elem_read(elem, control)) < 0) { error("Control %s element read error: %s\n", card, snd_strerror(err)); return err; } printf("%s: values=", space); for (idx = 0; idx < count; idx++) { if (idx > 0) printf(","); switch (type) { case SND_CTL_ELEM_TYPE_BOOLEAN: printf("%s", snd_ctl_elem_value_get_boolean(control, idx) ? "on" : "off"); break; case SND_CTL_ELEM_TYPE_INTEGER: printf("%li", snd_ctl_elem_value_get_integer(control, idx)); break; case SND_CTL_ELEM_TYPE_INTEGER64: printf("%Li", snd_ctl_elem_value_get_integer64(control, idx)); break; case SND_CTL_ELEM_TYPE_ENUMERATED: printf("%u", snd_ctl_elem_value_get_enumerated(control, idx)); break; case SND_CTL_ELEM_TYPE_BYTES: printf("0x%02x", snd_ctl_elem_value_get_byte(control, idx)); break; case SND_CTL_ELEM_TYPE_IEC958: snd_ctl_elem_value_get_iec958(control, &iec958); printf("[AES0=0x%02x AES1=0x%02x AES2=0x%02x AES3=0x%02x]", iec958.status[0], iec958.status[1], iec958.status[2], iec958.status[3]); break; default: printf("?"); break; } } printf("\n"); __skip_read: if (!snd_ctl_elem_info_is_tlv_readable(info)) goto __skip_tlv; tlv = malloc(4096); if ((err = snd_hctl_elem_tlv_read(elem, tlv, 4096)) < 0) { error("Control %s element TLV read error: %s\n", card, snd_strerror(err)); free(tlv); return err; } decode_tlv(strlen(space), tlv, 4096); free(tlv); } __skip_tlv: return 0; }
static int get_control(snd_ctl_t *handle, snd_ctl_elem_id_t *id, snd_config_t *top) { snd_ctl_elem_value_t *ctl; snd_ctl_elem_info_t *info; snd_config_t *control, *comment, *item, *value; const char *s; char buf[256]; unsigned int idx; int err; unsigned int device, subdevice, index; const char *name; snd_ctl_elem_type_t type; unsigned int count; snd_ctl_elem_value_alloca(&ctl); snd_ctl_elem_info_alloca(&info); snd_ctl_elem_info_set_id(info, id); err = snd_ctl_elem_info(handle, info); if (err < 0) { error("Cannot read control info '%s': %s", id_str(id), snd_strerror(err)); return err; } if (snd_ctl_elem_info_is_inactive(info) || !snd_ctl_elem_info_is_readable(info)) return 0; snd_ctl_elem_value_set_id(ctl, id); err = snd_ctl_elem_read(handle, ctl); if (err < 0) { error("Cannot read control '%s': %s", id_str(id), snd_strerror(err)); return err; } err = snd_config_compound_add(top, num_str(snd_ctl_elem_info_get_numid(info)), 0, &control); if (err < 0) { error("snd_config_compound_add: %s", snd_strerror(err)); return err; } err = snd_config_compound_add(control, "comment", 1, &comment); if (err < 0) { error("snd_config_compound_add: %s", snd_strerror(err)); return err; } buf[0] = '\0'; buf[1] = '\0'; if (snd_ctl_elem_info_is_readable(info)) strcat(buf, " read"); if (snd_ctl_elem_info_is_writable(info)) strcat(buf, " write"); if (snd_ctl_elem_info_is_inactive(info)) strcat(buf, " inactive"); if (snd_ctl_elem_info_is_volatile(info)) strcat(buf, " volatile"); if (snd_ctl_elem_info_is_locked(info)) strcat(buf, " locked"); if (snd_ctl_elem_info_is_user(info)) strcat(buf, " user"); err = snd_config_string_add(comment, "access", buf + 1); if (err < 0) { error("snd_config_string_add: %s", snd_strerror(err)); return err; } type = snd_ctl_elem_info_get_type(info); device = snd_ctl_elem_info_get_device(info); subdevice = snd_ctl_elem_info_get_subdevice(info); index = snd_ctl_elem_info_get_index(info); name = snd_ctl_elem_info_get_name(info); count = snd_ctl_elem_info_get_count(info); s = snd_ctl_elem_type_name(type); err = snd_config_string_add(comment, "type", s); if (err < 0) { error("snd_config_string_add: %s", snd_strerror(err)); return err; } err = snd_config_integer_add(comment, "count", count); if (err < 0) { error("snd_config_integer_add: %s", snd_strerror(err)); return err; } switch (type) { case SND_CTL_ELEM_TYPE_BOOLEAN: break; case SND_CTL_ELEM_TYPE_INTEGER: { long min = snd_ctl_elem_info_get_min(info); long max = snd_ctl_elem_info_get_max(info); long step = snd_ctl_elem_info_get_step(info); if (step) sprintf(buf, "%li - %li (step %li)", min, max, step); else sprintf(buf, "%li - %li", min, max); err = snd_config_string_add(comment, "range", buf); if (err < 0) { error("snd_config_string_add: %s", snd_strerror(err)); return err; } if (snd_ctl_elem_info_is_tlv_readable(info)) { err = add_tlv_comments(handle, id, info, comment); if (err < 0) return err; } break; } case SND_CTL_ELEM_TYPE_INTEGER64: { long long min = snd_ctl_elem_info_get_min64(info); long long max = snd_ctl_elem_info_get_max64(info); long long step = snd_ctl_elem_info_get_step64(info); if (step) sprintf(buf, "%Li - %Li (step %Li)", min, max, step); else sprintf(buf, "%Li - %Li", min, max); err = snd_config_string_add(comment, "range", buf); if (err < 0) { error("snd_config_string_add: %s", snd_strerror(err)); return err; } break; } case SND_CTL_ELEM_TYPE_ENUMERATED: { unsigned int items; err = snd_config_compound_add(comment, "item", 1, &item); if (err < 0) { error("snd_config_compound_add: %s", snd_strerror(err)); return err; } items = snd_ctl_elem_info_get_items(info); for (idx = 0; idx < items; idx++) { snd_ctl_elem_info_set_item(info, idx); err = snd_ctl_elem_info(handle, info); if (err < 0) { error("snd_ctl_card_info: %s", snd_strerror(err)); return err; } err = snd_config_string_add(item, num_str(idx), snd_ctl_elem_info_get_item_name(info)); if (err < 0) { error("snd_config_string_add: %s", snd_strerror(err)); return err; } } break; } default: break; } s = snd_ctl_elem_iface_name(snd_ctl_elem_info_get_interface(info)); err = snd_config_string_add(control, "iface", s); if (err < 0) { error("snd_config_string_add: %s", snd_strerror(err)); return err; } if (device != 0) { err = snd_config_integer_add(control, "device", device); if (err < 0) { error("snd_config_integer_add: %s", snd_strerror(err)); return err; } } if (subdevice != 0) { err = snd_config_integer_add(control, "subdevice", subdevice); if (err < 0) { error("snd_config_integer_add: %s", snd_strerror(err)); return err; } } err = snd_config_string_add(control, "name", name); if (err < 0) { error("snd_config_string_add: %s", snd_strerror(err)); return err; } if (index != 0) { err = snd_config_integer_add(control, "index", index); if (err < 0) { error("snd_config_integer_add: %s", snd_strerror(err)); return err; } } switch (type) { case SND_CTL_ELEM_TYPE_BYTES: case SND_CTL_ELEM_TYPE_IEC958: { size_t size = type == SND_CTL_ELEM_TYPE_BYTES ? count : sizeof(snd_aes_iec958_t); char buf[size * 2 + 1]; char *p = buf; char *hex = "0123456789abcdef"; const unsigned char *bytes = (const unsigned char *)snd_ctl_elem_value_get_bytes(ctl); for (idx = 0; idx < size; idx++) { int v = bytes[idx]; *p++ = hex[v >> 4]; *p++ = hex[v & 0x0f]; } *p = '\0'; err = snd_config_string_add(control, "value", buf); if (err < 0) { error("snd_config_string_add: %s", snd_strerror(err)); return err; } return 0; } default: break; } if (count == 1) { switch (type) { case SND_CTL_ELEM_TYPE_BOOLEAN: err = snd_config_string_add(control, "value", snd_ctl_elem_value_get_boolean(ctl, 0) ? "true" : "false"); if (err < 0) { error("snd_config_string_add: %s", snd_strerror(err)); return err; } return 0; case SND_CTL_ELEM_TYPE_INTEGER: err = snd_config_integer_add(control, "value", snd_ctl_elem_value_get_integer(ctl, 0)); if (err < 0) { error("snd_config_integer_add: %s", snd_strerror(err)); return err; } return 0; case SND_CTL_ELEM_TYPE_INTEGER64: err = snd_config_integer64_add(control, "value", snd_ctl_elem_value_get_integer64(ctl, 0)); if (err < 0) { error("snd_config_integer64_add: %s", snd_strerror(err)); return err; } return 0; case SND_CTL_ELEM_TYPE_ENUMERATED: { unsigned int v = snd_ctl_elem_value_get_enumerated(ctl, 0); snd_config_t *c; err = snd_config_search(item, num_str(v), &c); if (err == 0) { err = snd_config_get_string(c, &s); assert(err == 0); err = snd_config_string_add(control, "value", s); } else { err = snd_config_integer_add(control, "value", v); } if (err < 0) error("snd_config add: %s", snd_strerror(err)); return 0; } default: error("Unknown control type: %d\n", type); return -EINVAL; } } err = snd_config_compound_add(control, "value", 1, &value); if (err < 0) { error("snd_config_compound_add: %s", snd_strerror(err)); return err; } switch (type) { case SND_CTL_ELEM_TYPE_BOOLEAN: for (idx = 0; idx < count; idx++) { err = snd_config_string_add(value, num_str(idx), snd_ctl_elem_value_get_boolean(ctl, idx) ? "true" : "false"); if (err < 0) { error("snd_config_string_add: %s", snd_strerror(err)); return err; } } break; case SND_CTL_ELEM_TYPE_INTEGER: for (idx = 0; idx < count; idx++) { err = snd_config_integer_add(value, num_str(idx), snd_ctl_elem_value_get_integer(ctl, idx)); if (err < 0) { error("snd_config_integer_add: %s", snd_strerror(err)); return err; } } break; case SND_CTL_ELEM_TYPE_INTEGER64: for (idx = 0; idx < count; idx++) { err = snd_config_integer64_add(value, num_str(idx), snd_ctl_elem_value_get_integer64(ctl, idx)); if (err < 0) { error("snd_config_integer64_add: %s", snd_strerror(err)); return err; } } break; case SND_CTL_ELEM_TYPE_ENUMERATED: for (idx = 0; idx < count; idx++) { unsigned int v = snd_ctl_elem_value_get_enumerated(ctl, idx); snd_config_t *c; err = snd_config_search(item, num_str(v), &c); if (err == 0) { err = snd_config_get_string(c, &s); assert(err == 0); err = snd_config_string_add(value, num_str(idx), s); } else { err = snd_config_integer_add(value, num_str(idx), v); } if (err < 0) { error("snd_config add: %s", snd_strerror(err)); return err; } } break; default: error("Unknown control type: %d\n", type); return -EINVAL; } return 0; }
/************************************************************************** * ALSA_CheckSetVolume [internal] * * Helper function for Alsa volume queries. This tries to simplify * the process of managing the volume. All parameters are optional * (pass NULL to ignore or not use). * Return values are MMSYSERR_NOERROR on success, or !0 on failure; * error codes are normalized into the possible documented return * values from waveOutGetVolume. */ int ALSA_CheckSetVolume(snd_hctl_t *hctl, int *out_left, int *out_right, int *out_min, int *out_max, int *out_step, int *new_left, int *new_right) { int rc = MMSYSERR_NOERROR; int value_count = 0; snd_hctl_elem_t * elem = NULL; snd_ctl_elem_info_t * eleminfop = NULL; snd_ctl_elem_value_t * elemvaluep = NULL; snd_ctl_elem_id_t * elemidp = NULL; const char *names[] = {"PCM Playback Volume", "Line Playback Volume", NULL}; const char **name; #define EXIT_ON_ERROR(f,txt,exitcode) do \ { \ int err; \ if ( (err = (f) ) < 0) \ { \ ERR(txt " failed: %s\n", snd_strerror(err)); \ rc = exitcode; \ goto out; \ } \ } while(0) if (! hctl) return MMSYSERR_NOTSUPPORTED; /* Allocate areas to return information about the volume */ EXIT_ON_ERROR(snd_ctl_elem_id_malloc(&elemidp), "snd_ctl_elem_id_malloc", MMSYSERR_NOMEM); EXIT_ON_ERROR(snd_ctl_elem_value_malloc (&elemvaluep), "snd_ctl_elem_value_malloc", MMSYSERR_NOMEM); EXIT_ON_ERROR(snd_ctl_elem_info_malloc (&eleminfop), "snd_ctl_elem_info_malloc", MMSYSERR_NOMEM); snd_ctl_elem_id_clear(elemidp); snd_ctl_elem_value_clear(elemvaluep); snd_ctl_elem_info_clear(eleminfop); /* Setup and find an element id that exactly matches the characteristic we want ** FIXME: It is probably short sighted to hard code and fixate on PCM Playback Volume */ for( name = names; *name; name++ ) { snd_ctl_elem_id_set_name(elemidp, *name); snd_ctl_elem_id_set_interface(elemidp, SND_CTL_ELEM_IFACE_MIXER); elem = snd_hctl_find_elem(hctl, elemidp); if (elem) { /* Read and return volume information */ EXIT_ON_ERROR(snd_hctl_elem_info(elem, eleminfop), "snd_hctl_elem_info", MMSYSERR_NOTSUPPORTED); value_count = snd_ctl_elem_info_get_count(eleminfop); if (out_min || out_max || out_step) { if (!snd_ctl_elem_info_is_readable(eleminfop)) { ERR("snd_ctl_elem_info_is_readable returned false; cannot return info\n"); rc = MMSYSERR_NOTSUPPORTED; goto out; } if (out_min) *out_min = snd_ctl_elem_info_get_min(eleminfop); if (out_max) *out_max = snd_ctl_elem_info_get_max(eleminfop); if (out_step) *out_step = snd_ctl_elem_info_get_step(eleminfop); } if (out_left || out_right) { EXIT_ON_ERROR(snd_hctl_elem_read(elem, elemvaluep), "snd_hctl_elem_read", MMSYSERR_NOTSUPPORTED); if (out_left) *out_left = snd_ctl_elem_value_get_integer(elemvaluep, 0); if (out_right) { if (value_count == 1) *out_right = snd_ctl_elem_value_get_integer(elemvaluep, 0); else if (value_count == 2) *out_right = snd_ctl_elem_value_get_integer(elemvaluep, 1); else { ERR("Unexpected value count %d from snd_ctl_elem_info_get_count while getting volume info\n", value_count); rc = -1; goto out; } } } /* Set the volume */ if (new_left || new_right) { EXIT_ON_ERROR(snd_hctl_elem_read(elem, elemvaluep), "snd_hctl_elem_read", MMSYSERR_NOTSUPPORTED); if (new_left) snd_ctl_elem_value_set_integer(elemvaluep, 0, *new_left); if (new_right) { if (value_count == 1) snd_ctl_elem_value_set_integer(elemvaluep, 0, *new_right); else if (value_count == 2) snd_ctl_elem_value_set_integer(elemvaluep, 1, *new_right); else { ERR("Unexpected value count %d from snd_ctl_elem_info_get_count while setting volume info\n", value_count); rc = -1; goto out; } } EXIT_ON_ERROR(snd_hctl_elem_write(elem, elemvaluep), "snd_hctl_elem_write", MMSYSERR_NOTSUPPORTED); } break; } } if( !*name ) { ERR("Could not find '{PCM,Line} Playback Volume' element\n"); rc = MMSYSERR_NOTSUPPORTED; } #undef EXIT_ON_ERROR out: if (elemvaluep) snd_ctl_elem_value_free(elemvaluep); if (eleminfop) snd_ctl_elem_info_free(eleminfop); if (elemidp) snd_ctl_elem_id_free(elemidp); return rc; }
void main( int argc, char *argv[] ) { struct structArgs cmdArgs; int errNum; // ************************************************************************ // ALSA control elements. // ************************************************************************ snd_ctl_t *ctl; // Simple control handle. snd_ctl_elem_id_t *id; // Simple control element id. snd_ctl_elem_value_t *control; // Simple control element value. snd_ctl_elem_type_t type; // Simple control element type. snd_ctl_elem_info_t *info; // Simple control info container. // ************************************************************************ // Get command line parameters. // ************************************************************************ argp_parse( &argp, argc, argv, 0, 0, &cmdArgs ); printf( "Card = %i\n", cmdArgs.card ); printf( "Control = %i\n", cmdArgs.control ); printf( "Value 1 = %i\n", cmdArgs.value1 ); printf( "Value 2 = %i\n", cmdArgs.value2 ); // ************************************************************************ // Set up ALSA control. // ************************************************************************ sprintf( cmdArgs.deviceID, "hw:%i", cmdArgs.card ); printf( "Device ID = %s.\n", cmdArgs.deviceID ); if ( snd_ctl_open( &ctl, cmdArgs.deviceID, 1 ) < 0 ) { printf( "Error opening control.\n" ); return; } // Initialise a simple control element id structure. snd_ctl_elem_id_alloca( &id ); snd_ctl_elem_id_set_numid( id, cmdArgs.control ); // Initialise info element. snd_ctl_elem_info_alloca( &info ); snd_ctl_elem_info_set_numid( info, cmdArgs.control ); // Is the control valid? if ( snd_ctl_elem_info( ctl, info ) < 0 ) { printf( "Error getting control element info.\n" ); return; } // Find type of control. // either: // SND_CTL_ELEM_TYPE_INTEGER, // SND_CTL_ELEM_TYPE_INTEGER64, // SND_CTL_ELEM_TYPE_ENUMERATED, etc. // Only interested in INTEGER. type = snd_ctl_elem_info_get_type( info ); if ( type != SND_CTL_ELEM_TYPE_INTEGER ) { printf( "Control type is not integer.\n" ); printf( "Type = %s\n", type ); return; } // ************************************************************************ // Get some information for selected control. // ************************************************************************ printf( "Min value for control = %d\n", snd_ctl_elem_info_get_min( info )); printf( "Max value for control = %d\n", snd_ctl_elem_info_get_max( info )); printf( "Step value for control = %d\n", snd_ctl_elem_info_get_step( info )); // Initialise the control element value container. snd_ctl_elem_value_alloca( &control ); snd_ctl_elem_value_set_id( control, id ); // ************************************************************************ // Set values for selected control. // ************************************************************************ snd_ctl_elem_value_set_integer( control, 0, cmdArgs.value1 ); if ( snd_ctl_elem_write( ctl, control ) < 0 ) printf( "Error setting L volume" ); else printf( "Set L volume to %d.\n", cmdArgs.value1 ); snd_ctl_elem_value_set_integer( control, 1, cmdArgs.value2 ); if ( snd_ctl_elem_write( ctl, control ) < 0 ) printf( "Error setting R volume" ); else printf( "Set R volume to %d.\n", cmdArgs.value2 ); // ************************************************************************ // Clean up. // ************************************************************************ snd_ctl_close( ctl ); return; }