void VolumeControl::init() { //initialize audio mixer interface #if defined (__APPLE__) #error TODO: Not implemented for MacOS yet!!! #elif defined(__linux__) //try to open mixer device if (mixerHandle == nullptr) { snd_mixer_selem_id_alloca(&mixerSelemId); //sets simple-mixer index and name snd_mixer_selem_id_set_index(mixerSelemId, mixerIndex); snd_mixer_selem_id_set_name(mixerSelemId, mixerName); //open mixer if (snd_mixer_open(&mixerHandle, 0) >= 0) { LOG(LogDebug) << "VolumeControl::init() - Opened ALSA mixer"; //ok. attach to defualt card if (snd_mixer_attach(mixerHandle, mixerCard) >= 0) { LOG(LogDebug) << "VolumeControl::init() - Attached to default card"; //ok. register simple element class if (snd_mixer_selem_register(mixerHandle, NULL, NULL) >= 0) { LOG(LogDebug) << "VolumeControl::init() - Registered simple element class"; //ok. load registered elements if (snd_mixer_load(mixerHandle) >= 0) { LOG(LogDebug) << "VolumeControl::init() - Loaded mixer elements"; //ok. find elements now mixerElem = snd_mixer_find_selem(mixerHandle, mixerSelemId); if (mixerElem != nullptr) { //wohoo. good to go... LOG(LogDebug) << "VolumeControl::init() - Mixer initialized"; } else { LOG(LogError) << "VolumeControl::init() - Failed to find mixer elements!"; snd_mixer_close(mixerHandle); mixerHandle = nullptr; } } else { LOG(LogError) << "VolumeControl::init() - Failed to load mixer elements!"; snd_mixer_close(mixerHandle); mixerHandle = nullptr; } } else { LOG(LogError) << "VolumeControl::init() - Failed to register simple element class!"; snd_mixer_close(mixerHandle); mixerHandle = nullptr; } } else { LOG(LogError) << "VolumeControl::init() - Failed to attach to default card!"; snd_mixer_close(mixerHandle); mixerHandle = nullptr; } } else { LOG(LogError) << "VolumeControl::init() - Failed to open ALSA mixer!"; } } #elif defined(WIN32) || defined(_WIN32) //get windows version information OSVERSIONINFOEXA osVer = {sizeof(OSVERSIONINFO)}; ::GetVersionExA(reinterpret_cast<LPOSVERSIONINFOA>(&osVer)); //check windows version if(osVer.dwMajorVersion < 6) { //Windows older than Vista. use mixer API. open default mixer if (mixerHandle == nullptr) { if (mixerOpen(&mixerHandle, 0, NULL, 0, 0) == MMSYSERR_NOERROR) { //retrieve info on the volume slider control for the "Speaker Out" line MIXERLINECONTROLS mixerLineControls; mixerLineControls.cbStruct = sizeof(MIXERLINECONTROLS); mixerLineControls.dwLineID = 0xFFFF0000; //Id of "Speaker Out" line mixerLineControls.cControls = 1; //mixerLineControls.dwControlID = 0x00000000; //Id of "Speaker Out" line's volume slider mixerLineControls.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME; //Get volume control mixerLineControls.pamxctrl = &mixerControl; mixerLineControls.cbmxctrl = sizeof(MIXERCONTROL); if (mixerGetLineControls((HMIXEROBJ)mixerHandle, &mixerLineControls, MIXER_GETLINECONTROLSF_ONEBYTYPE) != MMSYSERR_NOERROR) { LOG(LogError) << "VolumeControl::getVolume() - Failed to get mixer volume control!"; mixerClose(mixerHandle); mixerHandle = nullptr; } } else { LOG(LogError) << "VolumeControl::init() - Failed to open mixer!"; } } } else { //Windows Vista or above. use EndpointVolume API. get device enumerator if (endpointVolume == nullptr) { CoInitialize(nullptr); IMMDeviceEnumerator * deviceEnumerator = nullptr; CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_INPROC_SERVER, __uuidof(IMMDeviceEnumerator), (LPVOID *)&deviceEnumerator); if (deviceEnumerator != nullptr) { //get default endpoint IMMDevice * defaultDevice = nullptr; deviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &defaultDevice); if (defaultDevice != nullptr) { //retrieve endpoint volume defaultDevice->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, nullptr, (LPVOID *)&endpointVolume); if (endpointVolume == nullptr) { LOG(LogError) << "VolumeControl::init() - Failed to get default audio endpoint volume!"; } //release default device. we don't need it anymore defaultDevice->Release(); } else { LOG(LogError) << "VolumeControl::init() - Failed to get default audio endpoint!"; } //release device enumerator. we don't need it anymore deviceEnumerator->Release(); } else { LOG(LogError) << "VolumeControl::init() - Failed to get audio endpoint enumerator!"; CoUninitialize(); } } } #endif }
ALSAMixer::ALSAMixer() { int err; initMixer (&mMixer[SND_PCM_STREAM_PLAYBACK], "AndroidOut"); initMixer (&mMixer[SND_PCM_STREAM_CAPTURE], "AndroidIn"); snd_mixer_selem_id_t *sid; snd_mixer_selem_id_alloca(&sid); for (int i = 0; i <= SND_PCM_STREAM_LAST; i++) { if (!mMixer[i]) continue; mixer_info_t *info = mixerMasterProp[i].mInfo = new mixer_info_t; property_get (mixerMasterProp[i].propName, info->name, mixerMasterProp[i].propDefault); for (snd_mixer_elem_t *elem = snd_mixer_first_elem(mMixer[i]); elem; elem = snd_mixer_elem_next(elem)) { if (!snd_mixer_selem_is_active(elem)) continue; snd_mixer_selem_get_id(elem, sid); // Find PCM playback volume control element. const char *elementName = snd_mixer_selem_id_get_name(sid); if (info->elem == NULL && strcmp(elementName, info->name) == 0 && hasVolume[i] (elem)) { info->elem = elem; getVolumeRange[i] (elem, &info->min, &info->max); info->volume = info->max; setVol[i] (elem, info->volume); if (i == SND_PCM_STREAM_PLAYBACK && snd_mixer_selem_has_playback_switch (elem)) snd_mixer_selem_set_playback_switch_all (elem, 1); break; } } LOGV("Mixer: master '%s' %s.", info->name, info->elem ? "found" : "not found"); for (int j = 0; mixerProp[j][i].device; j++) { mixer_info_t *info = mixerProp[j][i].mInfo = new mixer_info_t; property_get (mixerProp[j][i].propName, info->name, mixerProp[j][i].propDefault); for (snd_mixer_elem_t *elem = snd_mixer_first_elem(mMixer[i]); elem; elem = snd_mixer_elem_next(elem)) { if (!snd_mixer_selem_is_active(elem)) continue; snd_mixer_selem_get_id(elem, sid); // Find PCM playback volume control element. const char *elementName = snd_mixer_selem_id_get_name(sid); if (info->elem == NULL && strcmp(elementName, info->name) == 0 && hasVolume[i] (elem)) { info->elem = elem; getVolumeRange[i] (elem, &info->min, &info->max); info->volume = info->max; setVol[i] (elem, info->volume); if (i == SND_PCM_STREAM_PLAYBACK && snd_mixer_selem_has_playback_switch (elem)) snd_mixer_selem_set_playback_switch_all (elem, 1); break; } } LOGV("Mixer: route '%s' %s.", info->name, info->elem ? "found" : "not found"); } } LOGV("mixer initialized."); }
gboolean show_mixer(GtkWidget *widget, GdkEvent *event, gpointer user_data) { GtkWidget *win = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW(win), "SIDE Mixer"); gtk_container_set_border_width(GTK_CONTAINER(win), 10); gtk_window_resize(GTK_WINDOW(win), 150, 300); GdkScreen *screen = gdk_screen_get_default(); fwin = win; g_timeout_add(100, check_focus, win); GtkWidget *box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5); GtkWidget *dsc = gtk_label_new("master"); GtkAdjustment *adj = gtk_adjustment_new(50, 0, 100, 5, 0, 0); g_signal_connect(G_OBJECT(adj), "value-changed", G_CALLBACK(set_volume), NULL); GtkWidget *scl = gtk_scale_new(GTK_ORIENTATION_VERTICAL, adj); gtk_scale_set_draw_value(GTK_SCALE(scl), FALSE); s_mute = gtk_toggle_button_new_with_label("mute"); g_signal_connect(G_OBJECT(s_mute), "toggled", G_CALLBACK(set_mute), (gpointer) adj); gtk_box_pack_end(GTK_BOX(box), s_mute, FALSE, FALSE, 0); gtk_container_add(GTK_CONTAINER(box), dsc); gtk_box_pack_end(GTK_BOX(box), scl, TRUE, TRUE, 5); int x, y; gtk_window_get_size(GTK_WINDOW(win), &x, &y); gtk_window_set_decorated(GTK_WINDOW(win), FALSE); gtk_window_move(GTK_WINDOW(win), gdk_screen_get_width(screen), gdk_screen_get_height(screen) - (y+25)); gtk_container_add(GTK_CONTAINER(win), box); gtk_widget_show_all(win); snd_mixer_t *mix; snd_mixer_selem_id_t *sid; const char *card = "default"; const char *selem_name = "Master"; long val, max, min; snd_mixer_open(&mix, 0); snd_mixer_attach(mix, card); snd_mixer_selem_register(mix, NULL, NULL); snd_mixer_load(mix); snd_mixer_selem_id_alloca(&sid); snd_mixer_selem_id_set_index(sid, 0); snd_mixer_selem_id_set_name(sid, selem_name); snd_mixer_elem_t* elem = snd_mixer_find_selem(mix, sid); snd_mixer_selem_get_playback_volume_range(elem, &min, &max); snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_MONO , &val); gtk_adjustment_set_value(adj, 100 - (int) 100 * val / max); snd_mixer_close(mix); return FALSE; }
static void set_mixer(const char *device, const char *mixer, int mixer_index, bool setmax, float ldB, float rdB) { int err; long nleft, nright; long min, max; snd_mixer_t *handle; snd_mixer_selem_id_t *sid; snd_mixer_elem_t* elem; if ((err = snd_mixer_open(&handle, 0)) < 0) { LOG_ERROR("open error: %s", snd_strerror(err)); return; } if ((err = snd_mixer_attach(handle, device)) < 0) { LOG_ERROR("attach error: %s", snd_strerror(err)); snd_mixer_close(handle); return; } if ((err = snd_mixer_selem_register(handle, NULL, NULL)) < 0) { LOG_ERROR("register error: %s", snd_strerror(err)); snd_mixer_close(handle); return; } if ((err = snd_mixer_load(handle)) < 0) { LOG_ERROR("load error: %s", snd_strerror(err)); snd_mixer_close(handle); return; } snd_mixer_selem_id_alloca(&sid); snd_mixer_selem_id_set_index(sid, mixer_index); snd_mixer_selem_id_set_name(sid, mixer); if ((elem = snd_mixer_find_selem(handle, sid)) == NULL) { LOG_ERROR("error find selem %s", mixer); snd_mixer_close(handle); return; } if (snd_mixer_selem_has_playback_switch(elem)) { snd_mixer_selem_set_playback_switch_all(elem, 1); // unmute } err = snd_mixer_selem_get_playback_dB_range(elem, &min, &max); if (err < 0 || max - min < 1000) { // unable to get db range or range is less than 10dB - ignore and set using raw values if ((err = snd_mixer_selem_get_playback_volume_range(elem, &min, &max)) < 0) { LOG_ERROR("unable to get volume raw range"); } else { long lraw, rraw; if (setmax) { lraw = rraw = max; } else { lraw = ((ldB > -MINVOL_DB ? MINVOL_DB + floor(ldB) : 0) / MINVOL_DB * (max-min)) + min; rraw = ((rdB > -MINVOL_DB ? MINVOL_DB + floor(rdB) : 0) / MINVOL_DB * (max-min)) + min; } LOG_DEBUG("setting vol raw [%ld..%ld]", min, max); if ((err = snd_mixer_selem_set_playback_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, lraw)) < 0) { LOG_ERROR("error setting left volume: %s", snd_strerror(err)); } if ((err = snd_mixer_selem_set_playback_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, rraw)) < 0) { LOG_ERROR("error setting right volume: %s", snd_strerror(err)); } } } else { // set db directly LOG_DEBUG("setting vol dB [%ld..%ld]", min, max); if (setmax) { // set to 0dB if available as this should be max volume for music recored at max pcm values if (max >= 0 && min <= 0) { ldB = rdB = 0; } else { ldB = rdB = max; } } if ((err = snd_mixer_selem_set_playback_dB(elem, SND_MIXER_SCHN_FRONT_LEFT, 100 * ldB, 1)) < 0) { LOG_ERROR("error setting left volume: %s", snd_strerror(err)); } if ((err = snd_mixer_selem_set_playback_dB(elem, SND_MIXER_SCHN_FRONT_RIGHT, 100 * rdB, 1)) < 0) { LOG_ERROR("error setting right volume: %s", snd_strerror(err)); } } if ((err = snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, &nleft)) < 0) { LOG_ERROR("error getting left vol: %s", snd_strerror(err)); } if ((err = snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, &nright)) < 0) { LOG_ERROR("error getting right vol: %s", snd_strerror(err)); } LOG_DEBUG("%s left: %3.1fdB -> %ld right: %3.1fdB -> %ld", mixer, ldB, nleft, rdB, nright); snd_mixer_close(handle); }
/** * Initializes the alsa system by getting the cards * and channels and setting the io watch for external * volume changes. * * @return 0 on success otherwise negative error code */ static int alsaset(void) { char *card_name; char *channel; // update list of available cards DEBUG_PRINT("Getting available cards..."); get_cards(); assert(cards != NULL); // get selected card assert(active_card == NULL); card_name = get_selected_card(); DEBUG_PRINT("Selected card: %s", card_name); if (card_name) { active_card = find_card(card_name); g_free(card_name); } // if not available, use the default card if (!active_card) { DEBUG_PRINT("Using default soundcard"); active_card = cards->data; } // If no playable channels, iterate on card list until a valid card is // found. // In most situations, the first card of the list (which is the // Alsa default card) can be opened. // However, in some situations the default card may be unavailable. // For example, when it's an USB DAC, and it's disconnected. if (!active_card->channels) { GSList *item; DEBUG_PRINT("Card '%s' has no playable channels, iterating on card list", active_card->dev); for (item = cards; item; item = item->next) { active_card = item->data; if (active_card->channels) break; } assert(item != NULL); } // open card DEBUG_PRINT("Opening card '%s'...", active_card->dev); smixer_options.device = active_card->dev; handle = open_mixer(active_card->dev, &smixer_options, smixer_level); assert(handle != NULL); // Set the channel // We have to handle the fact that the channel name defined // in PNMixer configuration is not necessarily valid. // For example, this may happen when the default alsa soundcard // is modified. The channel names of the new default card may // not match the channel names of the previous default card. assert(elem == NULL); channel = get_selected_channel(active_card->name); if (channel) { snd_mixer_selem_id_t *sid; snd_mixer_selem_id_alloca(&sid); snd_mixer_selem_id_set_name(sid, channel); elem = snd_mixer_find_selem(handle, sid); g_free(channel); } if (elem == NULL) { elem = snd_mixer_first_elem(handle); } assert(elem != NULL); // Set callback DEBUG_PRINT("Using channel '%s'", snd_mixer_selem_get_name(elem)); snd_mixer_elem_set_callback(elem, alsa_cb); // set watch for volume changes set_io_watch(handle); return 0; }
static int init(int argc, char **argv) { const char *str; int value; int hardware_mixer = 0; config.audio_backend_latency_offset = 0; // this is the default for ALSA config.audio_backend_buffer_desired_length = 6615; // default for alsa with a software mixer // get settings from settings file first, allow them to be overridden by command line options if (config.cfg != NULL) { /* Get the desired buffer size setting. */ if (config_lookup_int(config.cfg, "alsa.audio_backend_buffer_desired_length_software", &value)) { if ((value < 0) || (value > 66150)) die("Invalid alsa audio backend buffer desired length (software) \"%d\". It should be between 0 and " "66150, default is 6615", value); else { config.audio_backend_buffer_desired_length = value; } } /* Get the latency offset. */ if (config_lookup_int(config.cfg, "alsa.audio_backend_latency_offset", &value)) { if ((value < -66150) || (value > 66150)) die("Invalid alsa audio backend buffer latency offset \"%d\". It should be between -66150 and +66150, default is 0", value); else config.audio_backend_latency_offset = value; } /* Get the Output Device Name. */ if (config_lookup_string(config.cfg, "alsa.output_device", &str)) { alsa_out_dev = (char *)str; } /* Get the Mixer Type setting. */ if (config_lookup_string(config.cfg, "alsa.mixer_type", &str)) { inform("The alsa mixer_type setting is deprecated and has been ignored. FYI, using the \"mixer_control_name\" setting automatically chooses a hardware mixer."); } /* Get the Mixer Device Name. */ if (config_lookup_string(config.cfg, "alsa.mixer_device", &str)) { alsa_mix_dev = (char *)str; } /* Get the Mixer Control Name. */ if (config_lookup_string(config.cfg, "alsa.mixer_control_name", &str)) { alsa_mix_ctrl = (char *)str; hardware_mixer = 1; } } optind = 1; // optind=0 is equivalent to optind=1 plus special behaviour argv--; // so we shift the arguments to satisfy getopt() argc++; // some platforms apparently require optreset = 1; - which? int opt; while ((opt = getopt(argc, argv, "d:t:m:c:i:")) > 0) { switch (opt) { case 'd': alsa_out_dev = optarg; break; case 't': inform("The alsa backend -t option is deprecated and has been ignored. FYI, using the -c option automatically chooses a hardware mixer."); break; case 'm': alsa_mix_dev = optarg; break; case 'c': alsa_mix_ctrl = optarg; hardware_mixer = 1; break; case 'i': alsa_mix_index = strtol(optarg, NULL, 10); break; default: help(); die("Invalid audio option -%c specified", opt); } } if (optind < argc) die("Invalid audio argument: %s", argv[optind]); debug(1,"Output device name is \"%s\".",alsa_out_dev); if (!hardware_mixer) return 0; if (alsa_mix_dev == NULL) alsa_mix_dev = alsa_out_dev; int ret = 0; snd_mixer_selem_id_alloca(&alsa_mix_sid); snd_mixer_selem_id_set_index(alsa_mix_sid, alsa_mix_index); snd_mixer_selem_id_set_name(alsa_mix_sid, alsa_mix_ctrl); if ((snd_mixer_open(&alsa_mix_handle, 0)) < 0) die("Failed to open mixer"); debug(1,"Mixer device name is \"%s\".",alsa_mix_dev); if ((snd_mixer_attach(alsa_mix_handle, alsa_mix_dev)) < 0) die("Failed to attach mixer"); if ((snd_mixer_selem_register(alsa_mix_handle, NULL, NULL)) < 0) die("Failed to register mixer element"); ret = snd_mixer_load(alsa_mix_handle); if (ret < 0) die("Failed to load mixer element"); debug(1,"Mixer Control name is \"%s\".",alsa_mix_ctrl); alsa_mix_elem = snd_mixer_find_selem(alsa_mix_handle, alsa_mix_sid); if (!alsa_mix_elem) die("Failed to find mixer element"); if (snd_mixer_selem_get_playback_volume_range(alsa_mix_elem, &alsa_mix_minv, &alsa_mix_maxv) < 0) debug(1, "Can't read mixer's [linear] min and max volumes."); else { if (snd_mixer_selem_get_playback_dB_range (alsa_mix_elem, &alsa_mix_mindb, &alsa_mix_maxdb) == 0) { audio_alsa.volume = &volume; // insert the volume function now we know it can do dB stuff audio_alsa.parameters = ¶meters; // likewise the parameters stuff if (alsa_mix_mindb == SND_CTL_TLV_DB_GAIN_MUTE) { // Raspberry Pi does this debug(1, "Lowest dB value is a mute."); if (snd_mixer_selem_ask_playback_vol_dB(alsa_mix_elem, alsa_mix_minv + 1, &alsa_mix_mindb) == 0) debug(1, "Can't get dB value corresponding to a \"volume\" of 1."); } debug(1, "Hardware mixer has dB volume from %f to %f.", (1.0 * alsa_mix_mindb) / 100.0, (1.0 * alsa_mix_maxdb) / 100.0); if (config.volume_range_db) { long suggested_alsa_min_db = alsa_mix_maxdb - (long)trunc(config.volume_range_db*100); if (suggested_alsa_min_db > alsa_mix_mindb) alsa_mix_mindb = suggested_alsa_min_db; else inform("The volume_range_db setting, %f is greater than the native range of the mixer %f, so it is ignored.",config.volume_range_db,(alsa_mix_maxdb-alsa_mix_mindb)/100.0); }else{ if(config.isset_volume_min_db){ long suggested_alsa_min_db = (long) trunc(config.volume_min_db * 100); if(suggested_alsa_min_db > alsa_mix_mindb){ alsa_mix_mindb = suggested_alsa_min_db; } } if(config.isset_volume_max_db){ long suggested_alsa_max_db = (long) trunc(config.volume_max_db * 100); if(suggested_alsa_max_db < alsa_mix_maxdb){ alsa_mix_maxdb = suggested_alsa_max_db; } } } } else { // use the linear scale and do the db conversion ourselves debug(1, "note: the hardware mixer specified -- \"%s\" -- does not have a dB volume scale, so it can't be used.",alsa_mix_ctrl); /* debug(1, "Min and max volumes are %d and %d.",alsa_mix_minv,alsa_mix_maxv); alsa_mix_maxdb = 0; if ((alsa_mix_maxv!=0) && (alsa_mix_minv!=0)) alsa_mix_mindb = -20*100*(log10(alsa_mix_maxv*1.0)-log10(alsa_mix_minv*1.0)); else if (alsa_mix_maxv!=0) alsa_mix_mindb = -20*100*log10(alsa_mix_maxv*1.0); audio_alsa.volume = &linear_volume; // insert the linear volume function audio_alsa.parameters = ¶meters; // likewise the parameters stuff debug(1,"Max and min dB calculated are %d and %d.",alsa_mix_maxdb,alsa_mix_mindb); */ } } if (snd_mixer_selem_has_playback_switch(alsa_mix_elem)) { has_mute = 1; debug(1, "Has mute ability."); } return 0; }
/* to set/get/query special features/parameters */ static int control(int cmd, void *arg) { switch(cmd) { case AOCONTROL_QUERY_FORMAT: return CONTROL_TRUE; case AOCONTROL_GET_VOLUME: case AOCONTROL_SET_VOLUME: { ao_control_vol_t *vol = (ao_control_vol_t *)arg; int err; snd_mixer_t *handle; snd_mixer_elem_t *elem; snd_mixer_selem_id_t *sid; char *mix_name = "PCM"; char *card = "default"; int mix_index = 0; long pmin, pmax; long get_vol, set_vol; float f_multi; if(AF_FORMAT_IS_IEC61937(ao_data.format)) return CONTROL_TRUE; if(mixer_channel) { char *test_mix_index; mix_name = strdup(mixer_channel); if ((test_mix_index = strchr(mix_name, ','))){ *test_mix_index = 0; test_mix_index++; mix_index = strtol(test_mix_index, &test_mix_index, 0); if (*test_mix_index){ mp_msg(MSGT_AO,MSGL_ERR, MSGTR_AO_ALSA_InvalidMixerIndexDefaultingToZero); mix_index = 0 ; } } } if(mixer_device) card = mixer_device; //allocate simple id snd_mixer_selem_id_alloca(&sid); //sets simple-mixer index and name snd_mixer_selem_id_set_index(sid, mix_index); snd_mixer_selem_id_set_name(sid, mix_name); if (mixer_channel) { free(mix_name); mix_name = NULL; } if ((err = snd_mixer_open(&handle, 0)) < 0) { mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_MixerOpenError, snd_strerror(err)); return CONTROL_ERROR; } if ((err = snd_mixer_attach(handle, card)) < 0) { mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_MixerAttachError, card, snd_strerror(err)); snd_mixer_close(handle); return CONTROL_ERROR; } if ((err = snd_mixer_selem_register(handle, NULL, NULL)) < 0) { mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_MixerRegisterError, snd_strerror(err)); snd_mixer_close(handle); return CONTROL_ERROR; } err = snd_mixer_load(handle); if (err < 0) { mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_MixerLoadError, snd_strerror(err)); snd_mixer_close(handle); return CONTROL_ERROR; } elem = snd_mixer_find_selem(handle, sid); if (!elem) { mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToFindSimpleControl, snd_mixer_selem_id_get_name(sid), snd_mixer_selem_id_get_index(sid)); snd_mixer_close(handle); return CONTROL_ERROR; } snd_mixer_selem_get_playback_volume_range(elem,&pmin,&pmax); f_multi = (100 / (float)(pmax - pmin)); if (cmd == AOCONTROL_SET_VOLUME) { set_vol = vol->left / f_multi + pmin + 0.5; //setting channels if ((err = snd_mixer_selem_set_playback_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, set_vol)) < 0) { mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_ErrorSettingLeftChannel, snd_strerror(err)); snd_mixer_close(handle); return CONTROL_ERROR; } mp_msg(MSGT_AO,MSGL_DBG2,"left=%li, ", set_vol); set_vol = vol->right / f_multi + pmin + 0.5; if ((err = snd_mixer_selem_set_playback_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, set_vol)) < 0) { mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_ErrorSettingRightChannel, snd_strerror(err)); snd_mixer_close(handle); return CONTROL_ERROR; } mp_msg(MSGT_AO,MSGL_DBG2,"right=%li, pmin=%li, pmax=%li, mult=%f\n", set_vol, pmin, pmax, f_multi); if (snd_mixer_selem_has_playback_switch(elem)) { int lmute = (vol->left == 0.0); int rmute = (vol->right == 0.0); if (snd_mixer_selem_has_playback_switch_joined(elem)) { lmute = rmute = lmute && rmute; } else { snd_mixer_selem_set_playback_switch(elem, SND_MIXER_SCHN_FRONT_RIGHT, !rmute); } snd_mixer_selem_set_playback_switch(elem, SND_MIXER_SCHN_FRONT_LEFT, !lmute); } } else { snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, &get_vol); vol->left = (get_vol - pmin) * f_multi; snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, &get_vol); vol->right = (get_vol - pmin) * f_multi; mp_msg(MSGT_AO,MSGL_DBG2,"left=%f, right=%f\n",vol->left,vol->right); } snd_mixer_close(handle); return CONTROL_OK; } } //end switch return CONTROL_UNKNOWN; }
ALSAMixer::ALSAMixer(const char *sndcard) { int err; LOGI(" Init ALSAMIXER for SNDCARD : %s",sndcard); mixerMasterProp = ALSA_PROP(AudioSystem::DEVICE_OUT_ALL, "master", "PCM", "Capture"); mixerProp = { ALSA_PROP(AudioSystem::DEVICE_OUT_EARPIECE, "earpiece", "Earpiece", "Capture"), ALSA_PROP(AudioSystem::DEVICE_OUT_SPEAKER, "speaker", "Speaker", ""), ALSA_PROP(AudioSystem::DEVICE_OUT_WIRED_HEADSET, "headset", "Headphone", "Capture"), ALSA_PROP(AudioSystem::DEVICE_OUT_BLUETOOTH_SCO, "bluetooth.sco", "Bluetooth", "Bluetooth Capture"), ALSA_PROP(AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP, "bluetooth.a2dp", "Bluetooth A2DP", "Bluetooth A2DP Capture"), ALSA_PROP(static_cast<AudioSystem::audio_devices>(0), "", NULL, NULL) }; initMixer (&mMixer[SND_PCM_STREAM_PLAYBACK], sndcard); initMixer (&mMixer[SND_PCM_STREAM_CAPTURE], sndcard); snd_mixer_selem_id_t *sid; snd_mixer_selem_id_alloca(&sid); for (int i = 0; i <= SND_PCM_STREAM_LAST; i++) { mixer_info_t *info = mixerMasterProp[i].mInfo = new mixer_info_t; property_get (mixerMasterProp[i].propName, info->name, mixerMasterProp[i].propDefault); for (snd_mixer_elem_t *elem = snd_mixer_first_elem(mMixer[i]); elem; elem = snd_mixer_elem_next(elem)) { if (!snd_mixer_selem_is_active(elem)) continue; snd_mixer_selem_get_id(elem, sid); // Find PCM playback volume control element. const char *elementName = snd_mixer_selem_id_get_name(sid); if (info->elem == NULL && strcmp(elementName, info->name) == 0 && hasVolume[i] (elem)) { info->elem = elem; getVolumeRange[i] (elem, &info->min, &info->max); info->volume = info->max; setVol[i] (elem, info->volume); if (i == SND_PCM_STREAM_PLAYBACK && snd_mixer_selem_has_playback_switch (elem)) snd_mixer_selem_set_playback_switch_all (elem, 1); break; } } LOGV("Mixer: master '%s' %s.", info->name, info->elem ? "found" : "not found"); for (int j = 0; mixerProp[j][i].device; j++) { mixer_info_t *info = mixerProp[j][i].mInfo = new mixer_info_t; property_get (mixerProp[j][i].propName, info->name, mixerProp[j][i].propDefault); for (snd_mixer_elem_t *elem = snd_mixer_first_elem(mMixer[i]); elem; elem = snd_mixer_elem_next(elem)) { if (!snd_mixer_selem_is_active(elem)) continue; snd_mixer_selem_get_id(elem, sid); // Find PCM playback volume control element. const char *elementName = snd_mixer_selem_id_get_name(sid); if (info->elem == NULL && strcmp(elementName, info->name) == 0 && hasVolume[i] (elem)) { info->elem = elem; getVolumeRange[i] (elem, &info->min, &info->max); info->volume = info->max; setVol[i] (elem, info->volume); if (i == SND_PCM_STREAM_PLAYBACK && snd_mixer_selem_has_playback_switch (elem)) snd_mixer_selem_set_playback_switch_all (elem, 1); break; } } LOGV("Mixer: route '%s' %s.", info->name, info->elem ? "found" : "not found"); } } LOGV("mixer initialized."); }
static int setlevels(const char *dev) { snd_mixer_t *handle; snd_mixer_selem_id_t *sid; snd_mixer_elem_t *elem; int err; char *mixer_dev, *c; /* Basically we just want to turn on Mic capture. */ if ((err = snd_mixer_open(&handle, 0)) < 0) { fprintf(stderr, "Mixer open failed: %s\n", snd_strerror(err)); return -1; } mixer_dev = strdup(dev); if (strncmp(mixer_dev, "plug", 4) == 0) memmove(mixer_dev, mixer_dev + 4, strlen(mixer_dev) - 4 + 1); if ((c = strchr(mixer_dev, ','))) *c = '\0'; if ((err = snd_mixer_attach(handle, mixer_dev)) < 0) { fprintf(stderr, "Mixer attach to %s failed: %s\n", mixer_dev, snd_strerror(err)); free(mixer_dev); snd_mixer_close(handle); return -1; } free(mixer_dev); if ((err = snd_mixer_selem_register(handle, NULL, NULL)) < 0) { fprintf(stderr, "Mixer register failed: %s\n", snd_strerror(err)); snd_mixer_close(handle); return -1; } if ((err = snd_mixer_load(handle)) < 0) { fprintf(stderr, "Mixer load failed: %s\n", snd_strerror(err)); snd_mixer_close(handle); return -1; } snd_mixer_selem_id_alloca(&sid); snd_mixer_selem_id_set_name(sid, "Mic"); if ((elem = snd_mixer_find_selem(handle, sid)) == NULL) { fprintf(stderr, "Warning: Could not find Mic element\n"); } else { if ((err = snd_mixer_selem_set_capture_switch_all(elem, 1)) < 0) { fprintf(stderr, "Failed to enable microphone capture: %s\n", snd_strerror(err)); snd_mixer_close(handle); return -1; } } snd_mixer_selem_id_set_name(sid, "Capture"); if ((elem = snd_mixer_find_selem(handle, sid)) == NULL) { fprintf(stderr, "Warning: Could not find Capture element\n"); } else { if ((err = snd_mixer_selem_set_capture_switch_all(elem, 1)) < 0) { fprintf(stderr, "Failed to enable microphone capture: %s\n", snd_strerror(err)); snd_mixer_close(handle); return -1; } } return 0; }
static int init(int argc, char **argv) { int hardware_mixer = 0; optind = 1; // optind=0 is equivalent to optind=1 plus special behaviour argv--; // so we shift the arguments to satisfy getopt() argc++; // some platforms apparently require optreset = 1; - which? int opt; while ((opt = getopt(argc, argv, "d:t:m:c:i:")) > 0) { switch (opt) { case 'd': alsa_out_dev = optarg; break; case 't': if (strcmp(optarg, "hardware") == 0) hardware_mixer = 1; break; case 'm': alsa_mix_dev = optarg; break; case 'c': alsa_mix_ctrl = optarg; break; case 'i': alsa_mix_index = strtol(optarg, NULL, 10); break; default: help(); die("Invalid audio option -%c specified", opt); } } if (optind < argc) die("Invalid audio argument: %s", argv[optind]); if (!hardware_mixer) return 0; if (alsa_mix_dev == NULL) alsa_mix_dev = alsa_out_dev; audio_alsa.volume = &volume; int ret = 0; long alsa_mix_maxv; snd_mixer_selem_id_alloca(&alsa_mix_sid); snd_mixer_selem_id_set_index(alsa_mix_sid, alsa_mix_index); snd_mixer_selem_id_set_name(alsa_mix_sid, alsa_mix_ctrl); if ((snd_mixer_open(&alsa_mix_handle, 0)) < 0) die ("Failed to open mixer"); if ((snd_mixer_attach(alsa_mix_handle, alsa_mix_dev)) < 0) die ("Failed to attach mixer"); if ((snd_mixer_selem_register(alsa_mix_handle, NULL, NULL)) < 0) die ("Failed to register mixer element"); ret = snd_mixer_load(alsa_mix_handle); if (ret < 0) die ("Failed to load mixer element"); alsa_mix_elem = snd_mixer_find_selem(alsa_mix_handle, alsa_mix_sid); if (!alsa_mix_elem) die ("Failed to find mixer element"); snd_mixer_selem_get_playback_volume_range (alsa_mix_elem, &alsa_mix_minv, &alsa_mix_maxv); alsa_mix_range = alsa_mix_maxv - alsa_mix_minv; return 0; }
static int mixer_open(void) { snd_mixer_elem_t *elem; snd_mixer_elem_t *master; snd_mixer_elem_t *pcm; snd_mixer_elem_t *custom; snd_mixer_selem_id_t *sid; int ret; ret = snd_mixer_open(&mixer_hdl, 0); if (ret < 0) { DPRINTF(E_LOG, L_LAUDIO, "Failed to open mixer: %s\n", snd_strerror(ret)); mixer_hdl = NULL; return -1; } ret = snd_mixer_attach(mixer_hdl, card_name); if (ret < 0) { DPRINTF(E_LOG, L_LAUDIO, "Failed to attach mixer: %s\n", snd_strerror(ret)); goto out_close; } ret = snd_mixer_selem_register(mixer_hdl, NULL, NULL); if (ret < 0) { DPRINTF(E_LOG, L_LAUDIO, "Failed to register mixer: %s\n", snd_strerror(ret)); goto out_detach; } ret = snd_mixer_load(mixer_hdl); if (ret < 0) { DPRINTF(E_LOG, L_LAUDIO, "Failed to load mixer: %s\n", snd_strerror(ret)); goto out_detach; } /* Grab interesting elements */ snd_mixer_selem_id_alloca(&sid); pcm = NULL; master = NULL; custom = NULL; for (elem = snd_mixer_first_elem(mixer_hdl); elem; elem = snd_mixer_elem_next(elem)) { snd_mixer_selem_get_id(elem, sid); if (mixer_name && (strcmp(snd_mixer_selem_id_get_name(sid), mixer_name) == 0)) { custom = elem; break; } else if (strcmp(snd_mixer_selem_id_get_name(sid), "PCM") == 0) pcm = elem; else if (strcmp(snd_mixer_selem_id_get_name(sid), "Master") == 0) master = elem; } if (mixer_name) { if (custom) vol_elem = custom; else { DPRINTF(E_LOG, L_LAUDIO, "Failed to open configured mixer element '%s'\n", mixer_name); goto out_detach; } } else if (pcm) vol_elem = pcm; else if (master) vol_elem = master; else { DPRINTF(E_LOG, L_LAUDIO, "Failed to open PCM or Master mixer element\n"); goto out_detach; } /* Get min & max volume */ snd_mixer_selem_get_playback_volume_range(vol_elem, &vol_min, &vol_max); return 0; out_detach: snd_mixer_detach(mixer_hdl, card_name); out_close: snd_mixer_close(mixer_hdl); mixer_hdl = NULL; vol_elem = NULL; return -1; }
int indicator_music_init(Indicator *indicator) { DBusMessage* msg; DBusMessageIter args, element; DBusPendingCall* pending; char *player; int current_type; if(!dbus.session.connection) return -1; if(!(msg=dbus_message_new_method_call(dbus.interface, dbus.object, dbus.interface, "ListNames"))) return -1; if(!dbus_connection_send_with_reply(dbus.session.connection, msg, &pending, -1)) { dbus_connection_unref(dbus.session.connection); return -1; } if(!pending) { dbus_message_unref(msg); dbus_connection_unref(dbus.session.connection); return -1; } dbus_connection_flush(dbus.session.connection); dbus_message_unref(msg); dbus_pending_call_block(pending); if(!(msg=dbus_pending_call_steal_reply(pending))) { dbus_connection_unref(dbus.session.connection); return -1; } dbus_pending_call_unref(pending); if(!(dbus_message_iter_init(msg, &args)&&dbus_message_iter_get_arg_type(&args)==DBUS_TYPE_ARRAY)) { dbus_message_unref(msg); dbus_connection_unref(dbus.session.connection); return -1; } for(dbus_message_iter_recurse(&args, &element); (current_type=dbus_message_iter_get_arg_type(&element))!=DBUS_TYPE_INVALID; dbus_message_iter_next(&element)) { if(current_type!=DBUS_TYPE_STRING) continue; dbus_message_iter_get_basic(&element, &player); if(!strncmp(player, mpris.base, strlen(mpris.base))) { mediaplayer_register(player+strlen(mpris.base)); } } dbus_message_unref(msg); dbus_bus_add_match(dbus.session.connection, "type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged'", &dbus.session.error); dbus_connection_flush(dbus.session.connection); if(dbus_error_is_set(&dbus.session.error)) { dbus_connection_unref(dbus.session.connection); mediaplayer_deregister_all(); return -1; } /*init alsa*/ snd_mixer_selem_id_alloca(&alsa.sid); snd_mixer_selem_id_set_index(alsa.sid, alsa.mix_index); snd_mixer_selem_id_set_name(alsa.sid, alsa.mix_name); if((snd_mixer_open(&alsa.handle, 0))<0) return -1; if((snd_mixer_attach(alsa.handle, alsa.card))<0) { snd_mixer_close(alsa.handle); return -1; } if((snd_mixer_selem_register(alsa.handle, NULL, NULL))<0) { snd_mixer_close(alsa.handle); return -1; } if(snd_mixer_load(alsa.handle)<0) { snd_mixer_close(alsa.handle); return -1; } if(!(alsa.elem=snd_mixer_find_selem(alsa.handle, alsa.sid))) { snd_mixer_close(alsa.handle); return -1; } snd_mixer_selem_get_playback_volume_range (alsa.elem, &alsa.minv, &alsa.maxv); return 0; }
ALSAMixer::ALSAMixer() { int err; initMixer (&mMixer[SND_PCM_STREAM_PLAYBACK], "AndroidOut"); initMixer (&mMixer[SND_PCM_STREAM_CAPTURE], "AndroidIn"); snd_mixer_selem_id_t *sid; snd_mixer_selem_id_alloca(&sid); for (int i = 0; i <= SND_PCM_STREAM_LAST; i++) { mixer_info_t *info = mixerMasterProp[i].mInfo = new mixer_info_t; property_get (mixerMasterProp[i].propName, info->name, mixerMasterProp[i].propDefault); for (snd_mixer_elem_t *elem = snd_mixer_first_elem(mMixer[i]); elem; elem = snd_mixer_elem_next(elem)) { if (!snd_mixer_selem_is_active(elem)) continue; snd_mixer_selem_get_id(elem, sid); // Find PCM playback volume control element. const char *elementName = snd_mixer_selem_id_get_name(sid); if (info->elem == NULL && strcmp(elementName, info->name) == 0 && hasVolume[i] (elem)) { info->elem = elem; getVolumeRange[i] (elem, &info->min, &info->max); info->volume = info->max; setVol[i] (elem, info->volume); if (i == SND_PCM_STREAM_PLAYBACK && snd_mixer_selem_has_playback_switch (elem)) snd_mixer_selem_set_playback_switch_all (elem, 1); break; } } LOGV("Mixer: master '%s' %s.", info->name, info->elem ? "found" : "not found"); for (int j = 0; mixerProp[j][i].device; j++) { mixer_info_t *info = mixerProp[j][i].mInfo = new mixer_info_t; property_get (mixerProp[j][i].propName, info->name, mixerProp[j][i].propDefault); for (snd_mixer_elem_t *elem = snd_mixer_first_elem(mMixer[i]); elem; elem = snd_mixer_elem_next(elem)) { if (!snd_mixer_selem_is_active(elem)) continue; snd_mixer_selem_get_id(elem, sid); // Find PCM playback volume control element. const char *elementName = snd_mixer_selem_id_get_name(sid); if (info->elem == NULL && strcmp(elementName, info->name) == 0 && hasVolume[i] (elem)) { info->elem = elem; getVolumeRange[i] (elem, &info->min, &info->max); info->volume = info->max; setVol[i] (elem, info->volume); if (i == SND_PCM_STREAM_PLAYBACK && snd_mixer_selem_has_playback_switch (elem)) snd_mixer_selem_set_playback_switch_all (elem, 1); break; } } LOGV("Mixer: route '%s' %s.", info->name, info->elem ? "found" : "not found"); } } #ifdef AUDIO_MODEM_TI ALSAControl control("hw:00"); status_t error; #if 0//(WORKAROUND_AVOID_VOICE_VOLUME_SATURATION == 1) //[LG_FW_P970_MERGE] - jungsoo1221.lee LOGV("Workaround: Voice call max volume limited to: %d", WORKAROUND_MAX_VOICE_VOLUME); #endif #if 0//(WORKAROUND_SET_VOICE_VOLUME_MIN == 1) //[LG_FW_P970_MERGE] - jungsoo1221.lee LOGV("Workaround: Voice call min volume limited to: %d", WORKAROUND_MIN_VOICE_VOLUME); #endif for (int i = 0; inCallVolumeProp[i].device; i++) { mixer_incall_vol_info_t *info = inCallVolumeProp[i].mInfo = new mixer_incall_vol_info_t; property_get (inCallVolumeProp[i].propName, info->name, inCallVolumeProp[i].propDefault); error = control.get(info->name, info->volume, 0); error = control.getmin(info->name, info->min); error = control.getmax(info->name, info->max); #if 0//(WORKAROUND_AVOID_VOICE_VOLUME_SATURATION == 1) //[LG_FW_P970_MERGE] - jungsoo1221.lee info->max = WORKAROUND_MAX_VOICE_VOLUME; #endif #if 0//(WORKAROUND_SET_VOICE_VOLUME_MIN == 1) //[LG_FW_P970_MERGE] - jungsoo1221.lee info->min = WORKAROUND_MIN_VOICE_VOLUME; #endif if (error < 0) { LOGV("Mixer: In Call Volume '%s': not found", info->name); } else { LOGV("Mixer: In Call Volume '%s': vol. %d min. %d max. %d", info->name, info->volume, info->min, info->max); } } #endif LOGV("mixer initialized."); }
bool AlsaMixer::StepVolume( const char* channel, int step ) { LOG( Logger::LOG_DEBUG, "AlsaMixer::StepVolume( %s, %d )", channel, step); bool success = false; snd_mixer_elem_t *elem; snd_mixer_selem_id_t *sid; snd_mixer_selem_id_alloca(&sid); snd_mixer_selem_id_set_index( sid, 0); snd_mixer_selem_id_set_name( sid, channel); elem = snd_mixer_find_selem(_handle, sid); if (!elem) { LOG( Logger::LOG_ERROR, "Unable to find simple control '%s',%i\n", snd_mixer_selem_id_get_name(sid), snd_mixer_selem_id_get_index(sid)); goto end; } if( snd_mixer_selem_has_playback_volume(elem) ) { long min, max; if( snd_mixer_selem_get_playback_volume_range(elem, &min, &max) < 0 ) { LOG( Logger::LOG_ERROR, "Unable to get playback volume range for control '%s',%i\n", snd_mixer_selem_id_get_name(sid), snd_mixer_selem_id_get_index(sid)); goto end; } for (int chn = 0; chn <= SND_MIXER_SCHN_LAST; chn++) { long volume; if (snd_mixer_selem_get_playback_volume(elem, (snd_mixer_selem_channel_id_t)chn, &volume ) >= 0) { volume += step; if( volume > max )volume = max; if( volume < min )volume = min; if (snd_mixer_selem_set_playback_volume(elem, (snd_mixer_selem_channel_id_t)chn, volume ) >= 0) { success = true; } } } }else if( snd_mixer_selem_has_capture_volume(elem) ) { long min, max; if( snd_mixer_selem_get_capture_volume_range(elem, &min, &max) < 0 ) { LOG( Logger::LOG_ERROR, "Unable to get capture volume range for control '%s',%i\n", snd_mixer_selem_id_get_name(sid), snd_mixer_selem_id_get_index(sid)); goto end; } for (int chn = 0; chn <= SND_MIXER_SCHN_LAST; chn++) { long volume; if (snd_mixer_selem_get_capture_volume(elem, (snd_mixer_selem_channel_id_t)chn, &volume ) >= 0) { volume += step; if( volume > max )volume = max; if( volume < min )volume = min; if (snd_mixer_selem_set_capture_volume(elem, (snd_mixer_selem_channel_id_t)chn, volume ) >= 0) { success = true; } } } } if( !success ) { LOG( Logger::LOG_ERROR, "Error setting control '%s',%i\n", snd_mixer_selem_id_get_name(sid), snd_mixer_selem_id_get_index(sid)); } end: return success; }
void toggleMute(struct playerHandles *ph, int *mute){ long val; int err; float range_p; long current=*mute; long vmin,vmax; char tail[OUTPUT_TAIL_SIZE]; snd_mixer_t *handle; snd_mixer_elem_t *elem; snd_mixer_selem_id_t *sid; snd_mixer_selem_id_alloca(&sid); snd_mixer_selem_id_set_index(sid,0); snd_mixer_selem_id_set_name(sid,"PCM"); if(snd_mixer_open(&handle,0)<0) return; if(snd_mixer_attach(handle,"default")<0){ snd_mixer_close(handle); return; } if(snd_mixer_selem_register(handle,NULL,NULL)<0){ snd_mixer_close(handle); return; } if(snd_mixer_load(handle)<0){ snd_mixer_close(handle); return; } elem=snd_mixer_find_selem(handle,sid); if(!elem){ snd_mixer_close(handle); return; } snd_mixer_selem_get_playback_volume_range(elem,&vmin,&vmax); range_p = (100.0f/(float)(vmax-vmin)); if(*mute>0){ // Unmute and perform volume change *mute=0; sprintf(tail,"Volume: %ld%%",current); addStatusTail(tail,ph->outdetail); current=current/range_p+vmin; } else{ // Mute snd_mixer_selem_get_playback_volume(elem,0,¤t); *mute=current*range_p+vmin; current=0; addStatusTail("Volume Muted",ph->outdetail); } fflush(stdout); if(snd_mixer_selem_set_playback_volume(elem,0,current)<0){ snd_mixer_close(handle); return; } if(snd_mixer_selem_set_playback_volume(elem,1,current)<0){ snd_mixer_close(handle); return; } snd_mixer_close(handle); }
int main(int argc, char * argv[]) { int pos = 125; long min, max; long gpiodelay_value = 250; // was 50 snd_mixer_t *handle; snd_mixer_selem_id_t *sid; const char *card = "default"; // Previous linux driver's mixer name // const char *selem_name = "Playback Digital"; // const char *selem_name = "PCM"; const char *selem_name = "Digital"; // Linux 4.1.6-v7+ #810 int x, mute_state; long i, currentVolume; printf("IQaudIO.com Pi-DAC Volume Control support Rotary Encoder) v1.5 Aug 30th 2015\n\n"); wiringPiSetup (); /* pull up is needed as encoder common is grounded */ pinMode (ENCODER_A, INPUT); pullUpDnControl (ENCODER_A, PUD_UP); pinMode (ENCODER_B, INPUT); pullUpDnControl (ENCODER_B, PUD_UP); encoderPos = pos; // Setup ALSA access snd_mixer_open(&handle, 0); snd_mixer_attach(handle, card); snd_mixer_selem_register(handle, NULL, NULL); snd_mixer_load(handle); snd_mixer_selem_id_alloca(&sid); snd_mixer_selem_id_set_index(sid, 0); snd_mixer_selem_id_set_name(sid, selem_name); snd_mixer_elem_t* elem = snd_mixer_find_selem(handle, sid); snd_mixer_selem_get_playback_volume_range(elem, &min, &max); if (DEBUG_PRINT) printf("Returned card VOLUME range - min: %ld, max: %ld\n", min, max); // Minimum given is mute, we need the first real value min++; // Get current volume if (x = snd_mixer_selem_get_playback_volume (elem, SND_MIXER_SCHN_FRONT_LEFT, ¤tVolume)) { printf("%d %s\n", x, snd_strerror(x)); } else if (DEBUG_PRINT) printf("Current ALSA volume LEFT: %ld\n", currentVolume); if (x = snd_mixer_selem_get_playback_volume (elem, SND_MIXER_SCHN_FRONT_RIGHT, ¤tVolume)) { printf("%d %s\n", x, snd_strerror(x)); } else if (DEBUG_PRINT) printf("Current ALSA volume RIGHT: %ld\n", currentVolume); /* monitor encoder level changes */ wiringPiISR (ENCODER_A, INT_EDGE_BOTH, &encoderPulse); wiringPiISR (ENCODER_B, INT_EDGE_BOTH, &encoderPulse); // Now sit and spin waiting for GPIO pins to go active... while (1) { if (encoderPos != pos) { // get current volume if (x = snd_mixer_selem_get_playback_volume (elem, SND_MIXER_SCHN_FRONT_LEFT, ¤tVolume)) { printf("%d %s\n", x, snd_strerror(x)); } else if (DEBUG_PRINT) printf(" Current ALSA volume: %ld\n", currentVolume); // Adjust for MUTE if (currentVolume < min) { currentVolume = 0; if (DEBUG_PRINT) printf(" Current ALSA volume set to min: %ld\n", currentVolume); } // What way did the encoder go? if (encoderPos > pos) { pos = encoderPos; currentVolume = currentVolume + 10; // Adjust for MAX volume if (currentVolume > max) currentVolume = max; if (DEBUG_PRINT) printf("Volume UP %d - %ld", pos, currentVolume); } else if (encoderPos < pos) { pos = encoderPos; currentVolume = currentVolume - 10; // Adjust for MUTE if (currentVolume < min) currentVolume = 0; if (DEBUG_PRINT) printf("Volume DOWN %d - %ld", pos, currentVolume); } if (x = snd_mixer_selem_set_playback_volume_all(elem, currentVolume)) { printf(" ERROR %d %s\n", x, snd_strerror(x)); } else if (DEBUG_PRINT) printf(" Volume successfully set to %ld using ALSA!\n", currentVolume); } // Check x times per second, MAY NEED TO ADJUST THS FREQUENCY FOR SOME ENCODERS */ delay(gpiodelay_value); /* check pos x times per second */ } // We never get here but should close the sockets etc. on exit. snd_mixer_close(handle); }
void changeVolume(struct playerHandles *ph, int mod){ long val; int err; float range_p; long cur_vol,new_volume; long vmin,vmax; char tail[OUTPUT_TAIL_SIZE]; snd_mixer_t *handle; snd_mixer_elem_t *elem; snd_mixer_selem_id_t *sid; snd_mixer_selem_id_alloca(&sid); snd_mixer_selem_id_set_index(sid,0); snd_mixer_selem_id_set_name(sid,"PCM"); if(snd_mixer_open(&handle,0)<0) return; if(snd_mixer_attach(handle,"default")<0){ snd_mixer_close(handle); return; } if(snd_mixer_selem_register(handle,NULL,NULL)<0){ snd_mixer_close(handle); return; } if(snd_mixer_load(handle)<0){ snd_mixer_close(handle); return; } elem=snd_mixer_find_selem(handle,sid); if(!elem){ snd_mixer_close(handle); return; } snd_mixer_selem_get_playback_volume_range(elem,&vmin,&vmax); range_p = (100.0f/(float)(vmax-vmin)); snd_mixer_selem_get_playback_volume(elem,0,&cur_vol); new_volume = cur_vol+vmin+mod/range_p; if(new_volume==cur_vol && mod!=0)new_volume+=(mod<0?-1:1); if(new_volume<vmin)new_volume=vmin; if(new_volume>vmax)new_volume=vmax; if(snd_mixer_selem_set_playback_volume(elem,0,new_volume)<0){ snd_mixer_close(handle); return; } sprintf(tail,"Volume: %d%%",(int)((float)range_p*(float)new_volume)); addStatusTail(tail,ph->outdetail); snd_mixer_selem_get_playback_volume(elem,1,&cur_vol); new_volume = cur_vol+vmin+mod/range_p; if(new_volume==cur_vol && mod!=0)new_volume+=(mod<0?-1:1); if(new_volume<vmin)new_volume=vmin; if(new_volume>vmax)new_volume=vmax; if(snd_mixer_selem_set_playback_volume(elem,1,new_volume)<0){ snd_mixer_close(handle); return; } snd_mixer_close(handle); }
int getvolume(long* left_volume, long* right_volume) { int ret = 0; snd_mixer_t* handle; snd_mixer_elem_t* elem; snd_mixer_selem_id_t* sid; static const char* mix_name = "Master"; static const char* card = "default"; static int mix_index = 0; snd_mixer_selem_id_alloca(&sid); //sets simple-mixer index and name snd_mixer_selem_id_set_index(sid, mix_index); snd_mixer_selem_id_set_name(sid, mix_name); if ((snd_mixer_open(&handle, 0)) < 0) return -1; if ((snd_mixer_attach(handle, card)) < 0) { snd_mixer_close(handle); return -2; } if ((snd_mixer_selem_register(handle, NULL, NULL)) < 0) { snd_mixer_close(handle); return -3; } ret = snd_mixer_load(handle); if (ret < 0) { snd_mixer_close(handle); return -4; } elem = snd_mixer_find_selem(handle, sid); if (!elem) { snd_mixer_close(handle); return -5; } long minv; long maxv; snd_mixer_selem_get_playback_volume_range (elem, &minv, &maxv); if(snd_mixer_selem_get_playback_volume(elem, 0, left_volume) < 0) { snd_mixer_close(handle); return -3; } /* make the value bound to 100 */ *left_volume -= minv; maxv -= minv; minv = 0; *left_volume = 100 * (*left_volume) / maxv; // make the value bound from 0 to 100 snd_mixer_selem_get_playback_volume_range (elem, &minv, &maxv); if(snd_mixer_selem_get_playback_volume(elem, 1, right_volume) < 0) { snd_mixer_close(handle); return -4; } /* make the value bound to 100 */ *right_volume -= minv; maxv -= minv; minv = 0; *right_volume = 100 * (*right_volume) / maxv; // make the value bound from 0 to 100 snd_mixer_close(handle); return 0; }
static int alsa_mixer_init(alsa_mixer_t *mixer, char *card) { snd_mixer_selem_id_t *sid; snd_mixer_elem_t *elem; snd_mixer_selem_id_alloca(&sid); int err; if ((err = snd_mixer_open(&mixer->handle, 0)) < 0) { DEBUG("Mixer %s open error: %s", card, snd_strerror(err)); mixer->handle = NULL; return err; } if ((err = snd_mixer_attach(mixer->handle, card)) < 0) { DEBUG("Mixer %s local error: %s\n", card, snd_strerror(err)); snd_mixer_close(mixer->handle); mixer->handle = NULL; return err; } if ((err = snd_mixer_selem_register(mixer->handle, NULL, NULL)) < 0) { DEBUG("Mixer register error: %s", snd_strerror(err)); snd_mixer_close(mixer->handle); return err; } snd_mixer_set_callback(mixer->handle, on_mixer_event); err = snd_mixer_load(mixer->handle); if (err < 0) { DEBUG("Mixer %s load error: %s", card, snd_strerror(err)); snd_mixer_close(mixer->handle); return err; } for (elem = snd_mixer_first_elem(mixer->handle); elem; elem = snd_mixer_elem_next(elem)) { const char *name = NULL; snd_mixer_selem_get_id(elem, sid); if (!snd_mixer_selem_is_active(elem)) continue; name = snd_mixer_selem_id_get_name(sid); if(snd_mixer_selem_has_playback_volume(elem)) { if(strcmp(name, "Master") == 0) mixer->master = elem; else if(strcmp(name, "PCM") == 0) mixer->pcm = elem; } else if(snd_mixer_selem_has_capture_volume(elem) || snd_mixer_selem_has_capture_switch(elem)) { DEBUG("capture elem name = %s\n", name); /**make sure have one capture source if capture is available*/ if(NULL == mixer->mic) { mixer->mic = elem; } /**if have Microphone, replace it*/ if(strcmp(name, "Microphone") == 0) mixer->mic = elem; if(strcmp(name, "Capture") == 0) mixer->mic = elem; } } DEBUG("master = %x, pcm = %x, microphone = %x\n", mixer->master, mixer->pcm, mixer->mic); return 0; }
static int control(struct ao *ao, enum aocontrol cmd, void *arg) { struct priv *p = ao->priv; snd_mixer_t *handle = NULL; switch (cmd) { case AOCONTROL_GET_MUTE: case AOCONTROL_SET_MUTE: case AOCONTROL_GET_VOLUME: case AOCONTROL_SET_VOLUME: { int err; snd_mixer_elem_t *elem; snd_mixer_selem_id_t *sid; long pmin, pmax; long get_vol, set_vol; float f_multi; if (!af_fmt_is_pcm(ao->format)) return CONTROL_FALSE; snd_mixer_selem_id_alloca(&sid); snd_mixer_selem_id_set_index(sid, p->cfg_mixer_index); snd_mixer_selem_id_set_name(sid, p->cfg_mixer_name); err = snd_mixer_open(&handle, 0); CHECK_ALSA_ERROR("Mixer open error"); err = snd_mixer_attach(handle, p->cfg_mixer_device); CHECK_ALSA_ERROR("Mixer attach error"); err = snd_mixer_selem_register(handle, NULL, NULL); CHECK_ALSA_ERROR("Mixer register error"); err = snd_mixer_load(handle); CHECK_ALSA_ERROR("Mixer load error"); elem = snd_mixer_find_selem(handle, sid); if (!elem) { MP_VERBOSE(ao, "Unable to find simple control '%s',%i.\n", snd_mixer_selem_id_get_name(sid), snd_mixer_selem_id_get_index(sid)); goto alsa_error; } snd_mixer_selem_get_playback_volume_range(elem, &pmin, &pmax); f_multi = (100 / (float)(pmax - pmin)); switch (cmd) { case AOCONTROL_SET_VOLUME: { ao_control_vol_t *vol = arg; set_vol = vol->left / f_multi + pmin + 0.5; err = snd_mixer_selem_set_playback_volume (elem, SND_MIXER_SCHN_FRONT_LEFT, set_vol); CHECK_ALSA_ERROR("Error setting left channel"); MP_DBG(ao, "left=%li, ", set_vol); set_vol = vol->right / f_multi + pmin + 0.5; err = snd_mixer_selem_set_playback_volume (elem, SND_MIXER_SCHN_FRONT_RIGHT, set_vol); CHECK_ALSA_ERROR("Error setting right channel"); MP_DBG(ao, "right=%li, pmin=%li, pmax=%li, mult=%f\n", set_vol, pmin, pmax, f_multi); break; } case AOCONTROL_GET_VOLUME: { ao_control_vol_t *vol = arg; snd_mixer_selem_get_playback_volume (elem, SND_MIXER_SCHN_FRONT_LEFT, &get_vol); vol->left = (get_vol - pmin) * f_multi; snd_mixer_selem_get_playback_volume (elem, SND_MIXER_SCHN_FRONT_RIGHT, &get_vol); vol->right = (get_vol - pmin) * f_multi; MP_DBG(ao, "left=%f, right=%f\n", vol->left, vol->right); break; } case AOCONTROL_SET_MUTE: { bool *mute = arg; if (!snd_mixer_selem_has_playback_switch(elem)) goto alsa_error; if (!snd_mixer_selem_has_playback_switch_joined(elem)) { snd_mixer_selem_set_playback_switch (elem, SND_MIXER_SCHN_FRONT_RIGHT, !*mute); } snd_mixer_selem_set_playback_switch (elem, SND_MIXER_SCHN_FRONT_LEFT, !*mute); break; } case AOCONTROL_GET_MUTE: { bool *mute = arg; if (!snd_mixer_selem_has_playback_switch(elem)) goto alsa_error; int tmp = 1; snd_mixer_selem_get_playback_switch (elem, SND_MIXER_SCHN_FRONT_LEFT, &tmp); *mute = !tmp; if (!snd_mixer_selem_has_playback_switch_joined(elem)) { snd_mixer_selem_get_playback_switch (elem, SND_MIXER_SCHN_FRONT_RIGHT, &tmp); *mute &= !tmp; } break; } } snd_mixer_close(handle); return CONTROL_OK; } } //end switch return CONTROL_UNKNOWN; alsa_error: if (handle) snd_mixer_close(handle); return CONTROL_ERROR; }
/* Drawbacks. Sets volume on both channels but gets volume on one. Can be easily adapted. */ int audio_volume(audio_volume_action action, long* outvol) { int ret = 0; snd_mixer_t* handle; snd_mixer_elem_t* elem; snd_mixer_selem_id_t* sid; static const char* mix_name = "Master"; static const char* card = "default"; static int mix_index = 0; long pmin, pmax; long get_vol, set_vol; float f_multi; snd_mixer_selem_id_alloca(&sid); //sets simple-mixer index and name snd_mixer_selem_id_set_index(sid, mix_index); snd_mixer_selem_id_set_name(sid, mix_name); if ((snd_mixer_open(&handle, 0)) < 0) return -1; if ((snd_mixer_attach(handle, card)) < 0) { snd_mixer_close(handle); return -2; } if ((snd_mixer_selem_register(handle, NULL, NULL)) < 0) { snd_mixer_close(handle); return -3; } ret = snd_mixer_load(handle); if (ret < 0) { snd_mixer_close(handle); return -4; } elem = snd_mixer_find_selem(handle, sid); if (!elem) { snd_mixer_close(handle); return -5; } long minv, maxv; snd_mixer_selem_get_playback_volume_range (elem, &minv, &maxv); fprintf(stderr, "Volume range <%li,%li>\n", minv, maxv); if(action == AUDIO_VOLUME_GET) { if(snd_mixer_selem_get_playback_volume(elem, 0, outvol) < 0) { snd_mixer_close(handle); return -6; } fprintf(stderr, "Get volume %li with status %i\n", *outvol, ret); /* make the value bound to 100 */ *outvol -= minv; maxv -= minv; minv = 0; *outvol = 100 * (*outvol) / maxv; // make the value bound from 0 to 100 } else if(action == AUDIO_VOLUME_SET) { if(*outvol < 0 || *outvol > VOLUME_BOUND) // out of bounds return -7; *outvol = (*outvol * (maxv - minv) / (100-1)) + minv; if(snd_mixer_selem_set_playback_volume(elem, 0, *outvol) < 0) { snd_mixer_close(handle); return -8; } if(snd_mixer_selem_set_playback_volume(elem, 1, *outvol) < 0) { snd_mixer_close(handle); return -9; } fprintf(stderr, "Set volume %li with status %i\n", *outvol, ret); } snd_mixer_close(handle); return 0; }
static void alsa_volume_mixer(double volume) { snd_mixer_t* handle; snd_mixer_elem_t* elem; snd_mixer_selem_id_t* sid; unsigned c; long pmin, pmax; long v; int r; snd_mixer_selem_id_alloca(&sid); log_std(("sound:alsa: soundb_alsa_volume(volume:%g)\n", (double)volume)); snd_mixer_selem_id_set_name(sid, "Master"); r = snd_mixer_open(&handle, 0); if (r < 0) { log_std(("ERROR:sound:alsa: Mixer open error: %s\n", snd_strerror(r))); goto err; } r = snd_mixer_attach(handle, alsa_option.mixer_buffer); if (r < 0) { log_std(("ERROR:sound:alsa: Mixer attach error: %s\n", snd_strerror(r))); goto err_close; } if ((r = snd_mixer_selem_register(handle, NULL, NULL)) < 0) { log_std(("ERROR:sound:alsa: Mixer register error: %s\n", snd_strerror(r))); goto err_close; } r = snd_mixer_load(handle); if (r < 0) { log_std(("ERROR:sound:alsa: Mixer load error: %s\n", snd_strerror(r))); goto err_close; } elem = snd_mixer_find_selem(handle, sid); if (!elem) { log_std(("ERROR:sound:alsa: Unable to find simple control '%s',%i\n", snd_mixer_selem_id_get_name(sid), snd_mixer_selem_id_get_index(sid))); goto err_close; } if (volume > 0) { if (snd_mixer_selem_has_playback_switch(elem)) { log_std(("sound:alsa: enable playback\n")); for(c=0;c<=SND_MIXER_SCHN_LAST;++c) { snd_mixer_selem_set_playback_switch(elem, c, 1); } } else { log_std(("sound:alsa: skip enable playback\n")); } if (snd_mixer_selem_has_playback_volume(elem)) { log_std(("sound:alsa: set playback volume\n")); snd_mixer_selem_get_playback_volume_range(elem, &pmin, &pmax); v = pmin + (pmax - pmin) * volume + 0.5; if (v < pmin) v = pmin; if (v > pmax) v = pmax; log_std(("sound:alsa: min:%d, max:%d, set:%d\n", (int)pmin, (int)pmax, (int)v)); for(c=0;c<=SND_MIXER_SCHN_LAST;++c) { snd_mixer_selem_set_playback_volume(elem, c, v); } } else { log_std(("sound:alsa: skip set playback volume\n")); } } else { if (snd_mixer_selem_has_playback_switch(elem)) { log_std(("sound:alsa: disable playback\n")); for(c=0;c<=SND_MIXER_SCHN_LAST;++c) { snd_mixer_selem_set_playback_switch(elem, c, 0); } } else { log_std(("sound:alsa: skip disable playback\n")); } } snd_mixer_close(handle); return; err_close: snd_mixer_close(handle); err: return; }