static char *card_name(const char *device) { char *pt; if ((pt = index(device, ':')) == NULL) return strdup(device); snd_card_get_name(atoi(pt + 1), &pt); return pt; }
void CAESinkALSA::EnumerateDevice(AEDeviceInfoList &list, const std::string &device, const std::string &description, snd_config_t *config) { snd_pcm_t *pcmhandle = NULL; if (!OpenPCMDevice(device, "", ALSA_MAX_CHANNELS, &pcmhandle, config)) return; snd_pcm_info_t *pcminfo; snd_pcm_info_alloca(&pcminfo); memset(pcminfo, 0, snd_pcm_info_sizeof()); int err = snd_pcm_info(pcmhandle, pcminfo); if (err < 0) { CLog::Log(LOGINFO, "CAESinkALSA - Unable to get pcm_info for \"%s\"", device.c_str()); snd_pcm_close(pcmhandle); } int cardNr = snd_pcm_info_get_card(pcminfo); CAEDeviceInfo info; info.m_deviceName = device; info.m_deviceType = AEDeviceTypeFromName(device); if (cardNr >= 0) { /* "HDA NVidia", "HDA Intel", "HDA ATI HDMI", "SB Live! 24-bit External", ... */ char *cardName; if (snd_card_get_name(cardNr, &cardName) == 0) info.m_displayName = cardName; if (info.m_deviceType == AE_DEVTYPE_HDMI && info.m_displayName.size() > 5 && info.m_displayName.substr(info.m_displayName.size()-5) == " HDMI") { /* We already know this is HDMI, strip it */ info.m_displayName.erase(info.m_displayName.size()-5); } /* "CONEXANT Analog", "USB Audio", "HDMI 0", "ALC889 Digital" ... */ std::string pcminfoName = snd_pcm_info_get_name(pcminfo); /* * Filter "USB Audio", in those cases snd_card_get_name() is more * meaningful already */ if (pcminfoName != "USB Audio") info.m_displayNameExtra = pcminfoName; if (info.m_deviceType == AE_DEVTYPE_HDMI) { /* replace, this was likely "HDMI 0" */ info.m_displayNameExtra = "HDMI"; int dev = snd_pcm_info_get_device(pcminfo); if (dev >= 0) { /* lets see if we can get ELD info */ snd_ctl_t *ctlhandle; std::stringstream sstr; sstr << "hw:" << cardNr; std::string strHwName = sstr.str(); if (snd_ctl_open_lconf(&ctlhandle, strHwName.c_str(), 0, config) == 0) { snd_hctl_t *hctl; if (snd_hctl_open_ctl(&hctl, ctlhandle) == 0) { snd_hctl_load(hctl); bool badHDMI = false; if (!GetELD(hctl, dev, info, badHDMI)) CLog::Log(LOGDEBUG, "CAESinkALSA - Unable to obtain ELD information for device \"%s\" (not supported by device, or kernel older than 3.2)", device.c_str()); /* snd_hctl_close also closes ctlhandle */ snd_hctl_close(hctl); if (badHDMI) { /* * Warn about disconnected devices, but keep them enabled * Detection can go wrong on Intel, Nvidia and on all * AMD (fglrx) hardware, so it is not safe to close those * handles */ CLog::Log(LOGDEBUG, "CAESinkALSA - HDMI device \"%s\" may be unconnected (no ELD data)", device.c_str()); } } else { snd_ctl_close(ctlhandle); } } } } else if (info.m_deviceType == AE_DEVTYPE_IEC958) { /* append instead of replace, pcminfoName is useful for S/PDIF */ if (!info.m_displayNameExtra.empty()) info.m_displayNameExtra += ' '; info.m_displayNameExtra += "S/PDIF"; } else if (info.m_displayNameExtra.empty()) { /* for USB audio, it gets a bit confusing as there is * - "SB Live! 24-bit External" * - "SB Live! 24-bit External, S/PDIF" * so add "Analog" qualifier to the first one */ info.m_displayNameExtra = "Analog"; } /* "default" is a device that will be used for all inputs, while * "@" will be mangled to front/default/surroundXX as necessary */ if (device == "@" || device == "default") { /* Make it "Default (whatever)" */ info.m_displayName = "Default (" + info.m_displayName + (info.m_displayNameExtra.empty() ? "" : " " + info.m_displayNameExtra + ")"); info.m_displayNameExtra = ""; } } else { /* virtual devices: "default", "pulse", ... */ /* description can be e.g. "PulseAudio Sound Server" - for hw devices it is * normally uninteresting, like "HDMI Audio Output" or "Default Audio Device", * so we only use it for virtual devices that have no better display name */ info.m_displayName = description; } snd_pcm_hw_params_t *hwparams; snd_pcm_hw_params_alloca(&hwparams); memset(hwparams, 0, snd_pcm_hw_params_sizeof()); /* ensure we can get a playback configuration for the device */ if (snd_pcm_hw_params_any(pcmhandle, hwparams) < 0) { CLog::Log(LOGINFO, "CAESinkALSA - No playback configurations available for device \"%s\"", device.c_str()); snd_pcm_close(pcmhandle); return; } /* detect the available sample rates */ for (unsigned int *rate = ALSASampleRateList; *rate != 0; ++rate) if (snd_pcm_hw_params_test_rate(pcmhandle, hwparams, *rate, 0) >= 0) info.m_sampleRates.push_back(*rate); /* detect the channels available */ int channels = 0; for (int i = ALSA_MAX_CHANNELS; i >= 1; --i) { /* Reopen the device if needed on the special "surroundXX" cases */ if (info.m_deviceType == AE_DEVTYPE_PCM && (i == 8 || i == 6 || i == 4)) OpenPCMDevice(device, "", i, &pcmhandle, config); if (snd_pcm_hw_params_test_channels(pcmhandle, hwparams, i) >= 0) { channels = i; break; } } if (device == "default" && channels == 2) { /* This looks like the ALSA standard default stereo dmix device, we * probably want to use "@" instead to get surroundXX. */ snd_pcm_close(pcmhandle); EnumerateDevice(list, "@", description, config); return; } CAEChannelInfo alsaChannels; for (int i = 0; i < channels; ++i) { if (!info.m_channels.HasChannel(ALSAChannelMap[i])) info.m_channels += ALSAChannelMap[i]; alsaChannels += ALSAChannelMap[i]; } /* remove the channels from m_channels that we cant use */ info.m_channels.ResolveChannels(alsaChannels); /* detect the PCM sample formats that are available */ for (enum AEDataFormat i = AE_FMT_MAX; i > AE_FMT_INVALID; i = (enum AEDataFormat)((int)i - 1)) { if (AE_IS_RAW(i) || i == AE_FMT_MAX) continue; snd_pcm_format_t fmt = AEFormatToALSAFormat(i); if (fmt == SND_PCM_FORMAT_UNKNOWN) continue; if (snd_pcm_hw_params_test_format(pcmhandle, hwparams, fmt) >= 0) info.m_dataFormats.push_back(i); } snd_pcm_close(pcmhandle); list.push_back(info); }
QList<QByteArray> QAudioDeviceInfoInternal::availableDevices(QAudio::Mode mode) { QList<QByteArray> allDevices; 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); allDevices.append(deviceName.toLocal8Bit().constData()); if (deviceDescription.contains(QLatin1String("Default Audio Device"))) devices.append(deviceName.toLocal8Bit().constData()); } 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"); } #else int idx = 0; char* name; while(snd_card_get_name(idx,&name) == 0) { devices.append(name); idx++; } if (idx > 0) devices.append("default"); #endif #if (!defined(Q_WS_MAEMO_5) && !defined(Q_WS_MAEMO_6)) if (devices.size() == 0 && allDevices.size() > 0) return allDevices; #endif return devices; }
bool QAudioDeviceInfoInternal::testSettings(const QAudioFormat& format) const { // Set nearest to closest settings that do work. // See if what is in settings will work (return value). int err = 0; snd_pcm_t* handle; snd_pcm_hw_params_t *params; QString dev = device; QList<QByteArray> devices = QAudioDeviceInfoInternal::availableDevices(QAudio::AudioOutput); if(dev.compare(QLatin1String("default")) == 0) { #if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14) dev = QLatin1String(devices.first().constData()); #else dev = QLatin1String("hw:0,0"); #endif } else { #if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14) dev = device; #else int idx = 0; char *name; QString shortName = device.mid(device.indexOf(QLatin1String("="),0)+1); while(snd_card_get_name(idx,&name) == 0) { if(shortName.compare(QLatin1String(name)) == 0) break; idx++; } dev = QString(QLatin1String("hw:%1,0")).arg(idx); #endif } if(mode == QAudio::AudioOutput) { err=snd_pcm_open( &handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_PLAYBACK,0); } else { err=snd_pcm_open( &handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_CAPTURE,0); } if(err < 0) { handle = 0; return false; } bool testChannel = false; bool testCodec = false; bool testFreq = false; bool testType = false; bool testSize = false; int dir = 0; snd_pcm_nonblock( handle, 0 ); snd_pcm_hw_params_alloca( ¶ms ); snd_pcm_hw_params_any( handle, params ); // set the values! snd_pcm_hw_params_set_channels(handle,params,format.channels()); snd_pcm_hw_params_set_rate(handle,params,format.frequency(),dir); err = -1; switch(format.sampleSize()) { case 8: if(format.sampleType() == QAudioFormat::SignedInt) err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S8); else if(format.sampleType() == QAudioFormat::UnSignedInt) err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U8); break; case 16: if(format.sampleType() == QAudioFormat::SignedInt) { if(format.byteOrder() == QAudioFormat::LittleEndian) err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S16_LE); else if(format.byteOrder() == QAudioFormat::BigEndian) err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S16_BE); } else if(format.sampleType() == QAudioFormat::UnSignedInt) { if(format.byteOrder() == QAudioFormat::LittleEndian) err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U16_LE); else if(format.byteOrder() == QAudioFormat::BigEndian) err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U16_BE); } break; case 32: if(format.sampleType() == QAudioFormat::SignedInt) { if(format.byteOrder() == QAudioFormat::LittleEndian) err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S32_LE); else if(format.byteOrder() == QAudioFormat::BigEndian) err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S32_BE); } else if(format.sampleType() == QAudioFormat::UnSignedInt) { if(format.byteOrder() == QAudioFormat::LittleEndian) err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U32_LE); else if(format.byteOrder() == QAudioFormat::BigEndian) err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U32_BE); } } // For now, just accept only audio/pcm codec if(!format.codec().startsWith(QLatin1String("audio/pcm"))) { err=-1; } else testCodec = true; if(err>=0 && format.channels() != -1) { err = snd_pcm_hw_params_test_channels(handle,params,format.channels()); if(err>=0) err = snd_pcm_hw_params_set_channels(handle,params,format.channels()); if(err>=0) testChannel = true; } if(err>=0 && format.frequency() != -1) { err = snd_pcm_hw_params_test_rate(handle,params,format.frequency(),0); if(err>=0) err = snd_pcm_hw_params_set_rate(handle,params,format.frequency(),dir); if(err>=0) testFreq = true; } if((err>=0 && format.sampleSize() != -1) && (format.sampleType() != QAudioFormat::Unknown)) { switch(format.sampleSize()) { case 8: if(format.sampleType() == QAudioFormat::SignedInt) err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S8); else if(format.sampleType() == QAudioFormat::UnSignedInt) err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U8); break; case 16: if(format.sampleType() == QAudioFormat::SignedInt) { if(format.byteOrder() == QAudioFormat::LittleEndian) err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S16_LE); else if(format.byteOrder() == QAudioFormat::BigEndian) err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S16_BE); } else if(format.sampleType() == QAudioFormat::UnSignedInt) { if(format.byteOrder() == QAudioFormat::LittleEndian) err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U16_LE); else if(format.byteOrder() == QAudioFormat::BigEndian) err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U16_BE); } break; case 32: if(format.sampleType() == QAudioFormat::SignedInt) { if(format.byteOrder() == QAudioFormat::LittleEndian) err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S32_LE); else if(format.byteOrder() == QAudioFormat::BigEndian) err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S32_BE); } else if(format.sampleType() == QAudioFormat::UnSignedInt) { if(format.byteOrder() == QAudioFormat::LittleEndian) err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U32_LE); else if(format.byteOrder() == QAudioFormat::BigEndian) err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U32_BE); } } if(err>=0) { testSize = true; testType = true; } } if(err>=0) err = snd_pcm_hw_params(handle, params); if(err == 0) { // settings work // close() if(handle) snd_pcm_close(handle); return true; } if(handle) snd_pcm_close(handle); return false; }
bool QAudioInputPrivate::open() { #ifdef DEBUG_AUDIO QTime now(QTime::currentTime()); qDebug()<<now.second()<<"s "<<now.msec()<<"ms :open()"; #endif clockStamp.restart(); timeStamp.restart(); elapsedTimeOffset = 0; int dir; int err=-1; int count=0; unsigned int freakuency=settings.frequency(); QString dev = QString(QLatin1String(m_device.constData())); QList<QByteArray> devices = QAudioDeviceInfoInternal::availableDevices(QAudio::AudioInput); if(dev.compare(QLatin1String("default")) == 0) { #if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14) dev = QLatin1String(devices.first()); #else dev = QLatin1String("hw:0,0"); #endif } else { #if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14) dev = QLatin1String(m_device); #else int idx = 0; char *name; QString shortName = QLatin1String(m_device.mid(m_device.indexOf('=',0)+1).constData()); while(snd_card_get_name(idx,&name) == 0) { if(qstrncmp(shortName.toLocal8Bit().constData(),name,shortName.length()) == 0) break; idx++; } dev = QString(QLatin1String("hw:%1,0")).arg(idx); #endif } // Step 1: try and open the device while((count < 5) && (err < 0)) { err=snd_pcm_open(&handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_CAPTURE,0); if(err < 0) count++; } if (( err < 0)||(handle == 0)) { errorState = QAudio::OpenError; deviceState = QAudio::StoppedState; emit stateChanged(deviceState); return false; } snd_pcm_nonblock( handle, 0 ); // Step 2: Set the desired HW parameters. snd_pcm_hw_params_alloca( &hwparams ); bool fatal = false; QString errMessage; unsigned int chunks = 8; err = snd_pcm_hw_params_any( handle, hwparams ); if ( err < 0 ) { fatal = true; errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_any: err = %1").arg(err); } if ( !fatal ) { err = snd_pcm_hw_params_set_rate_resample( handle, hwparams, 1 ); if ( err < 0 ) { fatal = true; errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_rate_resample: err = %1").arg(err); } } if ( !fatal ) { err = snd_pcm_hw_params_set_access( handle, hwparams, access ); if ( err < 0 ) { fatal = true; errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_access: err = %1").arg(err); } } if ( !fatal ) { err = setFormat(); if ( err < 0 ) { fatal = true; errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_format: err = %1").arg(err); } } if ( !fatal ) { err = snd_pcm_hw_params_set_channels( handle, hwparams, (unsigned int)settings.channels() ); if ( err < 0 ) { fatal = true; errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_channels: err = %1").arg(err); } } if ( !fatal ) { err = snd_pcm_hw_params_set_rate_near( handle, hwparams, &freakuency, 0 ); if ( err < 0 ) { fatal = true; errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_rate_near: err = %1").arg(err); } } if ( !fatal ) { err = snd_pcm_hw_params_set_buffer_time_near(handle, hwparams, &buffer_time, &dir); if ( err < 0 ) { fatal = true; errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_buffer_time_near: err = %1").arg(err); } } if ( !fatal ) { err = snd_pcm_hw_params_set_period_time_near(handle, hwparams, &period_time, &dir); if ( err < 0 ) { fatal = true; errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_period_time_near: err = %1").arg(err); } } if ( !fatal ) { err = snd_pcm_hw_params_set_periods_near(handle, hwparams, &chunks, &dir); if ( err < 0 ) { fatal = true; errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_periods_near: err = %1").arg(err); } } if ( !fatal ) { err = snd_pcm_hw_params(handle, hwparams); if ( err < 0 ) { fatal = true; errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params: err = %1").arg(err); } } if( err < 0) { qWarning()<<errMessage; errorState = QAudio::OpenError; deviceState = QAudio::StoppedState; emit stateChanged(deviceState); return false; } snd_pcm_hw_params_get_buffer_size(hwparams,&buffer_frames); buffer_size = snd_pcm_frames_to_bytes(handle,buffer_frames); snd_pcm_hw_params_get_period_size(hwparams,&period_frames, &dir); period_size = snd_pcm_frames_to_bytes(handle,period_frames); snd_pcm_hw_params_get_buffer_time(hwparams,&buffer_time, &dir); snd_pcm_hw_params_get_period_time(hwparams,&period_time, &dir); // Step 3: Set the desired SW parameters. snd_pcm_sw_params_t *swparams; snd_pcm_sw_params_alloca(&swparams); snd_pcm_sw_params_current(handle, swparams); snd_pcm_sw_params_set_start_threshold(handle,swparams,period_frames); snd_pcm_sw_params_set_stop_threshold(handle,swparams,buffer_frames); snd_pcm_sw_params_set_avail_min(handle, swparams,period_frames); snd_pcm_sw_params(handle, swparams); // Step 4: Prepare audio if(audioBuffer == 0) audioBuffer = new char[buffer_size]; snd_pcm_prepare( handle ); snd_pcm_start(handle); // Step 5: Setup timer bytesAvailable = bytesReady(); if(pullMode) connect(audioSource,SIGNAL(readyRead()),this,SLOT(userFeed())); // Step 6: Start audio processing chunks = buffer_size/period_size; timer->start(period_time*chunks/2000); errorState = QAudio::NoError; deviceState = QAudio::ActiveState; totalTimeValue = 0; return true; }
// Initialize sound card // Return value: // 0 = opened ok // -1 = opened, but suboptimal // -2 = couldn't be opened int initPcmDevice(char *wanteddevname) { snd_pcm_hw_params_t *hwparams; char pcm_name[30]; unsigned int exact_rate = 44100; int card; gboolean found; char *cardname; pcm.BufferDrop = FALSE; snd_pcm_hw_params_alloca(&hwparams); card = -1; found = FALSE; if (strcmp(wanteddevname,"default") == 0) { found=TRUE; } else { do { snd_card_next(&card); if (card != -1) { snd_card_get_name(card,&cardname); if (strcmp(cardname, wanteddevname) == 0) { found=TRUE; break; } } } while (card != -1); } if (!found) { perror("Device disconnected?\n"); return(-2); } if (strcmp(wanteddevname,"default") == 0) { sprintf(pcm_name,"default"); } else { sprintf(pcm_name,"hw:%d",card); } if (snd_pcm_open(&pcm.handle, pcm_name, SND_PCM_STREAM_CAPTURE, 0) < 0) { perror("ALSA: Error opening PCM device"); return(-2); } /* Init hwparams with full configuration space */ if (snd_pcm_hw_params_any(pcm.handle, hwparams) < 0) { perror("ALSA: Can not configure this PCM device."); return(-2); } if (snd_pcm_hw_params_set_access(pcm.handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) { perror("ALSA: Error setting interleaved access."); return(-2); } if (snd_pcm_hw_params_set_format(pcm.handle, hwparams, SND_PCM_FORMAT_S16_LE) < 0) { perror("ALSA: Error setting format S16_LE."); return(-2); } if (snd_pcm_hw_params_set_rate_near(pcm.handle, hwparams, &exact_rate, 0) < 0) { perror("ALSA: Error setting sample rate."); return(-2); } // Try stereo first if (snd_pcm_hw_params_set_channels(pcm.handle, hwparams, 2) < 0) { // Fall back to mono if (snd_pcm_hw_params_set_channels(pcm.handle, hwparams, 1) < 0) { perror("ALSA: Error setting channels."); return(-2); } } if (snd_pcm_hw_params(pcm.handle, hwparams) < 0) { perror("ALSA: Error setting HW params."); return(-2); } pcm.Buffer = calloc( BUFLEN, sizeof(gint16)); memset(pcm.Buffer, 0, BUFLEN); if (exact_rate != 44100) { fprintf(stderr, "ALSA: Got %d Hz instead of 44100. Expect artifacts.\n", exact_rate); return(-1); } return(0); }
static void GetDevicesForCard( module_config_t *p_item, int i_card ) { int i_pcm_device = -1; int i_err = 0; snd_pcm_info_t *p_pcm_info; snd_ctl_t *p_ctl; char psz_dev[64]; char *psz_card_name; sprintf( psz_dev, "hw:%i", i_card ); if( ( i_err = snd_ctl_open( &p_ctl, psz_dev, 0 ) ) < 0 ) return; if( ( i_err = snd_card_get_name( i_card, &psz_card_name ) ) != 0) psz_card_name = _("Unknown soundcard"); snd_pcm_info_alloca( &p_pcm_info ); for (;;) { char *psz_device, *psz_descr; if( ( i_err = snd_ctl_pcm_next_device( p_ctl, &i_pcm_device ) ) < 0 ) i_pcm_device = -1; if( i_pcm_device < 0 ) break; snd_pcm_info_set_device( p_pcm_info, i_pcm_device ); snd_pcm_info_set_subdevice( p_pcm_info, 0 ); snd_pcm_info_set_stream( p_pcm_info, SND_PCM_STREAM_PLAYBACK ); if( ( i_err = snd_ctl_pcm_info( p_ctl, p_pcm_info ) ) < 0 ) { if( i_err != -ENOENT ) { /*printf( "get_devices_for_card(): " "snd_ctl_pcm_info() " "failed (%d:%d): %s.\n", i_card, i_pcm_device, snd_strerror( -i_err ) );*/ } continue; } if( asprintf( &psz_device, "hw:%d,%d", i_card, i_pcm_device ) == -1 ) break; if( asprintf( &psz_descr, "%s: %s (%s)", psz_card_name, snd_pcm_info_get_name(p_pcm_info), psz_device ) == -1 ) { free( psz_device ); break; } p_item->ppsz_list = (char **)realloc( p_item->ppsz_list, (p_item->i_list + 2) * sizeof(char *) ); p_item->ppsz_list_text = (char **)realloc( p_item->ppsz_list_text, (p_item->i_list + 2) * sizeof(char *) ); p_item->ppsz_list[ p_item->i_list ] = psz_device; p_item->ppsz_list_text[ p_item->i_list ] = psz_descr; p_item->i_list++; p_item->ppsz_list[ p_item->i_list ] = NULL; p_item->ppsz_list_text[ p_item->i_list ] = NULL; } snd_ctl_close( p_ctl ); }