// derived from alsa-utils/aplay.c static void palsa_enum_soundcards (void (*callback)(const char *name, const char *desc, void *), void *userdata) { void **hints, **n; char *name, *descr, *io; const char *filter = "Output"; if (snd_device_name_hint(-1, "pcm", &hints) < 0) return; n = hints; while (*n != NULL) { name = snd_device_name_get_hint(*n, "NAME"); descr = snd_device_name_get_hint(*n, "DESC"); io = snd_device_name_get_hint(*n, "IOID"); if (io == NULL || !strcmp(io, filter)) { if (name && descr && callback) { callback (name, descr, userdata); } } if (name != NULL) free(name); if (descr != NULL) free(descr); if (io != NULL) free(io); n++; } snd_device_name_free_hint(hints); }
/** * @brief Creates a list of all available devices. * * Uses the hints API of ALSA to collect all devices. This also includes plug * devices. The reason to collect these and not the raw hardware devices * (e.g. hw:0,0) is that hardware devices often have a very limited number of * supported formats, etc. Plugs on the other hand are software components that * map all types of formats and inputs to the hardware and therefore they are * much more flexible and more what we want. * * Further helpful info http://jan.newmarch.name/LinuxSound/Sampled/Alsa/. * * @return A collection of devices found on the system. */ AudioAlsa::DeviceInfoCollection AudioAlsa::getAvailableDevices() { DeviceInfoCollection deviceInfos; char **hints; /* Enumerate sound devices */ int err = snd_device_name_hint(-1, "pcm", (void***)&hints); if (err != 0) { return deviceInfos; } char** n = hints; while (*n != NULL) { char *name = snd_device_name_get_hint(*n, "NAME"); char *description = snd_device_name_get_hint(*n, "DESC"); if (name != 0 && description != 0) { deviceInfos.push_back(DeviceInfo(QString(name), QString(description))); } free(name); free(description); n++; } //Free the hint buffer snd_device_name_free_hint((void**)hints); return deviceInfos; }
void AlsaOutputDeviceFactory::rescan() { device_names.clear(); device_channels.clear(); //In the following, we always assume that the device supports 8 channels. //Todo: can we get more info out of Alsa somehow? void** hints = nullptr; auto hint_success = snd_device_name_hint(-1, "pcm", &hints); if(hint_success != 0 || hints == nullptr) { output_count = 0; return; //Advertise no devices because scanning failed. } for(int i = 0; hints[i] != nullptr; i++) { auto hint = hints[i]; auto name = snd_device_name_get_hint(hint, "NAME"); if(name == nullptr) continue; auto desc = snd_device_name_get_hint(hint, "DESC"); auto ioid = snd_device_name_get_hint(hint, "IOID"); if(ioid != nullptr && strcmp(ioid, "Input") != 0) goto freeing; device_names.push_back(std::string(name)); device_channels.push_back(8); //Can we do better? freeing: if(name) free(name); if(desc) free(desc); if(ioid) free(ioid); } snd_device_name_free_hint(hints); output_count = device_names.size(); }
static int alsa_pcm_list(ClientData clientData, Tcl_Interp *interp) { void **hints, **n; Tcl_Obj *pcm = Tcl_NewListObj(0, NULL); if (snd_device_name_hint(-1, "pcm", &hints) >= 0) { n = hints; while (*n != NULL) { char *name, *descr, *io; name = snd_device_name_get_hint(*n, "NAME"); descr = snd_device_name_get_hint(*n, "DESC"); io = snd_device_name_get_hint(*n, "IOID"); Tcl_ListObjAppendElement(interp, pcm, Tcl_ObjPrintf("%s %s %s", name?name:"(null)", descr?descr:"(null)", io?io:"(null)")); if (name != NULL) free(name); if (descr != NULL) free(descr); if (io != NULL) free(io); n++; } snd_device_name_free_hint(hints); } Tcl_SetObjResult(interp, pcm); return TCL_OK; }
void AlsaDeviceListModel::init( const char * _iface, const char * _filter) { void **hints, **n; char *name, *descr, *io; if (snd_device_name_hint(-1, _iface, &hints) < 0) return; n = hints; while (*n != NULL) { name = snd_device_name_get_hint(*n, "NAME"); descr = snd_device_name_get_hint(*n, "DESC"); io = snd_device_name_get_hint(*n, "IOID"); // Filter out non-null or filtered items if (io == NULL || _filter == NULL || strcmp(io, _filter) == 0) m_devices.append(StringPair(name, descr)); if (name != NULL) free(name); if (descr != NULL) free(descr); if (io != NULL) free(io); n++; } snd_device_name_free_hint(hints); }
Array AudioDriverALSA::get_device_list() { Array list; list.push_back("Default"); void **hints; if (snd_device_name_hint(-1, "pcm", &hints) < 0) return list; for (void **n = hints; *n != NULL; n++) { char *name = snd_device_name_get_hint(*n, "NAME"); char *desc = snd_device_name_get_hint(*n, "DESC"); if (name != NULL && !strncmp(name, "plughw", 6)) { if (desc) { list.push_back(String(name) + ";" + String(desc)); } else { list.push_back(String(name)); } } if (desc != NULL) free(desc); if (name != NULL) free(name); } snd_device_name_free_hint(hints); return list; }
void printInDevices() { char **hints; /* Enumerate sound devices */ int err = snd_device_name_hint(0, "pcm", (void***)&hints); if (err != 0) { fprintf(stderr, "Can't read data\n"); exit(EXIT_FAILURE); } char** n = hints; while (*n != NULL) { char *name = snd_device_name_get_hint(*n, "NAME"); char *desc = snd_device_name_get_hint(*n, "DESC"); char *type = snd_device_name_get_hint(*n, "IOID"); if (name != NULL && desc != NULL) { if (type == NULL || !strcmp(type, "Input")) { printf("%s\n%s: <%s>\n\n", desc, type == NULL ? "Input/Output" : "Input",name); } } if (name != NULL) free(name); if (name != NULL) free(desc); if (name != NULL) free(type); n++; } //Free hint buffer too snd_device_name_free_hint((void**)hints); snd_config_update_free_global(); }
QList<QByteArray> QAlsaAudioDeviceInfo::availableDevices(QAudio::Mode mode) { QList<QByteArray> devices; QByteArray filter; #if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14) // Create a list of all current audio devices that support mode void **hints, **n; char *name, *descr, *io; if(snd_device_name_hint(-1, "pcm", &hints) < 0) { qWarning() << "no alsa devices available"; return devices; } n = hints; if(mode == QAudio::AudioInput) { filter = "Input"; } else { filter = "Output"; } while (*n != NULL) { name = snd_device_name_get_hint(*n, "NAME"); if (name != 0 && qstrcmp(name, "null") != 0) { descr = snd_device_name_get_hint(*n, "DESC"); io = snd_device_name_get_hint(*n, "IOID"); if ((descr != NULL) && ((io == NULL) || (io == filter))) { QString deviceName = QLatin1String(name); QString deviceDescription = QLatin1String(descr); if (deviceDescription.contains(QLatin1String("Default Audio Device"))) devices.prepend(deviceName.toLocal8Bit().constData()); else devices.append(deviceName.toLocal8Bit().constData()); } free(descr); free(io); } free(name); ++n; } snd_device_name_free_hint(hints); #else int idx = 0; char* name; while(snd_card_get_name(idx,&name) == 0) { devices.append(name); idx++; } #endif if (devices.size() > 0) devices.append("default"); return devices; }
std::vector<std::string> alsa_get_devicelist() { std::vector<std::string> result; char **hints; int err = snd_device_name_hint(-1, "pcm", (void***)&hints); // Error initializing ALSA if (err != 0) return result; // special value to automatically detect on initialization result.push_back("auto"); char** n = hints; while (*n != NULL) { // Get the type (NULL/Input/Output) char *type = snd_device_name_get_hint(*n, "IOID"); char *name = snd_device_name_get_hint(*n, "NAME"); if (name != NULL) { // We only want output or special devices (like "default" or "pulse") // TODO Only those with type == NULL? if (type == NULL || strcmp(type, "Output") == 0) { // TODO Check if device works (however we need to hash the resulting list then) /*snd_pcm_t *handle; int rc = snd_pcm_open(&handle, name, SND_PCM_STREAM_PLAYBACK, 0); if (rc == 0) { result.push_back(name); snd_pcm_close(handle); } */ result.push_back(name); } } if (type != NULL) free(type); if (name != NULL) free(name); n++; } snd_device_name_free_hint((void**)hints); return result; }
void moko_alsa_volume_control_set_device_from_name (MokoAlsaVolumeControl *control, const gchar *name) { gint i = -1; if (!name) { moko_alsa_volume_control_set_device (control, NULL); return; } while (snd_card_next (&i) == 0) { void **hints; if (i == -1) break; if (snd_device_name_hint (i, "pcm", &hints) == 0) { gchar *separator; gchar *device = strdup (snd_device_name_get_hint ( hints[0], "DESC")); if ((separator = strchr (device, ','))) *separator = '\0'; if (strcmp (device, name) == 0) { g_free (device); device = strdup (snd_device_name_get_hint ( hints[0], "NAME")); snd_device_name_free_hint (hints); strchr (device, ':')[0] = '\0'; moko_alsa_volume_control_set_device ( control, device); return; } snd_device_name_free_hint (hints); g_free (device); } } g_debug ("Card '%s' not found", name); }
static char * find_pcm_name(const char *device_longname) { char *pcm_name = NULL; int card = -1; int done = 0; if (!device_longname) return strdup("default"); while (!done) { int err = snd_card_next(&card); if (err != 0) break; if (card == -1) break; char *longname = NULL; if (snd_card_get_longname(card, &longname) != 0) goto next_0; if (!longname) goto next_0; if (strcmp(device_longname, longname) != 0) goto next_2; void **hints; if (snd_device_name_hint(card, "pcm", &hints) != 0) goto next_2; for (int k = 0; hints[k] != NULL; k ++) { pcm_name = snd_device_name_get_hint(hints[k], "NAME"); if (strncmp(pcm_name, "default:", strlen("default:")) == 0) { done = 1; goto next_3; } free(pcm_name); pcm_name = NULL; } next_3: snd_device_name_free_hint(hints); next_2: free(longname); next_0: continue; } if (!pcm_name) pcm_name = strdup("default"); return pcm_name; }
QList<QByteArray> QAudioDeviceInfoInternal::deviceList(QAudio::Mode mode) { QList<QByteArray> devices; QByteArray filter; // Create a list of all current audio devices that support mode void **hints, **n; char *name, *descr, *io; if(snd_device_name_hint(-1, "pcm", &hints) < 0) { qWarning() << "no alsa devices available"; return devices; } n = hints; if(mode == QAudio::AudioInput) { filter = "Input"; } else { filter = "Output"; } while (*n != NULL) { name = snd_device_name_get_hint(*n, "NAME"); descr = snd_device_name_get_hint(*n, "DESC"); io = snd_device_name_get_hint(*n, "IOID"); if((name != NULL) && (descr != NULL) && ((io == NULL) || (io == filter))) { QString str = QLatin1String(name); if(str.contains(QLatin1String("default"))) { int pos = str.indexOf(QLatin1String("="),0); devices.append(str.mid(pos+1).toLocal8Bit().constData()); } } if(name != NULL) free(name); if(descr != NULL) free(descr); if(io != NULL) free(io); ++n; } snd_device_name_free_hint(hints); if(devices.size() > 0) { devices.append("default"); } return devices; }
/* API: refresh the device list */ static pj_status_t alsa_factory_refresh(pjmedia_aud_dev_factory *f) { struct alsa_factory *af = (struct alsa_factory*)f; char **hints, **n; int err; TRACE_((THIS_FILE, "pjmedia_snd_init: Enumerate sound devices")); if (af->pool != NULL) { pj_pool_release(af->pool); af->pool = NULL; } af->pool = pj_pool_create(af->pf, "alsa_aud", 256, 256, NULL); af->dev_cnt = 0; /* Enumerate sound devices */ err = snd_device_name_hint(-1, "pcm", (void***)&hints); if (err != 0) return PJMEDIA_EAUD_SYSERR; /* Set a null error handler prior to enumeration to suppress errors */ snd_lib_error_set_handler(null_alsa_error_handler); n = hints; while (*n != NULL) { char *name = snd_device_name_get_hint(*n, "NAME"); if (name != NULL) { if (0 != strcmp("null", name)) add_dev(af, name); free(name); } n++; } /* Get the mixer name */ get_mixer_name(af); /* Install error handler after enumeration, otherwise we'll get many * error messages about invalid card/device ID. */ snd_lib_error_set_handler(alsa_error_handler); err = snd_device_name_free_hint((void**)hints); PJ_LOG(4,(THIS_FILE, "ALSA driver found %d devices", af->dev_cnt)); return PJ_SUCCESS; }
static void *alsa_device_list_new(void *data) { void **hints, **n; union string_list_elem_attr attr; struct string_list *s = string_list_new(); if (!s) return NULL; attr.i = 0; if (snd_device_name_hint(-1, "pcm", &hints) != 0) goto error; n = hints; while (*n != NULL) { char *name = snd_device_name_get_hint(*n, "NAME"); char *io = snd_device_name_get_hint(*n, "IOID"); char *desc = snd_device_name_get_hint(*n, "DESC"); /* description of device IOID - input / output identifcation * ("Input" or "Output"), NULL means both) */ if (!io || string_is_equal(io,"Output")) string_list_append(s, name, attr); if (name) free(name); if (io) free(io); if (desc) free(desc); n++; } /* free hint buffer too */ snd_device_name_free_hint(hints); return s; error: string_list_free(s); return NULL; }
void moko_alsa_volume_control_set_device_from_card_number ( MokoAlsaVolumeControl *control, gint number) { void **hints; if (snd_device_name_hint (number, "pcm", &hints) == 0) { gchar *device = strdup (snd_device_name_get_hint ( hints[0], "NAME")); snd_device_name_free_hint (hints); strchr (device, ':')[0] = '\0'; moko_alsa_volume_control_set_device (control, device); g_free (device); } else g_debug ("Unable to find card number %d", number); }
int main() { size_t start, end; std::string desc; void ** hints, ** str_c; char * name_c, * desc_c; int index = 0; const char * ifaces[] = { "card", "pcm", "rawmidi", "timer", "seq", "hwdep", 0 }; snd_config_update(); while (ifaces[index]) { std::cout << "---- Trying \"" << ifaces[index] << "\" ----\n\n"; if (snd_device_name_hint(-1, ifaces[index], &hints) < 0) { std::cerr << "hint failed on \"" << ifaces[index] << "\"\n\n"; ++index; continue; } str_c = hints; while (*str_c) { name_c = snd_device_name_get_hint(*str_c, "NAME"); desc_c = snd_device_name_get_hint(*str_c, "DESC"); std::cout << name_c << "\n"; start = 0; desc = desc_c; do { end = desc.find("\n", start); std::cout << " " << desc.substr(start, end - start) << "\n"; start = end + 1; } while (end != std::string::npos); std::cout << "\n"; free(name_c); free(desc_c); ++str_c; } snd_device_name_free_hint(hints); ++index; } return 0; }
int main(int argc, char *argv[]) { const char *iface = "pcm"; char **hints, **n; int err; if (argc > 1) iface = argv[1]; err = snd_device_name_hint(-1, iface, &hints); if (err < 0) errx(1, "snd_device_name_hint error: %s", snd_strerror(err)); n = hints; while (*n != NULL) { printf("%s\n", *n); n++; } snd_device_name_free_hint(hints); return 0; }
void QAudioDeviceInfoInternal::checkSurround() { QList<QByteArray> devices; surround40 = false; surround51 = false; surround71 = false; void **hints, **n; char *name, *descr, *io; if(snd_device_name_hint(-1, "pcm", &hints) < 0) return; n = hints; while (*n != NULL) { name = snd_device_name_get_hint(*n, "NAME"); descr = snd_device_name_get_hint(*n, "DESC"); io = snd_device_name_get_hint(*n, "IOID"); if((name != NULL) && (descr != NULL)) { QString deviceName = QLatin1String(name); if (mode == QAudio::AudioOutput) { if(deviceName.contains(QLatin1String("surround40"))) surround40 = true; if(deviceName.contains(QLatin1String("surround51"))) surround51 = true; if(deviceName.contains(QLatin1String("surround71"))) surround71 = true; } } if(name != NULL) free(name); if(descr != NULL) free(descr); if(io != NULL) free(io); ++n; } snd_device_name_free_hint(hints); }
int main(int argc, char** argv) { if (argc != 2) { fprintf(stderr, "Usage: %s Input|Output\n", argv[0]); fprintf(stderr, "The \"Input\" filter lists all mics, the \"Output\" filter all playback devices.\n"); fprintf(stderr, "The filter name is case senstive!\n"); return 1; } void **hints; char *name, *desc, *io; const char *filter = argv[1]; int err = snd_device_name_hint(-1, "pcm", &hints); if (err != 0) return err; //filter = stream == SND_PCM_STREAM_CAPTURE ? "Input" : "Output"; for(void** n = hints; *n != NULL; n++) { name = snd_device_name_get_hint(*n, "NAME"); desc = snd_device_name_get_hint(*n, "DESC"); io = snd_device_name_get_hint(*n, "IOID"); if ( io != NULL && strcmp(io, filter) == 0 ) { printf("name: %s\n", name); printf(" desc: %s\n", desc); printf(" io: %s\n", io); } free(name); free(desc); free(io); } snd_device_name_free_hint(hints); return 0; }
void detect_pcm(AudioCapture *capture) { void **hints, **n; char *name, *desc, *io; snd_device_name_hint(-1, "pcm", &hints); n = hints; char *detected_name = NULL; int pcm_id = 0; fprintf(stderr, "Selecting (%d)\r\n", capture->selected_device); while(*n && !detected_name) { io = snd_device_name_get_hint(*n, "IOID"); if(!io || strcmp(io, "Input")) { if(io) free(io); n++; pcm_id++; continue; } name = snd_device_name_get_hint(*n, "NAME"); desc = snd_device_name_get_hint(*n, "DESC"); if((capture->selected_device < 0 && strstr(desc, "USB") && strstr(desc, "Default Audio")) || capture->selected_device == pcm_id) { detected_name = (char *)malloc(strlen(name) + 1); strcpy(detected_name, name); } free(name); free(desc); n++; } snd_device_name_free_hint(hints); // This is so for USB //capture->pcm_id = 1; fprintf(stderr, "Selected (%d)\r\n", pcm_id); capture->pcm_name = detected_name; }
void list_devices(void) { void **hints, **n; if (snd_device_name_hint(-1, "pcm", &hints) >= 0) { n = hints; printf("Output devices:\n"); while (*n) { char *name = snd_device_name_get_hint(*n, "NAME"); char *desc = snd_device_name_get_hint(*n, "DESC"); if (name) printf(" %-30s", name); if (desc) { char *s1 = strtok(desc, "\n"); char *s2 = strtok(NULL, "\n"); if (s1) printf(" - %s", s1); if (s2) printf(" - %s", s2); } printf("\n"); if (name) free(name); if (desc) free(desc); n++; } snd_device_name_free_hint(hints); } printf("\n"); }
void ProjectOptions::InitDevInfo() { snd_config_update(); int ctlNum = -1; SoundDevInfo *inf = waveList.AddItem(); inf->name = "default"; inf->id = -1; inf->sub = -1; inf->type = 0; inf->info = NULL; while (snd_card_next(&ctlNum) == 0 && ctlNum >= 0 ) { #if SND_LIB_VERSION >= 0x1000E void **hints = 0; void **hh; if (snd_device_name_hint(ctlNum, "pcm", &hints) == 0) { hh = hints; while (*hh) { char *name = snd_device_name_get_hint(*hh, "NAME"); char *desc = snd_device_name_get_hint(*hh, "DESC"); for (char *cp = desc; *cp; cp++) { if (*cp < 0x20) *cp = ' '; } SoundDevInfo *inf = waveList.AddItem(); inf->id = ctlNum; inf->sub = 0; inf->type = 0; inf->name = desc; inf->info = name; if (desc) free(desc); hh++; } snd_device_name_free_hint(hints); } if (snd_device_name_hint(ctlNum, "rawmidi", &hints) == 0) { hh = hints; while (*hh) { char *name = snd_device_name_get_hint(*hh, "NAME"); char *desc = snd_device_name_get_hint(*hh, "DESC"); for (char *cp = desc; *cp; cp++) { if (*cp < 0x20) *cp = ' '; } SoundDevInfo *inf = midiList.AddItem(); inf->id = ctlNum; inf->sub = 0; inf->type = 0; inf->name = desc; inf->info = name; if (desc) free(desc); hh++; } snd_device_name_free_hint(hints); } #endif /* snd_ctl_card_info_t *devInfo; snd_pcm_t *pcmHandle; snd_pcm_info_t *pcmInfo; snd_rawmidi_t *midiHandle; snd_rawmidi_info_t *midiInfo; char hw[128]; snprintf(hw, sizeof(hw), "hw:%d", ctlNum); int devNum; snd_ctl_t *ctl; if (snd_ctl_open(&ctl, hw, 0) >= 0) { devNum = -1; while (snd_ctl_pcm_next_device(ctl, &devNum) == 0 && devNum >= 0) { snprintf(hw, sizeof(hw), "hw:%d,%d", ctlNum, devNum); if (snd_pcm_open(&pcmHandle, hw, SND_PCM_STREAM_PLAYBACK, 0) >= 0) { snd_pcm_info_malloc(&pcmInfo); snd_pcm_info(pcmHandle, pcmInfo); SoundDevInfo *inf = waveList.AddItem(); inf->id = ctlNum; inf->sub = devNum; inf->type = 0; inf->info = (void*)pcmInfo; inf->name = snd_pcm_info_get_name(pcmInfo); //inf->name += " - "; //inf->name += snd_pcm_info_get_subdevice_name(pcmInfo); snd_pcm_close(pcmHandle); pcmHandle = 0; } } devNum = -1; while (snd_ctl_rawmidi_next_device(ctl, &devNum) == 0 && devNum >= 0) { snprintf(hw, sizeof(hw), "hw:%d,%d", ctlNum, devNum); if (snd_rawmidi_open(&midiHandle, NULL, hw, 0) >= 0) { snd_rawmidi_info_malloc(&midiInfo); snd_rawmidi_info(midiHandle, midiInfo); SoundDevInfo *inf = midiList.AddItem(); inf->id = ctlNum; inf->sub = devNum; inf->type = 1; inf->info = (void*)midiInfo; inf->name = snd_rawmidi_info_get_name(midiInfo); //inf->name += " - "; //inf->name += snd_rawmidi_info_get_subdevice_name(midiInfo); snd_rawmidi_close(midiHandle); } } // SoundDevInfo *inf = waveList.AddItem(); // snd_ctl_card_info_alloca(&devInfo); // snd_ctl_card_info(ctl, devInfo); // inf->name = snd_ctl_card_info_get_name(devInfo); // inf->info = reinterpret_cast<void*>(devInfo); // inf->id = (long)devNum; snd_ctl_close(ctl); }*/ } // TODO: //snd_lib_error_set_handler(alsa_error_handler); }
size_t stack_alsa_audio_device_list_outputs(StackAudioDeviceDesc **outputs) { static const int common_sample_rates[] = {8000, 11025, 16000, 22050, 32000, 44100, 48000, 88200, 96000, 176400, 192000}; static const size_t num_common_sample_rates = 11; // Initialise pulse audio if (!stack_init_alsa_audio()) { // Failed to initialise, return NULL *outputs = NULL; return 0; } // Get some hints void **hints = NULL; int result = snd_device_name_hint(-1, "pcm", &hints); if (result != 0) { *outputs = NULL; return 0; } size_t alsa_device_count = 0, stack_device_count = 0; // Count how many devices we find for (size_t i = 0; hints[i] != NULL; i++) { alsa_device_count = i + 1; } // If there are no devices, return immediately if (alsa_device_count == 0) { snd_device_name_free_hint(hints); *outputs = NULL; return 0; } StackAudioDeviceDesc* devices = new StackAudioDeviceDesc[alsa_device_count]; // Iterate over the devices and build information for (size_t alsa_device_idx = 0, stack_device_idx = 0; alsa_device_idx < alsa_device_count; alsa_device_idx++) { char *name = snd_device_name_get_hint(hints[alsa_device_idx], "NAME"); char *desc = snd_device_name_get_hint(hints[alsa_device_idx], "DESC"); // Open the device snd_pcm_t* pcm = NULL; if (snd_pcm_open(&pcm, name, SND_PCM_STREAM_PLAYBACK, 0) != 0) { free(name); free(desc); continue; } // Get parameters snd_pcm_hw_params_t* hw_params = NULL; snd_pcm_hw_params_malloc(&hw_params); snd_pcm_hw_params_any(pcm, hw_params); // Get minimum and maximum number of channels unsigned int min, max; snd_pcm_hw_params_get_channels_min(hw_params, &min); snd_pcm_hw_params_get_channels_max(hw_params, &max); // Limit the maximum to 32 channels, as some devices return "-1" channels if (max > 32) { max = 32; } // Store channel counts devices[stack_device_idx].min_channels = min; devices[stack_device_idx].max_channels = max; // Get minimum and maximum sample rates int dir; snd_pcm_hw_params_get_rate_min(hw_params, &min, &dir); snd_pcm_hw_params_get_rate_max(hw_params, &max, &dir); // Iterate over our common sample rates and see which ones are valid size_t sample_rate_count = 0; for (size_t common_rate_idx = 0; common_rate_idx < num_common_sample_rates; common_rate_idx++) { if (common_sample_rates[common_rate_idx] >= min && common_sample_rates[common_rate_idx] <= max) { if (snd_pcm_hw_params_test_rate(pcm, hw_params, common_sample_rates[common_rate_idx], 0) == 0) { sample_rate_count++; } } } // Store the sample rates devices[stack_device_idx].num_rates = sample_rate_count; if (sample_rate_count > 0) { devices[stack_device_idx].rates = new uint32_t[sample_rate_count]; for (size_t rate_idx = 0, common_rate_idx = 0; common_rate_idx < num_common_sample_rates; common_rate_idx++) { if (common_sample_rates[common_rate_idx] >= min && common_sample_rates[common_rate_idx] <= max) { devices[stack_device_idx].rates[rate_idx] = common_sample_rates[common_rate_idx]; rate_idx++; } } } else { devices[stack_device_idx].rates = NULL; } // Store the name and description devices[stack_device_idx].name = strdup(name); devices[stack_device_idx].desc = strdup(desc); // Tidy up free(name); free(desc); snd_pcm_hw_params_free(hw_params); snd_pcm_close(pcm); stack_device_count++; stack_device_idx++; } // Tidy up snd_device_name_free_hint(hints); // We can technically return an array that contains more elements than // we say (if a device fails to open, for example), but this is not a // problem, as we only allocate and indeed free the internals of the // number we say, but we tidy up the whole array *outputs = devices; return stack_device_count; }
void CAESinkALSA::EnumerateDevicesEx(AEDeviceInfoList &list, bool force) { /* ensure that ALSA has been initialized */ snd_lib_error_set_handler(sndLibErrorHandler); if(!snd_config || force) { if(force) snd_config_update_free_global(); snd_config_update(); } snd_config_t *config; snd_config_copy(&config, snd_config); /* Always enumerate the default device. * Note: If "default" is a stereo device, EnumerateDevice() * will automatically add "@" instead to enable surroundXX mangling. * We don't want to do that if "default" can handle multichannel * itself (e.g. in case of a pulseaudio server). */ EnumerateDevice(list, "default", "", config); void **hints; if (snd_device_name_hint(-1, "pcm", &hints) < 0) { CLog::Log(LOGINFO, "CAESinkALSA - Unable to get a list of devices"); return; } std::string defaultDescription; for (void** hint = hints; *hint != NULL; ++hint) { char *io = snd_device_name_get_hint(*hint, "IOID"); char *name = snd_device_name_get_hint(*hint, "NAME"); char *desc = snd_device_name_get_hint(*hint, "DESC"); if ((!io || strcmp(io, "Output") == 0) && name && strcmp(name, "null") != 0) { std::string baseName = std::string(name); baseName = baseName.substr(0, baseName.find(':')); if (strcmp(name, "default") == 0) { /* added already, but lets get the description if we have one */ if (desc) defaultDescription = desc; } else if (baseName == "front") { /* Enumerate using the surroundXX mangling */ /* do not enumerate basic "front", it is already handled * by the default "@" entry added in the very beginning */ if (strcmp(name, "front") != 0) EnumerateDevice(list, std::string("@") + (name+5), desc ? desc : name, config); } /* Do not enumerate "default", it is already enumerated above. */ /* Do not enumerate the sysdefault or surroundXX devices, those are * always accompanied with a "front" device and it is handled above * as "@". The below devices will be automatically used if available * for a "@" device. */ /* Ubuntu has patched their alsa-lib so that "defaults.namehint.extended" * defaults to "on" instead of upstream "off", causing lots of unwanted * extra devices (many of which are not actually routed properly) to be * found by the enumeration process. Skip them as well ("hw", "dmix", * "plughw", "dsnoop"). */ else if (baseName != "default" && baseName != "sysdefault" && baseName != "surround40" && baseName != "surround41" && baseName != "surround50" && baseName != "surround51" && baseName != "surround71" && baseName != "hw" && baseName != "dmix" && baseName != "plughw" && baseName != "dsnoop") { EnumerateDevice(list, name, desc ? desc : name, config); } } free(io); free(name); free(desc); } snd_device_name_free_hint(hints); /* set the displayname for default device */ if (!list.empty() && list[0].m_deviceName == "default") { /* If we have one from a hint (DESC), use it */ if (!defaultDescription.empty()) list[0].m_displayName = defaultDescription; /* Otherwise use the discovered name or (unlikely) "Default" */ else if (list[0].m_displayName.empty()) list[0].m_displayName = "Default"; } /* lets check uniqueness, we may need to append DEV or CARD to DisplayName */ /* If even a single device of card/dev X clashes with Y, add suffixes to * all devices of both them, for clarity. */ /* clashing card names, e.g. "NVidia", "NVidia_2" */ std::set<std::string> cardsToAppend; /* clashing basename + cardname combinations, e.g. ("hdmi","Nvidia") */ std::set<std::pair<std::string, std::string> > devsToAppend; for (AEDeviceInfoList::iterator it1 = list.begin(); it1 != list.end(); ++it1) { for (AEDeviceInfoList::iterator it2 = it1+1; it2 != list.end(); ++it2) { if (it1->m_displayName == it2->m_displayName && it1->m_displayNameExtra == it2->m_displayNameExtra) { /* something needs to be done */ std::string cardString1 = GetParamFromName(it1->m_deviceName, "CARD"); std::string cardString2 = GetParamFromName(it2->m_deviceName, "CARD"); if (cardString1 != cardString2) { /* card name differs, add identifiers to all devices */ cardsToAppend.insert(cardString1); cardsToAppend.insert(cardString2); continue; } std::string devString1 = GetParamFromName(it1->m_deviceName, "DEV"); std::string devString2 = GetParamFromName(it2->m_deviceName, "DEV"); if (devString1 != devString2) { /* device number differs, add identifiers to all such devices */ devsToAppend.insert(std::make_pair(it1->m_deviceName.substr(0, it1->m_deviceName.find(':')), cardString1)); devsToAppend.insert(std::make_pair(it2->m_deviceName.substr(0, it2->m_deviceName.find(':')), cardString2)); continue; } /* if we got here, the configuration is really weird, just append the whole device string */ it1->m_displayName += " (" + it1->m_deviceName + ")"; it2->m_displayName += " (" + it2->m_deviceName + ")"; } } } for (std::set<std::string>::iterator it = cardsToAppend.begin(); it != cardsToAppend.end(); ++it) { for (AEDeviceInfoList::iterator itl = list.begin(); itl != list.end(); ++itl) { std::string cardString = GetParamFromName(itl->m_deviceName, "CARD"); if (cardString == *it) /* "HDA NVidia (NVidia)", "HDA NVidia (NVidia_2)", ... */ itl->m_displayName += " (" + cardString + ")"; } } for (std::set<std::pair<std::string, std::string> >::iterator it = devsToAppend.begin(); it != devsToAppend.end(); ++it) { for (AEDeviceInfoList::iterator itl = list.begin(); itl != list.end(); ++itl) { std::string baseName = itl->m_deviceName.substr(0, itl->m_deviceName.find(':')); std::string cardString = GetParamFromName(itl->m_deviceName, "CARD"); if (baseName == it->first && cardString == it->second) { std::string devString = GetParamFromName(itl->m_deviceName, "DEV"); /* "HDMI #0", "HDMI #1" ... */ itl->m_displayNameExtra += " #" + devString; } } } }
/* API: refresh the device list */ static pj_status_t alsa_factory_refresh(pjmedia_aud_dev_factory *f) { struct alsa_factory *af = (struct alsa_factory*)f; char **hints, **n; int err; TRACE_((THIS_FILE, "pjmedia_snd_init: Enumerate sound devices")); if (af->pool != NULL) { pj_pool_release(af->pool); af->pool = NULL; } af->pool = pj_pool_create(af->pf, "alsa_aud", 256, 256, NULL); af->dev_cnt = 0; /* Enumerate sound devices */ err = snd_device_name_hint(-1, "pcm", (void***)&hints); if (err != 0) return PJMEDIA_EAUD_SYSERR; /* Set a null error handler prior to enumeration to suppress errors */ snd_lib_error_set_handler(null_alsa_error_handler); n = hints; while (*n != NULL) { char *name = snd_device_name_get_hint(*n, "NAME"); char *desc = snd_device_name_get_hint(*n, "DESC"); if (name != NULL) { if (strncmp("null", name, 4) == 0 || strncmp("front", name, 5) == 0 || strncmp("rear", name, 4) == 0 || strncmp("side", name, 4) == 0 || strncmp("dmix", name, 4) == 0 || strncmp("dsnoop", name, 6) == 0 || strncmp("hw", name, 2) == 0 || strncmp("plughw", name, 6) == 0 || strncmp("center_lfe", name, 10) == 0 || strncmp("surround40", name, 10) == 0 || strncmp("surround41", name, 10) == 0 || strncmp("surround50", name, 10) == 0 || strncmp("surround51", name, 10) == 0 || strncmp("surround71", name, 10) == 0 || (strncmp("default", name, 7) == 0 && strstr(name, ":CARD=") != NULL)) { /* skip these devices, 'sysdefault' always contains the relevant information */ ; } else { add_dev(af, name, desc); } free(name); free(desc); } n++; } /* Install error handler after enumeration, otherwise we'll get many * error messages about invalid card/device ID. */ snd_lib_error_set_handler(alsa_error_handler); err = snd_device_name_free_hint((void**)hints); PJ_LOG(4,(THIS_FILE, "ALSA driver found %d devices", af->dev_cnt)); return PJ_SUCCESS; }
void AlsaBackend::UpdateDevicesList() { Log("AlsaBackend::UpdateDeviceList\n"); DeviceInfo info; void **hints, **n; char *name, *descr, *desc; unsigned devices = 0; InitDevicesList(); info.SetDevice(devices++, "default", "Default device", "default"); info.type = DeviceInfo::TYPE_PLUG; info.direction = 0; PcmPreProbe(info, OUTPUT); PcmPreProbe(info, INPUT); devicesList.push_back(info); // Start with safe alsa detection, list the devices from software config. snd_config_update(); if (snd_device_name_hint(-1, "pcm", &hints) < 0) return; n = hints; while (*n != NULL) { name = snd_device_name_get_hint(*n, "NAME"); descr = snd_device_name_get_hint(*n, "DESC"); if (!descr) desc = (char*)""; else { desc = descr; for (int i = strlen(desc); i > 0; i--) if (desc[i-1] == '\n') desc[i-1] = ' '; } if (IgnorePlugin(name)) { Log("Ignoring ALSA device %s", name); } else { info.SetDevice(devices++, name, desc, name); info.type = DeviceInfo::TYPE_PLUG; info.probed = false; info.direction = 0; PcmPreProbe(info, OUTPUT); PcmPreProbe(info, INPUT); if (info.direction != 0) devicesList.push_back(info); } if (name != NULL) free(name); if (descr != NULL) free(descr); n++; } snd_device_name_free_hint(hints); // Continue with new detection, this is a more thorough test with probing device characteristics enum { IDLEN = 12 }; char hwdev[IDLEN+1]; int card, err, dev; snd_ctl_t* handle = NULL; snd_ctl_card_info_t* cardinfo; snd_pcm_info_t* pcminfo; snd_ctl_card_info_alloca(&cardinfo); snd_pcm_info_alloca(&pcminfo); card = -1; while (snd_card_next(&card) == 0 && card >= 0) { snprintf(hwdev, IDLEN, "hw:%d", card); err = snd_ctl_open(&handle, hwdev, 0); if (sc_errcheck(err, "opening control interface", card, -1)) continue; err = snd_ctl_card_info(handle, cardinfo); if (sc_errcheck(err, "obtaining card info", card, -1)) { snd_ctl_close(handle); continue; } Log("Card %d, ID '%s', name '%s'", card, snd_ctl_card_info_get_id(cardinfo), snd_ctl_card_info_get_name(cardinfo)); dev = -1; if (snd_ctl_pcm_next_device(handle, &dev) < 0) { snd_ctl_close(handle); continue; } while (dev >= 0) { if (!DevProbe(handle, pcminfo, card, dev, OUTPUT) && !DevProbe(handle, pcminfo, card, dev, INPUT)) { if (snd_ctl_pcm_next_device(handle, &dev) < 0) break; } snprintf(hwdev, IDLEN, "hw:%d,%d", card, dev); char strbuf[DEVICE_NAME_MAXLEN]; snprintf(strbuf, DEVICE_NAME_MAXLEN, "%s, %s", snd_ctl_card_info_get_name(cardinfo), snd_pcm_info_get_name(pcminfo)); info.SetDevice(devices++, hwdev, strbuf, hwdev); info.type = DeviceInfo::TYPE_HW; info.probed = false; info.direction = 0; PcmPreProbe(info, OUTPUT); PcmPreProbe(info, INPUT); Log("**********\n%s :: %s\n**********\n", info.guid, info.displayName); devicesList.push_back(info); if (snd_ctl_pcm_next_device(handle, &dev) < 0) break; } snd_ctl_close(handle); } // And complement with chewing a bit on user-defined entries, too. // from PortAudio /* Iterate over plugin devices */ snd_config_t *topNode = NULL; assert(snd_config); if ((err = snd_config_search(snd_config, "pcm", &topNode)) >= 0) { snd_config_iterator_t i, next; snd_config_for_each(i, next, topNode) { const char *tpStr = "unknown", *idStr = NULL; int err = 0; snd_config_t *n = snd_config_iterator_entry(i), *tp = NULL; if ((err = snd_config_search(n, "type", &tp)) < 0) { if (-ENOENT != err) { Log("plugin list error: %s", snd_strerror(err)); } } else { snd_config_get_string(tp, &tpStr); } snd_config_get_id(n, &idStr); if (IgnorePlugin(idStr)) { Log("Ignoring ALSA plugin device %s of type %s", idStr, tpStr); continue; } Log("Found plugin %s of type %s", idStr, tpStr); info.SetDevice(devices++, idStr, idStr, tpStr); info.probed = false; info.direction = 0; if (strncmp(tpStr, "bluetooth", 9)==0) info.type = DeviceInfo::TYPE_BLUETOOTH; else if(strncmp(tpStr, "null", 4) == 0) { info.type = DeviceInfo::TYPE_NULL; // Never need to probe the null device. info.probed = true; } else if(strncmp(tpStr, "unknown", 4) == 0) info.type = DeviceInfo::TYPE_UNKNOWN; else { info.type = DeviceInfo::TYPE_PLUG; // No need to preprobe bluetooth, null and unknown(?) types. PcmPreProbe(info, OUTPUT); PcmPreProbe(info, INPUT); } devicesList.push_back(info); } }