/************************************************************************** * 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; }
int main(int argc, char *argv[]) { char *format = FORMAT; char *device = DEVICE; char *volume_control = VOLUME_CONTROL; char *mute_control = MUTE_CONTROL; int interval = INTERVAL; bool snoop = false; int opt; while ((opt = getopt(argc, argv, "hsf:i:d:v:m:")) != -1) { switch (opt) { case 'h': printf("volume [-h|-s|-f FORMAT|-i INTERVAL|-d DEVICE|-v VOLUME_CONTROL|-m MUTE_CONTROL]\n"); exit(EXIT_SUCCESS); break; case 's': snoop = true; break; case 'f': format = optarg; break; case 'd': device = optarg; break; case 'i': interval = atoi(optarg); break; case 'v': volume_control = optarg; break; case 'm': mute_control = optarg; break; } } int exit_code; snd_hctl_t *hctl; snd_ctl_elem_id_t *volume_id; snd_ctl_elem_id_t *mute_id; snd_ctl_elem_value_t *volume_ctl; snd_ctl_elem_value_t *mute_ctl; snd_ctl_elem_info_t *volume_info; snd_hctl_elem_t *volume_elem; snd_hctl_elem_t *mute_elem; int vol_max, vol_min; if (snd_hctl_open(&hctl, device, 0) || snd_hctl_load(hctl)) { return EXIT_FAILURE; } snd_ctl_elem_id_malloc(&volume_id); snd_ctl_elem_id_malloc(&mute_id); snd_ctl_elem_value_malloc(&volume_ctl); snd_ctl_elem_value_malloc(&mute_ctl); snd_ctl_elem_info_malloc(&volume_info); snd_ctl_elem_id_set_interface(volume_id, SND_CTL_ELEM_IFACE_MIXER); snd_ctl_elem_id_set_interface(mute_id, SND_CTL_ELEM_IFACE_MIXER); snd_ctl_elem_id_set_name(volume_id, volume_control); snd_ctl_elem_id_set_name(mute_id, mute_control); snd_ctl_elem_value_set_id(volume_ctl, volume_id); snd_ctl_elem_value_set_id(mute_ctl, mute_id); volume_elem = snd_hctl_find_elem(hctl, volume_id); mute_elem = snd_hctl_find_elem(hctl, mute_id); snd_ctl_elem_info_set_id(volume_info, volume_id); snd_hctl_elem_info(volume_elem, volume_info); vol_min = (int)snd_ctl_elem_info_get_min(volume_info); vol_max = (int)snd_ctl_elem_info_get_max(volume_info); if (volume_elem == NULL || mute_elem == NULL) { snd_hctl_close(hctl); return EXIT_FAILURE; } struct sigaction action = {.sa_handler = &sig_handler}; sigaction(SIGUSR1, &action, NULL); if (snoop) for (;;) { if (update) { update = false; continue; } if ((exit_code = put_infos(vol_min, vol_max, volume_elem, mute_elem, volume_ctl, mute_ctl, format)) == EXIT_FAILURE) break; sleep(interval); } else exit_code = put_infos(vol_min, vol_max, volume_elem, mute_elem, volume_ctl, mute_ctl, format); snd_hctl_close(hctl); snd_ctl_elem_id_free(volume_id); snd_ctl_elem_id_free(mute_id); snd_ctl_elem_value_free(volume_ctl); snd_ctl_elem_value_free(mute_ctl); snd_ctl_elem_info_free(volume_info); return exit_code; }
/* Try and find an IEC958 PCM device and mixer on card 0 and open it * This function is only used on older ALSA installs that don't have the * correct iec958 alias stuff set up, and relies on there being only * one IEC958 PCM device (relies IEC958 in the device name) and one IEC958 * mixer control for doing the settings. */ static int alsaspdifsink_find_pcm_device (AlsaSPDIFSink * sink) { int err = -1, dev, idx, count; const gchar *ctl_name = "hw:0"; const gchar *spdif_name = SND_CTL_NAME_IEC958 ("", PLAYBACK, NONE); int card = sink->card; gchar pcm_name[24]; snd_pcm_t *pcm = NULL; snd_ctl_t *ctl = NULL; snd_ctl_card_info_t *info = NULL; snd_ctl_elem_list_t *clist = NULL; snd_ctl_elem_id_t *cid = NULL; snd_pcm_info_t *pinfo = NULL; GST_WARNING ("Opening IEC958 named device failed. Trying to autodetect"); if ((err = snd_ctl_open (&ctl, ctl_name, card)) < 0) return err; snd_ctl_card_info_malloc (&info); snd_pcm_info_malloc (&pinfo); /* Find a mixer for IEC958 settings */ snd_ctl_elem_list_malloc (&clist); if ((err = snd_ctl_elem_list (ctl, clist)) < 0) goto beach; if ((err = snd_ctl_elem_list_alloc_space (clist, snd_ctl_elem_list_get_count (clist))) < 0) goto beach; if ((err = snd_ctl_elem_list (ctl, clist)) < 0) goto beach; count = snd_ctl_elem_list_get_used (clist); for (idx = 0; idx < count; idx++) { if (strstr (snd_ctl_elem_list_get_name (clist, idx), spdif_name) != NULL) break; } if (idx == count) { /* No SPDIF mixer availble */ err = 0; goto beach; } snd_ctl_elem_id_malloc (&cid); snd_ctl_elem_list_get_id (clist, idx, cid); /* Now find a PCM device for IEC 958 */ if ((err = snd_ctl_card_info (ctl, info)) < 0) goto beach; dev = -1; do { if (snd_ctl_pcm_next_device (ctl, &dev) < 0) goto beach; if (dev < 0) break; /* No more devices */ /* Filter for playback devices */ snd_pcm_info_set_device (pinfo, dev); snd_pcm_info_set_subdevice (pinfo, 0); snd_pcm_info_set_stream (pinfo, SND_PCM_STREAM_PLAYBACK); if ((err = snd_ctl_pcm_info (ctl, pinfo)) < 0) { if (err != -ENOENT) goto beach; /* Genuine error */ /* Device has no playback streams */ continue; } if (strstr (snd_pcm_info_get_name (pinfo), "IEC958") == NULL) continue; /* Not the device we are looking for */ count = snd_pcm_info_get_subdevices_count (pinfo); GST_LOG_OBJECT (sink, "Device %d has %d subdevices\n", dev, snd_pcm_info_get_subdevices_count (pinfo)); for (idx = 0; idx < count; idx++) { snd_pcm_info_set_subdevice (pinfo, idx); if ((err = snd_ctl_pcm_info (ctl, pinfo)) < 0) goto beach; g_assert (snd_pcm_info_get_stream (pinfo) == SND_PCM_STREAM_PLAYBACK); GST_LOG_OBJECT (sink, "Found playback stream on dev %d sub-d %d\n", dev, idx); /* Found a suitable PCM device, let's open it */ g_snprintf (pcm_name, 24, "hw:%d,%d", card, dev); if ((err = snd_pcm_open (&(pcm), pcm_name, SND_PCM_STREAM_PLAYBACK, 0)) < 0) goto beach; break; } } while (pcm == NULL); if (pcm != NULL) { snd_ctl_elem_value_t *cval; snd_aes_iec958_t iec958; /* Have a PCM device and a mixer, set things up */ snd_ctl_elem_value_malloc (&cval); snd_ctl_elem_value_set_id (cval, cid); snd_ctl_elem_value_get_iec958 (cval, &iec958); iec958.status[0] = IEC958_AES0_NONAUDIO; iec958.status[1] = IEC958_AES1_CON_ORIGINAL | IEC958_AES1_CON_PCM_CODER; iec958.status[2] = 0; iec958.status[3] = IEC958_AES3_CON_FS_48000; snd_ctl_elem_value_set_iec958 (cval, &iec958); snd_ctl_elem_value_free (cval); sink->pcm = pcm; pcm = NULL; err = 0; } beach: if (pcm) snd_pcm_close (pcm); if (clist) snd_ctl_elem_list_clear (clist); if (ctl) snd_ctl_close (ctl); if (clist) snd_ctl_elem_list_free (clist); if (cid) snd_ctl_elem_id_free (cid); if (info) snd_ctl_card_info_free (info); if (pinfo) snd_pcm_info_free (pinfo); return err; }