static int alsa_get_max_channel_count(cubeb * ctx, uint32_t * max_channels) { int rv; cubeb_stream * stm; snd_pcm_hw_params_t* hw_params; cubeb_stream_params params; params.rate = 44100; params.format = CUBEB_SAMPLE_FLOAT32NE; params.channels = 2; snd_pcm_hw_params_alloca(&hw_params); assert(ctx); rv = alsa_stream_init(ctx, &stm, "", params, 100, NULL, NULL, NULL); if (rv != CUBEB_OK) { return CUBEB_ERROR; } rv = snd_pcm_hw_params_any(stm->pcm, hw_params); if (rv < 0) { return CUBEB_ERROR; } rv = snd_pcm_hw_params_get_channels_max(hw_params, max_channels); if (rv < 0) { return CUBEB_ERROR; } alsa_stream_destroy(stm); return CUBEB_OK; }
optional<int> AudioOutputDeviceAlsa::ParameterChannels::RangeMaxAsInt(std::map<String,String> Parameters) { uint channels = 100; if (!Parameters.count("CARD")) return channels; // obtain information from given sound card String pcm_name = "hw:" + Parameters["CARD"]; snd_pcm_t* pcm_handle = NULL; if (snd_pcm_open(&pcm_handle, pcm_name.c_str(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK) < 0) return channels; snd_pcm_hw_params_t* hwparams; snd_pcm_hw_params_alloca(&hwparams); if (snd_pcm_hw_params_any(pcm_handle, hwparams) < 0) { snd_pcm_close(pcm_handle); return channels; } if (snd_pcm_hw_params_get_channels_max(hwparams, &channels) < 0) { snd_pcm_close(pcm_handle); return channels; } snd_pcm_close(pcm_handle); return channels; }
void DAUDIO_GetFormats(INT32 mixerIndex, INT32 deviceID, int isSource, void* creator) { snd_pcm_t* handle; snd_pcm_format_mask_t* formatMask; snd_pcm_format_t format; snd_pcm_hw_params_t* hwParams; int handledBits[MAX_BIT_INDEX+1]; int ret; int sampleSizeInBytes, significantBits, isSigned, isBigEndian, enc; int origSampleSizeInBytes, origSignificantBits; int channels, minChannels, maxChannels; int rate, bitIndex; for (bitIndex = 0; bitIndex <= MAX_BIT_INDEX; bitIndex++) handledBits[bitIndex] = FALSE; if (openPCMfromDeviceID(deviceID, &handle, isSource, TRUE /*query hardware*/) < 0) { return; } ret = snd_pcm_format_mask_malloc(&formatMask); if (ret != 0) { ERROR1("snd_pcm_format_mask_malloc returned error %d\n", ret); } else { ret = snd_pcm_hw_params_malloc(&hwParams); if (ret != 0) { ERROR1("snd_pcm_hw_params_malloc returned error %d\n", ret); } else { ret = snd_pcm_hw_params_any(handle, hwParams); if (ret != 0) { ERROR1("snd_pcm_hw_params_any returned error %d\n", ret); } } snd_pcm_hw_params_get_format_mask(hwParams, formatMask); #ifdef ALSA_PCM_NEW_HW_PARAMS_API if (ret == 0) { ret = snd_pcm_hw_params_get_channels_min(hwParams, &minChannels); if (ret != 0) { ERROR1("snd_pcm_hw_params_get_channels_min returned error %d\n", ret); } } if (ret == 0) { ret = snd_pcm_hw_params_get_channels_max(hwParams, &maxChannels); if (ret != 0) { ERROR1("snd_pcm_hw_params_get_channels_max returned error %d\n", ret); } } #else minChannels = snd_pcm_hw_params_get_channels_min(hwParams); maxChannels = snd_pcm_hw_params_get_channels_max(hwParams); if (minChannels > maxChannels) { ERROR2("MinChannels=%d, maxChannels=%d\n", minChannels, maxChannels); } #endif // since we queried the hw: device, for many soundcards, it will only // report the maximum number of channels (which is the only way to talk // to the hw: device). Since we will, however, open the plughw: device // when opening the Source/TargetDataLine, we can safely assume that // also the channels 1..maxChannels are available. #ifdef ALSA_PCM_USE_PLUGHW minChannels = 1; #endif if (ret == 0) { // plughw: supports any sample rate rate = -1; for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) { if (snd_pcm_format_mask_test(formatMask, format)) { // format exists if (getFormatFromAlsaFormat(format, &origSampleSizeInBytes, &origSignificantBits, &isSigned, &isBigEndian, &enc)) { // now if we use plughw:, we can use any bit size below the // natively supported ones. Some ALSA drivers only support the maximum // bit size, so we add any sample rates below the reported one. // E.g. this iteration reports support for 16-bit. // getBitIndex will return 2, so it will add entries for // 16-bit (bitIndex=2) and in the next do-while loop iteration, // it will decrease bitIndex and will therefore add 8-bit support. bitIndex = getBitIndex(origSampleSizeInBytes, origSignificantBits); do { if (bitIndex == 0 || bitIndex == MAX_BIT_INDEX || !handledBits[bitIndex]) { handledBits[bitIndex] = TRUE; sampleSizeInBytes = getSampleSizeInBytes(bitIndex, origSampleSizeInBytes); significantBits = getSignificantBits(bitIndex, origSignificantBits); if (maxChannels - minChannels > MAXIMUM_LISTED_CHANNELS) { // avoid too many channels explicitly listed // just add -1, min, and max DAUDIO_AddAudioFormat(creator, significantBits, -1, -1, rate, enc, isSigned, isBigEndian); DAUDIO_AddAudioFormat(creator, significantBits, sampleSizeInBytes * minChannels, minChannels, rate, enc, isSigned, isBigEndian); DAUDIO_AddAudioFormat(creator, significantBits, sampleSizeInBytes * maxChannels, maxChannels, rate, enc, isSigned, isBigEndian); } else { for (channels = minChannels; channels <= maxChannels; channels++) { DAUDIO_AddAudioFormat(creator, significantBits, (channels < 0)?-1:(sampleSizeInBytes * channels), channels, rate, enc, isSigned, isBigEndian); } } } #ifndef ALSA_PCM_USE_PLUGHW // without plugin, do not add fake formats break; #endif } while (--bitIndex > 0); } else { TRACE1("could not get format from alsa for format %d\n", format); } } else { //TRACE1("Format %d not supported\n", format); } } // for loop snd_pcm_hw_params_free(hwParams); } snd_pcm_format_mask_free(formatMask); } snd_pcm_close(handle); }
/** * ags_devout_pcm_info: * @soundcard: the #AgsSoundcard * @card_id: alsa identifier * @channels_min: minimum channels supported * @channels_max: maximum channels supported * @rate_min: minimum samplerate supported * @rate_max: maximum samplerate supported * @buffer_size_min: minimum buffer size supported * @buffer_size_max maximum buffer size supported * @error: on success %NULL * * List soundcard settings. * * Since: 0.4 */ void ags_devout_pcm_info(AgsSoundcard *soundcard, char *card_id, guint *channels_min, guint *channels_max, guint *rate_min, guint *rate_max, guint *buffer_size_min, guint *buffer_size_max, GError **error) { int rc; snd_pcm_t *handle; snd_pcm_hw_params_t *params; unsigned int val; int dir; snd_pcm_uframes_t frames; int err; /* Open PCM device for playback. */ handle = NULL; rc = snd_pcm_open(&handle, card_id, SND_PCM_STREAM_PLAYBACK, 0); if(rc < 0) { g_message("unable to open pcm device: %s\n\0", snd_strerror(rc)); g_set_error(error, AGS_DEVOUT_ERROR, AGS_DEVOUT_ERROR_LOCKED_SOUNDCARD, "unable to open pcm device: %s\n\0", snd_strerror(rc)); return; } /* Allocate a hardware parameters object. */ snd_pcm_hw_params_alloca(¶ms); /* Fill it in with default values. */ snd_pcm_hw_params_any(handle, params); /* channels */ snd_pcm_hw_params_get_channels_min(params, &val); *channels_min = val; snd_pcm_hw_params_get_channels_max(params, &val); *channels_max = val; /* samplerate */ dir = 0; snd_pcm_hw_params_get_rate_min(params, &val, &dir); *rate_min = val; dir = 0; snd_pcm_hw_params_get_rate_max(params, &val, &dir); *rate_max = val; /* buffer size */ dir = 0; snd_pcm_hw_params_get_buffer_size_min(params, &frames); *buffer_size_min = frames; dir = 0; snd_pcm_hw_params_get_buffer_size_max(params, &frames); *buffer_size_max = frames; snd_pcm_close(handle); }
static int palsa_set_hw_params (ddb_waveformat_t *fmt) { snd_pcm_hw_params_t *hw_params = NULL; int err = 0; memcpy (&plugin.fmt, fmt, sizeof (ddb_waveformat_t)); if (!plugin.fmt.channels) { // generic format plugin.fmt.bps = 16; plugin.fmt.is_float = 0; plugin.fmt.channels = 2; plugin.fmt.samplerate = 44100; plugin.fmt.channelmask = 3; } snd_pcm_nonblock(audio, 0); snd_pcm_drain (audio); snd_pcm_nonblock(audio, 1); if ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0) { fprintf (stderr, "cannot allocate hardware parameter structure (%s)\n", snd_strerror (err)); goto error; } if ((err = snd_pcm_hw_params_any (audio, hw_params)) < 0) { fprintf (stderr, "cannot initialize hardware parameter structure (%s)\n", snd_strerror (err)); goto error; } if ((err = snd_pcm_hw_params_set_access (audio, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { fprintf (stderr, "cannot set access type (%s)\n", snd_strerror (err)); goto error; } snd_pcm_format_t sample_fmt; switch (plugin.fmt.bps) { case 8: sample_fmt = SND_PCM_FORMAT_S8; break; case 16: #if WORDS_BIGENDIAN sample_fmt = SND_PCM_FORMAT_S16_BE; #else sample_fmt = SND_PCM_FORMAT_S16_LE; #endif break; case 24: #if WORDS_BIGENDIAN sample_fmt = SND_PCM_FORMAT_S24_3BE; #else sample_fmt = SND_PCM_FORMAT_S24_3LE; #endif break; case 32: if (plugin.fmt.is_float) { #if WORDS_BIGENDIAN sample_fmt = SND_PCM_FORMAT_FLOAT_BE; #else sample_fmt = SND_PCM_FORMAT_FLOAT_LE; #endif } else { #if WORDS_BIGENDIAN sample_fmt = SND_PCM_FORMAT_S32_BE; #else sample_fmt = SND_PCM_FORMAT_S32_LE; #endif } break; } if ((err = snd_pcm_hw_params_set_format (audio, hw_params, sample_fmt)) < 0) { fprintf (stderr, "cannot set sample format to %d bps (error: %s), trying all supported formats\n", plugin.fmt.bps, snd_strerror (err)); int fmt_cnt[] = { 16, 24, 32, 32, 8 }; #if WORDS_BIGENDIAN int fmt[] = { SND_PCM_FORMAT_S16_BE, SND_PCM_FORMAT_S24_3BE, SND_PCM_FORMAT_S32_BE, SND_PCM_FORMAT_FLOAT_BE, SND_PCM_FORMAT_S8, -1 }; #else int fmt[] = { SND_PCM_FORMAT_S16_LE, SND_PCM_FORMAT_S24_3LE, SND_PCM_FORMAT_S32_LE, SND_PCM_FORMAT_FLOAT_LE, SND_PCM_FORMAT_S8, -1 }; #endif // 1st try formats with higher bps int i = 0; for (i = 0; fmt[i] != -1; i++) { if (fmt[i] != sample_fmt && fmt_cnt[i] > plugin.fmt.bps) { if (snd_pcm_hw_params_set_format (audio, hw_params, fmt[i]) >= 0) { fprintf (stderr, "Found compatible format %d bps\n", fmt_cnt[i]); sample_fmt = fmt[i]; break; } } } if (fmt[i] == -1) { // next try formats with lower bps i = 0; for (i = 0; fmt[i] != -1; i++) { if (fmt[i] != sample_fmt && fmt_cnt[i] < plugin.fmt.bps) { if (snd_pcm_hw_params_set_format (audio, hw_params, fmt[i]) >= 0) { fprintf (stderr, "Found compatible format %d bps\n", fmt_cnt[i]); sample_fmt = fmt[i]; break; } } } } if (fmt[i] == -1) { fprintf (stderr, "Fallback format could not be found\n"); goto error; } } snd_pcm_hw_params_get_format (hw_params, &sample_fmt); trace ("chosen sample format: %04Xh\n", (int)sample_fmt); int val = plugin.fmt.samplerate; int ret = 0; if ((err = snd_pcm_hw_params_set_rate_resample (audio, hw_params, conf_alsa_resample)) < 0) { fprintf (stderr, "cannot setup resampling (%s)\n", snd_strerror (err)); goto error; } if ((err = snd_pcm_hw_params_set_rate_near (audio, hw_params, &val, &ret)) < 0) { fprintf (stderr, "cannot set sample rate (%s)\n", snd_strerror (err)); goto error; } plugin.fmt.samplerate = val; trace ("chosen samplerate: %d Hz\n", val); int chanmin, chanmax; snd_pcm_hw_params_get_channels_min (hw_params, &chanmin); snd_pcm_hw_params_get_channels_max (hw_params, &chanmax); trace ("minchan: %d, maxchan: %d\n", chanmin, chanmax); int nchan = plugin.fmt.channels; if (nchan > chanmax) { nchan = chanmax; } else if (nchan < chanmin) { nchan = chanmin; } trace ("setting chan=%d\n", nchan); if ((err = snd_pcm_hw_params_set_channels (audio, hw_params, nchan)) < 0) { fprintf (stderr, "cannot set channel count (%s)\n", snd_strerror (err)); } snd_pcm_hw_params_get_channels (hw_params, &nchan); trace ("alsa channels: %d\n", nchan); req_buffer_size = deadbeef->conf_get_int ("alsa.buffer", DEFAULT_BUFFER_SIZE); req_period_size = deadbeef->conf_get_int ("alsa.period", DEFAULT_PERIOD_SIZE); buffer_size = req_buffer_size; period_size = req_period_size; trace ("trying buffer size: %d frames\n", (int)buffer_size); trace ("trying period size: %d frames\n", (int)period_size); snd_pcm_hw_params_set_buffer_size_near (audio, hw_params, &buffer_size); snd_pcm_hw_params_set_period_size_near (audio, hw_params, &period_size, NULL); trace ("alsa buffer size: %d frames\n", (int)buffer_size); trace ("alsa period size: %d frames\n", (int)period_size); if ((err = snd_pcm_hw_params (audio, hw_params)) < 0) { fprintf (stderr, "cannot set parameters (%s)\n", snd_strerror (err)); goto error; } plugin.fmt.is_float = 0; switch (sample_fmt) { case SND_PCM_FORMAT_S8: plugin.fmt.bps = 8; break; case SND_PCM_FORMAT_S16_BE: case SND_PCM_FORMAT_S16_LE: plugin.fmt.bps = 16; break; case SND_PCM_FORMAT_S24_3BE: case SND_PCM_FORMAT_S24_3LE: plugin.fmt.bps = 24; break; case SND_PCM_FORMAT_S32_BE: case SND_PCM_FORMAT_S32_LE: plugin.fmt.bps = 32; break; case SND_PCM_FORMAT_FLOAT_LE: case SND_PCM_FORMAT_FLOAT_BE: plugin.fmt.bps = 32; plugin.fmt.is_float = 1; break; } trace ("chosen bps: %d (%s)\n", plugin.fmt.bps, plugin.fmt.is_float ? "float" : "int"); plugin.fmt.channels = nchan; plugin.fmt.channelmask = 0; if (nchan == 1) { plugin.fmt.channelmask = DDB_SPEAKER_FRONT_LEFT; } if (nchan == 2) { plugin.fmt.channelmask = DDB_SPEAKER_FRONT_LEFT | DDB_SPEAKER_FRONT_RIGHT; } if (nchan == 3) { plugin.fmt.channelmask = DDB_SPEAKER_FRONT_LEFT | DDB_SPEAKER_FRONT_RIGHT | DDB_SPEAKER_LOW_FREQUENCY; } if (nchan == 4) { plugin.fmt.channelmask = DDB_SPEAKER_FRONT_LEFT | DDB_SPEAKER_FRONT_RIGHT | DDB_SPEAKER_BACK_LEFT | DDB_SPEAKER_BACK_RIGHT; } if (nchan == 5) { plugin.fmt.channelmask = DDB_SPEAKER_FRONT_LEFT | DDB_SPEAKER_FRONT_RIGHT | DDB_SPEAKER_BACK_LEFT | DDB_SPEAKER_BACK_RIGHT | DDB_SPEAKER_FRONT_CENTER; } if (nchan == 6) { plugin.fmt.channelmask = DDB_SPEAKER_FRONT_LEFT | DDB_SPEAKER_FRONT_RIGHT | DDB_SPEAKER_BACK_LEFT | DDB_SPEAKER_BACK_RIGHT | DDB_SPEAKER_FRONT_CENTER | DDB_SPEAKER_LOW_FREQUENCY; } if (nchan == 7) { plugin.fmt.channelmask = DDB_SPEAKER_FRONT_LEFT | DDB_SPEAKER_FRONT_RIGHT | DDB_SPEAKER_BACK_LEFT | DDB_SPEAKER_BACK_RIGHT | DDB_SPEAKER_FRONT_CENTER | DDB_SPEAKER_SIDE_LEFT | DDB_SPEAKER_SIDE_RIGHT; } if (nchan == 8) { plugin.fmt.channelmask = DDB_SPEAKER_FRONT_LEFT | DDB_SPEAKER_FRONT_RIGHT | DDB_SPEAKER_BACK_LEFT | DDB_SPEAKER_BACK_RIGHT | DDB_SPEAKER_FRONT_CENTER | DDB_SPEAKER_SIDE_LEFT | DDB_SPEAKER_SIDE_RIGHT | DDB_SPEAKER_LOW_FREQUENCY; } error: if (err < 0) { memset (&plugin.fmt, 0, sizeof (ddb_waveformat_t)); } if (hw_params) { snd_pcm_hw_params_free (hw_params); } return err; }
/* ------- PCM INITS --------------------------------- */ static int set_hwparams(snd_pcm_t *handle, snd_pcm_hw_params_t *params,int *chs) { #ifndef ALSAAPI9 unsigned int rrate; int err, dir; int channels_allocated = 0; /* choose all parameters */ err = snd_pcm_hw_params_any(handle, params); if (err < 0) { check_error(err,"Broken configuration: no configurations available"); return err; } /* set the nointerleaved read/write format */ err = snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_MMAP_NONINTERLEAVED); if (err >= 0) { #ifdef ALSAMM_DEBUG if(sys_verbose) post("Access type %s available","SND_PCM_ACCESS_MMAP_NONINTERLEAVED"); #endif } else{ check_error(err,"No Accesstype SND_PCM_ACCESS_MMAP_NONINTERLEAVED"); return err; } /* set the sample format */ err = snd_pcm_hw_params_set_format(handle, params, ALSAMM_FORMAT); if (err < 0) { check_error(err,"Sample format not available for playback"); return err; } #ifdef ALSAMM_DEBUG if(sys_verbose) post("Setting format to %s",snd_pcm_format_name(ALSAMM_FORMAT)); #endif /* first check samplerate since channels numbers are samplerate dependent (double speed) */ /* set the stream rate */ rrate = alsamm_sr; #ifdef ALSAMM_DEBUG if(sys_verbose) post("Samplerate request: %i Hz",rrate); #endif dir=-1; err = snd_pcm_hw_params_set_rate_near(handle, params, &rrate, &dir); if (err < 0) { check_error(err,"Rate not available"); return err; } if (rrate != alsamm_sr) { post("Warning: rate %iHz doesn't match requested %iHz", rrate,alsamm_sr); alsamm_sr = rrate; } else if(sys_verbose) post("Samplerate is set to %iHz",alsamm_sr); /* Info on channels */ { int maxchs,minchs,channels = *chs; if((err = snd_pcm_hw_params_get_channels_max(params, (unsigned int *)&maxchs)) < 0){ check_error(err,"Getting channels_max not available"); return err; } if((err = snd_pcm_hw_params_get_channels_min(params, (unsigned int *)&minchs)) < 0){ check_error(err,"Getting channels_min not available"); return err; } #ifdef ALSAMM_DEBUG if(sys_verbose) post("Getting channels:min=%d, max= %d for request=%d",minchs,maxchs,channels); #endif if(channels < 0)channels=maxchs; if(channels > maxchs)channels = maxchs; if(channels < minchs)channels = minchs; if(channels != *chs) post("requested channels=%d but used=%d",*chs,channels); *chs = channels; #ifdef ALSAMM_DEBUG if(sys_verbose) post("trying to use channels: %d",channels); #endif } /* set the count of channels */ err = snd_pcm_hw_params_set_channels(handle, params, *chs); if (err < 0) { check_error(err,"Channels count not available"); return err; } /* testing for channels */ if((err = snd_pcm_hw_params_get_channels(params,(unsigned int *)chs)) < 0) check_error(err,"Get channels not available"); #ifdef ALSAMM_DEBUG else if(sys_verbose) post("When setting channels count and got %d",*chs); #endif /* if buffersize is set use this instead buffertime */ if(alsamm_buffersize > 0){ #ifdef ALSAMM_DEBUG if(sys_verbose) post("hw_params: ask for max buffersize of %d samples", (unsigned int) alsamm_buffersize ); #endif alsamm_buffer_size = alsamm_buffersize; err = snd_pcm_hw_params_set_buffer_size_near(handle, params, (unsigned long *)&alsamm_buffer_size); if (err < 0) { check_error(err,"Unable to set max buffer size"); return err; } } else{ if(alsamm_buffertime <= 0) /* should never happen, but use 20ms */ alsamm_buffertime = 20000; #ifdef ALSAMM_DEBUG if(sys_verbose) post("hw_params: ask for max buffertime of %d ms", (unsigned int) (alsamm_buffertime*0.001) ); #endif err = snd_pcm_hw_params_set_buffer_time_near(handle, params, &alsamm_buffertime, &dir); if (err < 0) { check_error(err,"Unable to set max buffer time"); return err; } } err = snd_pcm_hw_params_get_buffer_time(params, (unsigned int *)&alsamm_buffertime, &dir); if (err < 0) { check_error(err,"Unable to get buffer time"); return err; } #ifdef ALSAMM_DEBUG if(sys_verbose) post("hw_params: got buffertime to %f ms", (float) (alsamm_buffertime*0.001)); #endif err = snd_pcm_hw_params_get_buffer_size(params, (unsigned long *)&alsamm_buffer_size); if (err < 0) { check_error(err,"Unable to get buffer size"); return err; } #ifdef ALSAMM_DEBUG if(sys_verbose) post("hw_params: got buffersize to %d samples",(int) alsamm_buffer_size); #endif err = snd_pcm_hw_params_get_period_size(params, (unsigned long *)&alsamm_period_size, &dir); if (err > 0) { check_error(err,"Unable to get period size"); return err; } #ifdef ALSAMM_DEBUG if(sys_verbose) post("Got period size of %d", (int) alsamm_period_size); #endif { unsigned int pmin,pmax; err = snd_pcm_hw_params_get_periods_min(params, &pmin, &dir); if (err > 0) { check_error(err,"Unable to get period size"); return err; } err = snd_pcm_hw_params_get_periods_min(params, &pmax, &dir); if (err > 0) { check_error(err,"Unable to get period size"); return err; } /* use maximum of periods */ if( alsamm_periods <= 0) alsamm_periods = pmax; alsamm_periods = (alsamm_periods > pmax)?pmax:alsamm_periods; alsamm_periods = (alsamm_periods < pmin)?pmin:alsamm_periods; err = snd_pcm_hw_params_set_periods(handle, params, alsamm_periods, dir); if (err > 0) { check_error(err,"Unable to set periods"); return err; } err = snd_pcm_hw_params_get_periods(params, &pmin, &dir); if (err > 0) { check_error(err,"Unable to get periods"); return err; } #ifdef ALSAMM_DEBUG if(sys_verbose) post("Got periods of %d, where periodsmin=%d, periodsmax=%d", alsamm_periods,pmin,pmax); #endif } /* write the parameters to device */ err = snd_pcm_hw_params(handle, params); if (err < 0) { check_error(err,"Unable to set hw params"); return err; } #endif /* ALSAAPI9 */ return 0; }
bool ALSAWriter::processParams(bool *paramsCorrected) { const unsigned chn = getParam("chn").toUInt(); const unsigned rate = getParam("rate").toUInt(); const bool resetAudio = channels != chn || sample_rate != rate; channels = chn; sample_rate = rate; if (resetAudio || err) { snd_pcm_hw_params_t *params; snd_pcm_hw_params_alloca(¶ms); close(); QString chosenDevName = devName; if (autoFindMultichannelDevice && channels > 2) { bool mustAutoFind = true, forceStereo = false; if (!snd_pcm_open(&snd, chosenDevName.toLocal8Bit(), SND_PCM_STREAM_PLAYBACK, 0)) { if (snd_pcm_type(snd) == SND_PCM_TYPE_HW) { unsigned max_chn = 0; snd_pcm_hw_params_any(snd, params); mustAutoFind = snd_pcm_hw_params_get_channels_max(params, &max_chn) || max_chn < channels; } #ifdef HAVE_CHMAP else if (paramsCorrected) { snd_pcm_chmap_query_t **chmaps = snd_pcm_query_chmaps(snd); if (chmaps) snd_pcm_free_chmaps(chmaps); else forceStereo = true; } #endif snd_pcm_close(snd); snd = NULL; } if (mustAutoFind) { QString newDevName; if (channels <= 4) newDevName = "surround40"; else if (channels <= 6) newDevName = "surround51"; else newDevName = "surround71"; if (!newDevName.isEmpty() && newDevName != chosenDevName) { if (ALSACommon::getDevices().first.contains(newDevName)) chosenDevName = newDevName; else if (forceStereo) { channels = 2; *paramsCorrected = true; } } } } if (!chosenDevName.isEmpty()) { bool sndOpen = !snd_pcm_open(&snd, chosenDevName.toLocal8Bit(), SND_PCM_STREAM_PLAYBACK, 0); if (devName != chosenDevName) { if (sndOpen) QMPlay2Core.logInfo("ALSA :: " + devName + "\" -> \"" + chosenDevName + "\""); else { sndOpen = !snd_pcm_open(&snd, devName.toLocal8Bit(), SND_PCM_STREAM_PLAYBACK, 0); QMPlay2Core.logInfo("ALSA :: " + tr("Cannot open") + " \"" + chosenDevName + "\", " + tr("back to") + " \"" + devName + "\""); } } if (sndOpen) { snd_pcm_hw_params_any(snd, params); snd_pcm_format_t fmt = SND_PCM_FORMAT_UNKNOWN; if (!snd_pcm_hw_params_test_format(snd, params, SND_PCM_FORMAT_S32)) { fmt = SND_PCM_FORMAT_S32; sample_size = 4; } else if (!snd_pcm_hw_params_test_format(snd, params, SND_PCM_FORMAT_S16)) { fmt = SND_PCM_FORMAT_S16; sample_size = 2; } else if (!snd_pcm_hw_params_test_format(snd, params, SND_PCM_FORMAT_S8)) { fmt = SND_PCM_FORMAT_S8; sample_size = 1; } unsigned delay_us = round(delay * 1000000.0); if (fmt != SND_PCM_FORMAT_UNKNOWN && set_snd_pcm_hw_params(snd, params, fmt, channels, sample_rate, delay_us)) { bool err2 = false; if (channels != chn || sample_rate != rate) { if (paramsCorrected) *paramsCorrected = true; else err2 = true; } if (!err2) { err2 = snd_pcm_hw_params(snd, params); if (err2 && paramsCorrected) //jakiś błąd, próba zmiany sample_rate { snd_pcm_hw_params_any(snd, params); err2 = snd_pcm_hw_params_set_rate_resample(snd, params, false) || !set_snd_pcm_hw_params(snd, params, fmt, channels, sample_rate, delay_us) || snd_pcm_hw_params(snd, params); if (!err2) *paramsCorrected = true; } if (!err2) { modParam("delay", delay_us / 1000000.0); if (paramsCorrected && *paramsCorrected) { modParam("chn", channels); modParam("rate", sample_rate); } canPause = snd_pcm_hw_params_can_pause(params) && snd_pcm_hw_params_can_resume(params); mustSwapChn = channels == 6 || channels == 8; #ifdef HAVE_CHMAP if (mustSwapChn) { snd_pcm_chmap_query_t **chmaps = snd_pcm_query_chmaps(snd); if (chmaps) { for (int i = 0; chmaps[i]; ++i) { if (chmaps[i]->map.channels >= channels) { for (uint j = 0; j < channels; ++j) { mustSwapChn &= chmaps[i]->map.pos[j] == j + SND_CHMAP_FL; if (!mustSwapChn) break; } break; } } snd_pcm_free_chmaps(chmaps); } } #endif return true; } } } } } err = true; QMPlay2Core.logError("ALSA :: " + tr("Cannot open audio output stream")); } return readyWrite(); }
static void device_test (GtkWidget *w, alsa_driver *d) { guint chmin, chmax, i; gint err; gchar *new_device; d->can8 = FALSE; d->can16 = FALSE; d->canmono = FALSE; d->canstereo = FALSE; d->signedness8 = FALSE; d->signedness16 = FALSE; new_device = gtk_combo_box_get_active_text(GTK_COMBO_BOX(d->alsa_device)); if(g_ascii_strcasecmp(d->device, new_device)) { g_free(d->device); d->device = g_strdup(new_device); gui_hlp_combo_box_prepend_text_or_set_active(GTK_COMBO_BOX(d->alsa_device), d->device, FALSE); } for(i = 0; i < NUM_FORMATS; i++){ d->devcap[i].minfreq = 8000; d->devcap[i].maxfreq = 44100; d->devcap[i].minbufsize = 256; } d->devcap[MONO8].maxbufsize = 65536; d->devcap[STEREO8].maxbufsize = 32768; d->devcap[MONO16].maxbufsize = 32768; d->devcap[STEREO16].maxbufsize = 16384; if(pcm_open_and_load_hwparams(d) < 0) return; if(!snd_pcm_hw_params_test_format(d->soundfd, d->hwparams, SND_PCM_FORMAT_U8)) { d->can8 = TRUE; } if(!snd_pcm_hw_params_test_format(d->soundfd, d->hwparams, SND_PCM_FORMAT_S8)) { d->can8 = TRUE; d->signedness8 = TRUE; } if(!snd_pcm_hw_params_test_format(d->soundfd, d->hwparams, SND_PCM_FORMAT_U16)) { d->can16 = TRUE; } if(!snd_pcm_hw_params_test_format(d->soundfd, d->hwparams, SND_PCM_FORMAT_S16)) { d->can16 = TRUE; d->signedness16 = TRUE; } if((err = snd_pcm_hw_params_get_channels_min(d->hwparams, &chmin)) < 0) { alsa_error(N_("Unable to get minimal channels number"), err); snd_pcm_close(d->soundfd); return; } if((err = snd_pcm_hw_params_get_channels_max(d->hwparams, &chmax)) < 0) { alsa_error(N_("Unable to get maximal channels number"), err); snd_pcm_close(d->soundfd); return; } if(chmin > 2) { error_error("Both mono and stereo are not supported by ALSA device!!!"); snd_pcm_close(d->soundfd); return; } if(chmin == 1) d->canmono = TRUE; if(chmax >= 2) d->canstereo = TRUE; if(d->can8) { if((err = snd_pcm_hw_params_set_format(d->soundfd, d->hwparams, d->signedness8 ? SND_PCM_FORMAT_S8 : SND_PCM_FORMAT_U8)) < 0) { alsa_error(N_("Unable to set audio format"), err); snd_pcm_close(d->soundfd); return; } if(d->canmono) { if(set_rates(d, 1, MONO8) < 0) { snd_pcm_close(d->soundfd); return; } } if(d->canstereo) { snd_pcm_close(d->soundfd); if(pcm_open_and_load_hwparams(d) < 0) return; if(set_rates(d, 2, STEREO8) < 0) { snd_pcm_close(d->soundfd); return; } } } if(d->can16) { snd_pcm_close(d->soundfd); if(pcm_open_and_load_hwparams(d) < 0) return; if((err = snd_pcm_hw_params_set_format(d->soundfd, d->hwparams, d->signedness16 ? SND_PCM_FORMAT_S16 : SND_PCM_FORMAT_U16)) < 0) { alsa_error(N_("Unable to set audio format"), err); snd_pcm_close(d->soundfd); return; } if(d->canmono) { if(set_rates(d, 1, MONO16) < 0) { snd_pcm_close(d->soundfd); return; } } if(d->canstereo) { snd_pcm_close(d->soundfd); if(pcm_open_and_load_hwparams(d) < 0) return; if(set_rates(d, 2, STEREO16) < 0) { snd_pcm_close(d->soundfd); return; } } } snd_pcm_close(d->soundfd); update_controls(d); }
bool AudioInputALSA::PrepHwParams(void) { snd_pcm_hw_params_t* hwparams; snd_pcm_hw_params_alloca(&hwparams); if (AlsaBad(snd_pcm_hw_params_any(pcm_handle, hwparams), "failed to init hw params")) return false; snd_pcm_access_t axs = SND_PCM_ACCESS_RW_INTERLEAVED; //always? if (AlsaBad(snd_pcm_hw_params_set_access(pcm_handle, hwparams, axs), "failed to set interleaved rw io")) return false; snd_pcm_format_t format = (m_audio_sample_bits > 8) ? SND_PCM_FORMAT_S16 : SND_PCM_FORMAT_U8; if (AlsaBad(snd_pcm_hw_params_set_format(pcm_handle, hwparams, format), QString("failed to set sample format %1") .arg(snd_pcm_format_description(format)))) return false; if (VERBOSE_LEVEL_CHECK(VB_AUDIO, LOG_DEBUG)) { uint min_chans, max_chans; if(AlsaBad(snd_pcm_hw_params_get_channels_min(hwparams, &min_chans), QString("unable to get min channel count"))) min_chans = 0; if(AlsaBad(snd_pcm_hw_params_get_channels_max(hwparams, &max_chans), QString("unable to get max channel count"))) max_chans = 0; LOG(VB_AUDIO, LOG_DEBUG, LOC_DEV + QString("min channels %1, max channels %2, myth requests %3") .arg(min_chans).arg(max_chans).arg(m_audio_channels)); } if (AlsaBad(snd_pcm_hw_params_set_channels(pcm_handle, hwparams, m_audio_channels), QString("failed to set channels to %1") .arg(m_audio_channels))) { return false; } if (AlsaBad(snd_pcm_hw_params_set_rate(pcm_handle, hwparams, m_audio_sample_rate, 0), QString("failed to set sample rate %1") .arg(m_audio_sample_rate))) { uint rate_num = 0; uint rate_den = 0; if (!AlsaBad(snd_pcm_hw_params_get_rate_numden(hwparams, &rate_num, &rate_den), "snd_pcm_hw_params_get_rate_numden failed")) if (m_audio_sample_rate != (int)(rate_num / rate_den)) LOG(VB_GENERAL, LOG_ERR, LOC_DEV + QString("device reports sample rate as %1") .arg(rate_num / rate_den)); return false; } uint buffer_time = 64000; // 64 msec uint period_time = buffer_time / 4; if (AlsaBad(snd_pcm_hw_params_set_period_time_near(pcm_handle, hwparams, &period_time, NULL), "failed to set period time")) return false; if (AlsaBad(snd_pcm_hw_params_set_buffer_time_near(pcm_handle, hwparams, &buffer_time, NULL), "failed to set buffer time")) return false; if (AlsaBad(snd_pcm_hw_params_get_period_size(hwparams, &period_size, NULL), "failed to get period size")) return false; if (AlsaBad(snd_pcm_hw_params (pcm_handle, hwparams), "failed to set hwparams")) return false; myth_block_bytes = snd_pcm_frames_to_bytes(pcm_handle, period_size); LOG(VB_AUDIO, LOG_INFO, LOC_DEV + QString("channels %1, sample rate %2, buffer_time %3 msec, period " "size %4").arg(m_audio_channels) .arg(m_audio_sample_rate).arg(buffer_time / 1000.0, -1, 'f', 1) .arg(period_size)); LOG(VB_AUDIO, LOG_DEBUG, LOC_DEV + QString("myth block size %1") .arg(myth_block_bytes)); return true; }
int main(int argc, char *argv[]) { const char *device_name = "hw"; snd_pcm_t *pcm; snd_pcm_hw_params_t *hw_params; unsigned int i; unsigned int min, max; int any_rate; int err; if (argc > 1) device_name = argv[1]; err = snd_pcm_open(&pcm, device_name, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK); if (err < 0) { fprintf(stderr, "cannot open device '%s': %s\n", device_name, snd_strerror(err)); return 1; } snd_pcm_hw_params_alloca(&hw_params); err = snd_pcm_hw_params_any(pcm, hw_params); if (err < 0) { fprintf(stderr, "cannot get hardware parameters: %s\n", snd_strerror(err)); snd_pcm_close(pcm); return 1; } printf("Device: %s (type: %s)\n", device_name, snd_pcm_type_name(snd_pcm_type(pcm))); printf("Access types:"); for (i = 0; i < ARRAY_SIZE(accesses); ++i) { if (!snd_pcm_hw_params_test_access(pcm, hw_params, accesses[i])) printf(" %s", snd_pcm_access_name(accesses[i])); } putchar('\n'); printf("Formats:"); for (i = 0; i < ARRAY_SIZE(formats); ++i) { if (!snd_pcm_hw_params_test_format(pcm, hw_params, formats[i])) printf(" %s", snd_pcm_format_name(formats[i])); } putchar('\n'); err = snd_pcm_hw_params_get_channels_min(hw_params, &min); if (err < 0) { fprintf(stderr, "cannot get minimum channels count: %s\n", snd_strerror(err)); snd_pcm_close(pcm); return 1; } err = snd_pcm_hw_params_get_channels_max(hw_params, &max); if (err < 0) { fprintf(stderr, "cannot get maximum channels count: %s\n", snd_strerror(err)); snd_pcm_close(pcm); return 1; } printf("Channels:"); for (i = min; i <= max; ++i) { if (!snd_pcm_hw_params_test_channels(pcm, hw_params, i)) printf(" %u", i); } putchar('\n'); err = snd_pcm_hw_params_get_rate_min(hw_params, &min, NULL); if (err < 0) { fprintf(stderr, "cannot get minimum rate: %s\n", snd_strerror(err)); snd_pcm_close(pcm); return 1; } err = snd_pcm_hw_params_get_rate_max(hw_params, &max, NULL); if (err < 0) { fprintf(stderr, "cannot get maximum rate: %s\n", snd_strerror(err)); snd_pcm_close(pcm); return 1; } printf("Sample rates:"); if (min == max) printf(" %u", min); else if (!snd_pcm_hw_params_test_rate(pcm, hw_params, min + 1, 0)) printf(" %u-%u", min, max); else { any_rate = 0; for (i = 0; i < ARRAY_SIZE(rates); ++i) { if (!snd_pcm_hw_params_test_rate(pcm, hw_params, rates[i], 0)) { any_rate = 1; printf(" %u", rates[i]); } } if (!any_rate) printf(" %u-%u", min, max); } putchar('\n'); err = snd_pcm_hw_params_get_period_time_min(hw_params, &min, NULL); if (err < 0) { fprintf(stderr, "cannot get minimum period time: %s\n", snd_strerror(err)); snd_pcm_close(pcm); return 1; } err = snd_pcm_hw_params_get_period_time_max(hw_params, &max, NULL); if (err < 0) { fprintf(stderr, "cannot get maximum period time: %s\n", snd_strerror(err)); snd_pcm_close(pcm); return 1; } printf("Interrupt interval: %u-%u us\n", min, max); err = snd_pcm_hw_params_get_buffer_time_min(hw_params, &min, NULL); if (err < 0) { fprintf(stderr, "cannot get minimum buffer time: %s\n", snd_strerror(err)); snd_pcm_close(pcm); return 1; } err = snd_pcm_hw_params_get_buffer_time_max(hw_params, &max, NULL); if (err < 0) { fprintf(stderr, "cannot get maximum buffer time: %s\n", snd_strerror(err)); snd_pcm_close(pcm); return 1; } printf("Buffer size: %u-%u us\n", min, max); snd_pcm_close(pcm); return 0; }
bool AlsaRenderer::SetupHwParams() { snd_pcm_hw_params_t* params; /* allocate a hardware parameters object */ snd_pcm_hw_params_malloc(¶ms); /* choose all parameters */ snd_pcm_hw_params_any(m_PcmHandle, params); /* enable hardware resampling */ snd_pcm_hw_params_set_rate_resample(m_PcmHandle, params, m_Resample); /* set the interleaved read/write format */ snd_pcm_hw_params_set_access(m_PcmHandle, params, SND_PCM_ACCESS_RW_INTERLEAVED); /* signed 16-bit little-endian format */ snd_pcm_hw_params_set_format(m_PcmHandle, params, SND_PCM_FORMAT_S16_LE); /* set the count of channels */ snd_pcm_hw_params_get_channels_max(params, &m_Channels.max); snd_pcm_hw_params_get_channels_min(params, &m_Channels.min); if (m_Channels.val < m_Channels.min || m_Channels.val > m_Channels.max) m_Channels.val = m_Channels.min; snd_pcm_hw_params_set_channels(m_PcmHandle, params, m_Channels.val); /* set the stream rate */ snd_pcm_hw_params_get_rate_max(params, &m_SampleRate.max, &m_Dir); snd_pcm_hw_params_get_rate_min(params, &m_SampleRate.min, &m_Dir); if (m_SampleRate.val < m_SampleRate.min || m_SampleRate.val > m_SampleRate.max) m_SampleRate.val = m_SampleRate.min; printf("sample rate max:%d, min:%d, val:%d\n", m_SampleRate.max, m_SampleRate.min, m_SampleRate.val); snd_pcm_hw_params_set_rate(m_PcmHandle, params, m_SampleRate.val, m_Dir); /* we can set period and buffer by size or time */ /* by default we use "size" (count of frames) */ /* set how many frames in a buffer (a buffer cantains several periods) */ snd_pcm_hw_params_get_buffer_size_max(params, &m_BufferSize.max); snd_pcm_hw_params_get_buffer_size_min(params, &m_BufferSize.min); /* detect period time and buffer time range */ snd_pcm_hw_params_get_buffer_time_max(params, &m_BufferTime.max, &m_Dir); snd_pcm_hw_params_get_buffer_time_min(params, &m_BufferTime.min, &m_Dir); m_BufferTime.val = std::max(m_BufferTime.max / 2, m_BufferTime.min); //m_BufferTime.val = m_BufferTime.min + (m_BufferTime.max - m_BufferTime.min) /2; printf("buffer time max:%d, min:%d, val:%d\n", m_BufferTime.max, m_BufferTime.min, m_BufferTime.val); //(m_BufferTime.max > 120000) ? 120000 : m_BufferTime.max;//120000 snd_pcm_hw_params_set_buffer_time_near(m_PcmHandle, params, &m_BufferTime.val, &m_Dir); snd_pcm_hw_params_get_period_time_max(params, &m_PeriodTime.max, &m_Dir); snd_pcm_hw_params_get_period_time_min(params, &m_PeriodTime.min, &m_Dir); m_PeriodTime.val = m_BufferTime.val / 4; //m_PeriodTime.val = m_PeriodTime.min + (m_PeriodTime.max - m_PeriodTime.min) / 2; printf("period time max:%d, min:%d, val:%d\n", m_PeriodTime.max, m_PeriodTime.min, m_PeriodTime.val); snd_pcm_hw_params_set_period_time_near(m_PcmHandle, params, &m_PeriodTime.val, &m_Dir); int ret = snd_pcm_hw_params(m_PcmHandle, params); if (ret != 0) { snd_pcm_hw_params_free(params); return false; } // get period size again snd_pcm_hw_params_get_period_size(params, &m_PeriodSize.val, &m_Dir); snd_pcm_hw_params_get_buffer_size(params, &m_BufferSize.val); m_BitsPerSample = snd_pcm_format_physical_width(SND_PCM_FORMAT_S16_LE); m_FrameLength = (m_BitsPerSample/8 * m_Channels.val); //mPeriodBufferLength = m_PeriodSize.val * m_FrameLength ; snd_pcm_hw_params_free(params); return true; }
void info(char *dev_name, snd_pcm_stream_t stream) { snd_pcm_hw_params_t *hw_params; int err; snd_pcm_t *handle; unsigned int max; unsigned int min; unsigned int val; unsigned int dir; snd_pcm_uframes_t frames; if ((err = snd_pcm_open (&handle, dev_name, stream, 0)) < 0) { fprintf (stderr, "cannot open audio device %s (%s)\n", dev_name, snd_strerror (err)); return; } if ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0) { fprintf (stderr, "cannot allocate hardware parameter structure (%s)\n", snd_strerror (err)); exit (1); } if ((err = snd_pcm_hw_params_any (handle, hw_params)) < 0) { fprintf (stderr, "cannot initialize hardware parameter structure (%s)\n", snd_strerror (err)); exit (1); } if ((err = snd_pcm_hw_params_get_channels_max(hw_params, &max)) < 0) { fprintf (stderr, "cannot (%s)\n", snd_strerror (err)); exit (1); } printf("max channels %d\n", max); if ((err = snd_pcm_hw_params_get_channels_min(hw_params, &min)) < 0) { fprintf (stderr, "cannot get channel info (%s)\n", snd_strerror (err)); exit (1); } printf("min channels %d\n", min); /* if ((err = snd_pcm_hw_params_get_sbits(hw_params)) < 0) { fprintf (stderr, "cannot get bits info (%s)\n", snd_strerror (err)); exit (1); } printf("bits %d\n", err); */ if ((err = snd_pcm_hw_params_get_rate_min(hw_params, &val, &dir)) < 0) { fprintf (stderr, "cannot get min rate (%s)\n", snd_strerror (err)); exit (1); } printf("min rate %d hz\n", val); if ((err = snd_pcm_hw_params_get_rate_max(hw_params, &val, &dir)) < 0) { fprintf (stderr, "cannot get max rate (%s)\n", snd_strerror (err)); exit (1); } printf("max rate %d hz\n", val); if ((err = snd_pcm_hw_params_get_period_time_min(hw_params, &val, &dir)) < 0) { fprintf (stderr, "cannot get min period time (%s)\n", snd_strerror (err)); exit (1); } printf("min period time %d usecs\n", val); if ((err = snd_pcm_hw_params_get_period_time_max(hw_params, &val, &dir)) < 0) { fprintf (stderr, "cannot get max period time (%s)\n", snd_strerror (err)); exit (1); } printf("max period time %d usecs\n", val); if ((err = snd_pcm_hw_params_get_period_size_min(hw_params, &frames, &dir)) < 0) { fprintf (stderr, "cannot get min period size (%s)\n", snd_strerror (err)); exit (1); } printf("min period size in frames %d\n", frames); if ((err = snd_pcm_hw_params_get_period_size_max(hw_params, &frames, &dir)) < 0) { fprintf (stderr, "cannot get max period size (%s)\n", snd_strerror (err)); exit (1); } printf("max period size in frames %d\n", frames); if ((err = snd_pcm_hw_params_get_periods_min(hw_params, &val, &dir)) < 0) { fprintf (stderr, "cannot get min periods (%s)\n", snd_strerror (err)); exit (1); } printf("min periods per buffer %d\n", val); if ((err = snd_pcm_hw_params_get_periods_max(hw_params, &val, &dir)) < 0) { fprintf (stderr, "cannot get min periods (%s)\n", snd_strerror (err)); exit (1); } printf("max periods per buffer %d\n", val); if ((err = snd_pcm_hw_params_get_buffer_time_min(hw_params, &val, &dir)) < 0) { fprintf (stderr, "cannot get min buffer time (%s)\n", snd_strerror (err)); exit (1); } printf("min buffer time %d usecs\n", val); if ((err = snd_pcm_hw_params_get_buffer_time_max(hw_params, &val, &dir)) < 0) { fprintf (stderr, "cannot get max buffer time (%s)\n", snd_strerror (err)); exit (1); } printf("max buffer time %d usecs\n", val); if ((err = snd_pcm_hw_params_get_buffer_size_min(hw_params, &frames)) < 0) { fprintf (stderr, "cannot get min buffer size (%s)\n", snd_strerror (err)); exit (1); } printf("min buffer size in frames %d\n", frames); if ((err = snd_pcm_hw_params_get_buffer_size_max(hw_params, &frames)) < 0) { fprintf (stderr, "cannot get max buffer size (%s)\n", snd_strerror (err)); exit (1); } printf("max buffer size in frames %d\n", frames); }
/************************************************************************** * ALSA_TraceParameters [internal] * * used to trace format changes, hw and sw parameters */ void ALSA_TraceParameters(snd_pcm_hw_params_t * hw_params, snd_pcm_sw_params_t * sw, int full) { int err; snd_pcm_format_t format; snd_pcm_access_t access; #define X(x) ((x)? "true" : "false") if (full) TRACE("FLAGS: sampleres=%s overrng=%s pause=%s resume=%s syncstart=%s batch=%s block=%s double=%s " "halfd=%s joint=%s\n", X(snd_pcm_hw_params_can_mmap_sample_resolution(hw_params)), X(snd_pcm_hw_params_can_overrange(hw_params)), X(snd_pcm_hw_params_can_pause(hw_params)), X(snd_pcm_hw_params_can_resume(hw_params)), X(snd_pcm_hw_params_can_sync_start(hw_params)), X(snd_pcm_hw_params_is_batch(hw_params)), X(snd_pcm_hw_params_is_block_transfer(hw_params)), X(snd_pcm_hw_params_is_double(hw_params)), X(snd_pcm_hw_params_is_half_duplex(hw_params)), X(snd_pcm_hw_params_is_joint_duplex(hw_params))); #undef X err = snd_pcm_hw_params_get_access(hw_params, &access); if (err >= 0) { TRACE("access=%s\n", snd_pcm_access_name(access)); } else { snd_pcm_access_mask_t * acmask; acmask = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_access_mask_sizeof()); snd_pcm_hw_params_get_access_mask(hw_params, acmask); for ( access = SND_PCM_ACCESS_MMAP_INTERLEAVED; access <= SND_PCM_ACCESS_LAST; access++) if (snd_pcm_access_mask_test(acmask, access)) TRACE("access=%s\n", snd_pcm_access_name(access)); HeapFree( GetProcessHeap(), 0, acmask ); } err = snd_pcm_hw_params_get_format(hw_params, &format); if (err >= 0) { TRACE("format=%s\n", snd_pcm_format_name(format)); } else { snd_pcm_format_mask_t * fmask; fmask = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_format_mask_sizeof()); snd_pcm_hw_params_get_format_mask(hw_params, fmask); for ( format = SND_PCM_FORMAT_S8; format <= SND_PCM_FORMAT_LAST ; format++) if ( snd_pcm_format_mask_test(fmask, format) ) TRACE("format=%s\n", snd_pcm_format_name(format)); HeapFree( GetProcessHeap(), 0, fmask ); } do { int err=0; unsigned int val=0; err = snd_pcm_hw_params_get_channels(hw_params, &val); if (err<0) { unsigned int min = 0; unsigned int max = 0; err = snd_pcm_hw_params_get_channels_min(hw_params, &min), err = snd_pcm_hw_params_get_channels_max(hw_params, &max); TRACE("channels_min=%u, channels_min_max=%u\n", min, max); } else { TRACE("channels=%d\n", val); } } while(0); do { int err=0; snd_pcm_uframes_t val=0; err = snd_pcm_hw_params_get_buffer_size(hw_params, &val); if (err<0) { snd_pcm_uframes_t min = 0; snd_pcm_uframes_t max = 0; err = snd_pcm_hw_params_get_buffer_size_min(hw_params, &min), err = snd_pcm_hw_params_get_buffer_size_max(hw_params, &max); TRACE("buffer_size_min=%lu, buffer_size_min_max=%lu\n", min, max); } else { TRACE("buffer_size=%lu\n", val); } } while(0); #define X(x) do { \ int err=0; \ int dir=0; \ unsigned int val=0; \ err = snd_pcm_hw_params_get_##x(hw_params,&val, &dir); \ if (err<0) { \ unsigned int min = 0; \ unsigned int max = 0; \ err = snd_pcm_hw_params_get_##x##_min(hw_params, &min, &dir); \ err = snd_pcm_hw_params_get_##x##_max(hw_params, &max, &dir); \ TRACE(#x "_min=%u " #x "_max=%u\n", min, max); \ } else \ TRACE(#x "=%d\n", val); \ } while(0) X(rate); X(buffer_time); X(periods); do { int err=0; int dir=0; snd_pcm_uframes_t val=0; err = snd_pcm_hw_params_get_period_size(hw_params, &val, &dir); if (err<0) { snd_pcm_uframes_t min = 0; snd_pcm_uframes_t max = 0; err = snd_pcm_hw_params_get_period_size_min(hw_params, &min, &dir), err = snd_pcm_hw_params_get_period_size_max(hw_params, &max, &dir); TRACE("period_size_min=%lu, period_size_min_max=%lu\n", min, max); } else { TRACE("period_size=%lu\n", val); } } while(0); X(period_time); #undef X if (!sw) return; }
static GstCaps * gst_alsa_detect_channels (GstObject * obj, snd_pcm_hw_params_t * hw_params, GstCaps * in_caps) { GstCaps *caps; guint min, max; gint min_chans, max_chans; gint err, i; GST_LOG_OBJECT (obj, "probing channels ..."); if ((err = snd_pcm_hw_params_get_channels_min (hw_params, &min)) < 0) goto min_chan_error; if ((err = snd_pcm_hw_params_get_channels_max (hw_params, &max)) < 0) goto max_chan_error; /* note: the above functions may return (guint) -1 */ min_chans = min; max_chans = max; if (min_chans < 0) { min_chans = 1; max_chans = GST_ALSA_MAX_CHANNELS; } else if (max_chans < 0) { max_chans = GST_ALSA_MAX_CHANNELS; } if (min_chans > max_chans) { gint temp; GST_WARNING_OBJECT (obj, "minimum channels > maximum channels (%d > %d), " "please fix your soundcard drivers", min, max); temp = min_chans; min_chans = max_chans; max_chans = temp; } /* pro cards seem to return large numbers for min_channels */ if (min_chans > GST_ALSA_MAX_CHANNELS) { GST_DEBUG_OBJECT (obj, "min_chans = %u, looks like a pro card", min_chans); if (max_chans < min_chans) { max_chans = min_chans; } else { /* only support [max_chans; max_chans] for these cards for now * to avoid inflating the source caps with loads of structures ... */ min_chans = max_chans; } } else { min_chans = MAX (min_chans, 1); max_chans = MIN (GST_ALSA_MAX_CHANNELS, max_chans); } GST_DEBUG_OBJECT (obj, "Min. channels = %d (%d)", min_chans, min); GST_DEBUG_OBJECT (obj, "Max. channels = %d (%d)", max_chans, max); caps = gst_caps_new_empty (); for (i = 0; i < gst_caps_get_size (in_caps); ++i) { GstStructure *s; GType field_type; gint c_min = min_chans; gint c_max = max_chans; s = gst_caps_get_structure (in_caps, i); /* the template caps might limit the number of channels (like alsasrc), * in which case we don't want to return a superset, so hack around this * for the two common cases where the channels are either a fixed number * or a min/max range). Example: alsasrc template has channels = [1,2] and * the detection will claim to support 8 channels for device 'plughw:0' */ field_type = gst_structure_get_field_type (s, "channels"); if (field_type == G_TYPE_INT) { gst_structure_get_int (s, "channels", &c_min); gst_structure_get_int (s, "channels", &c_max); } else if (field_type == GST_TYPE_INT_RANGE) { const GValue *val; val = gst_structure_get_value (s, "channels"); c_min = CLAMP (gst_value_get_int_range_min (val), min_chans, max_chans); c_max = CLAMP (gst_value_get_int_range_max (val), min_chans, max_chans); } else { c_min = min_chans; c_max = max_chans; } caps_add_channel_configuration (caps, s, c_min, c_max); } gst_caps_unref (in_caps); return caps; /* ERRORS */ min_chan_error: { GST_ERROR_OBJECT (obj, "failed to query minimum channel count: %s", snd_strerror (err)); return NULL; } max_chan_error: { GST_ERROR_OBJECT (obj, "failed to query maximum channel count: %s", snd_strerror (err)); return NULL; } }
/*---------------------------------------------------------------------------- ** ALSA_ComputeCaps ** ** Given an ALSA PCM, figure out our HW CAPS structure info. ** ctl can be null, pcm is required, as is all output parms. ** */ static int ALSA_ComputeCaps(snd_ctl_t *ctl, snd_pcm_t *pcm, WORD *channels, DWORD *flags, DWORD *formats, DWORD *supports) { snd_pcm_hw_params_t *hw_params; snd_pcm_format_mask_t *fmask; snd_pcm_access_mask_t *acmask; unsigned int ratemin = 0; unsigned int ratemax = 0; unsigned int chmin = 0; unsigned int chmax = 0; int rc, dir = 0; hw_params = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_hw_params_sizeof() ); fmask = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_format_mask_sizeof() ); acmask = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_access_mask_sizeof() ); if ((rc = snd_pcm_hw_params_any(pcm, hw_params)) < 0) goto done; snd_pcm_hw_params_get_format_mask(hw_params, fmask); if ((rc = snd_pcm_hw_params_get_access_mask(hw_params, acmask)) < 0) goto done; if ((rc = snd_pcm_hw_params_get_rate_min(hw_params, &ratemin, &dir)) < 0) goto done; if ((rc = snd_pcm_hw_params_get_rate_max(hw_params, &ratemax, &dir)) < 0) goto done; if ((rc = snd_pcm_hw_params_get_channels_min(hw_params, &chmin)) < 0) goto done; if ((rc = snd_pcm_hw_params_get_channels_max(hw_params, &chmax)) < 0) goto done; #define X(r,v) \ if ( (r) >= ratemin && ( (r) <= ratemax || ratemax == -1) ) \ { \ if (snd_pcm_format_mask_test( fmask, SND_PCM_FORMAT_U8)) \ { \ if (chmin <= 1 && 1 <= chmax) \ *formats |= WAVE_FORMAT_##v##M08; \ if (chmin <= 2 && 2 <= chmax) \ *formats |= WAVE_FORMAT_##v##S08; \ } \ if (snd_pcm_format_mask_test( fmask, SND_PCM_FORMAT_S16_LE)) \ { \ if (chmin <= 1 && 1 <= chmax) \ *formats |= WAVE_FORMAT_##v##M16; \ if (chmin <= 2 && 2 <= chmax) \ *formats |= WAVE_FORMAT_##v##S16; \ } \ } X(11025,1); X(22050,2); X(44100,4); X(48000,48); X(96000,96); #undef X if (chmin > 1) FIXME("Device has a minimum of %d channels\n", chmin); *channels = chmax; /* FIXME: is sample accurate always true ? ** Can we do WAVECAPS_PITCH, WAVECAPS_SYNC, or WAVECAPS_PLAYBACKRATE? */ *supports |= WAVECAPS_SAMPLEACCURATE; *supports |= WAVECAPS_DIRECTSOUND; /* check for volume control support */ if (ctl) { if (snd_ctl_name(ctl)) { snd_hctl_t *hctl; if (snd_hctl_open(&hctl, snd_ctl_name(ctl), 0) >= 0) { snd_hctl_load(hctl); if (!ALSA_CheckSetVolume( hctl, NULL, NULL, NULL, NULL, NULL, NULL, NULL )) { *supports |= WAVECAPS_VOLUME; if (chmin <= 2 && 2 <= chmax) *supports |= WAVECAPS_LRVOLUME; } snd_hctl_free(hctl); snd_hctl_close(hctl); } } } *flags = DSCAPS_CERTIFIED | DSCAPS_CONTINUOUSRATE; *flags |= DSCAPS_SECONDARYMONO | DSCAPS_SECONDARYSTEREO; *flags |= DSCAPS_SECONDARY8BIT | DSCAPS_SECONDARY16BIT; if (*formats & (WAVE_FORMAT_1M08 | WAVE_FORMAT_2M08 | WAVE_FORMAT_4M08 | WAVE_FORMAT_48M08 | WAVE_FORMAT_96M08 | WAVE_FORMAT_1M16 | WAVE_FORMAT_2M16 | WAVE_FORMAT_4M16 | WAVE_FORMAT_48M16 | WAVE_FORMAT_96M16) ) *flags |= DSCAPS_PRIMARYMONO; if (*formats & (WAVE_FORMAT_1S08 | WAVE_FORMAT_2S08 | WAVE_FORMAT_4S08 | WAVE_FORMAT_48S08 | WAVE_FORMAT_96S08 | WAVE_FORMAT_1S16 | WAVE_FORMAT_2S16 | WAVE_FORMAT_4S16 | WAVE_FORMAT_48S16 | WAVE_FORMAT_96S16) ) *flags |= DSCAPS_PRIMARYSTEREO; if (*formats & (WAVE_FORMAT_1M08 | WAVE_FORMAT_2M08 | WAVE_FORMAT_4M08 | WAVE_FORMAT_48M08 | WAVE_FORMAT_96M08 | WAVE_FORMAT_1S08 | WAVE_FORMAT_2S08 | WAVE_FORMAT_4S08 | WAVE_FORMAT_48S08 | WAVE_FORMAT_96S08) ) *flags |= DSCAPS_PRIMARY8BIT; if (*formats & (WAVE_FORMAT_1M16 | WAVE_FORMAT_2M16 | WAVE_FORMAT_4M16 | WAVE_FORMAT_48M16 | WAVE_FORMAT_96M16 | WAVE_FORMAT_1S16 | WAVE_FORMAT_2S16 | WAVE_FORMAT_4S16 | WAVE_FORMAT_48S16 | WAVE_FORMAT_96S16) ) *flags |= DSCAPS_PRIMARY16BIT; rc = 0; done: if (rc < 0) ERR("failed: %s(%d)\n", snd_strerror(rc), rc); HeapFree( GetProcessHeap(), 0, hw_params ); HeapFree( GetProcessHeap(), 0, fmask ); HeapFree( GetProcessHeap(), 0, acmask ); return rc; }
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; }
/* Fill caps with the device capabilities. Return 0 on error. */ static int fill_capabilities (struct output_driver_caps *caps) { snd_pcm_hw_params_t *hw_params; snd_pcm_format_mask_t *format_mask; int err; unsigned val; if ((err = snd_pcm_open(&handle, options_get_str("AlsaDevice"), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) < 0) { error ("Can't open audio: %s", snd_strerror(err)); return 0; } if ((err = snd_pcm_hw_params_malloc(&hw_params)) < 0) { error ("Can't allocate alsa hardware parameters structure: %s", snd_strerror(err)); snd_pcm_close (handle); return 0; } if ((err = snd_pcm_hw_params_any (handle, hw_params)) < 0) { error ("Can't initialize hardware parameters structure: %s", snd_strerror(err)); snd_pcm_hw_params_free (hw_params); snd_pcm_close (handle); return 0; } if ((err = snd_pcm_hw_params_get_channels_min (hw_params, &val)) < 0) { error ("Can't get the minimum number of channels: %s", snd_strerror(err)); snd_pcm_hw_params_free (hw_params); snd_pcm_close (handle); return 0; } caps->min_channels = val; if ((err = snd_pcm_hw_params_get_channels_max (hw_params, &val)) < 0) { error ("Can't get the maximum number of channels: %s", snd_strerror(err)); snd_pcm_hw_params_free (hw_params); snd_pcm_close (handle); return 0; } caps->max_channels = val; if ((err = snd_pcm_format_mask_malloc(&format_mask)) < 0) { error ("Can't allocate format mask: %s", snd_strerror(err)); snd_pcm_hw_params_free (hw_params); snd_pcm_close (handle); return 0; } snd_pcm_hw_params_get_format_mask (hw_params, format_mask); caps->formats = SFMT_NE; if (snd_pcm_format_mask_test(format_mask, SND_PCM_FORMAT_S8)) caps->formats |= SFMT_S8; if (snd_pcm_format_mask_test(format_mask, SND_PCM_FORMAT_U8)) caps->formats |= SFMT_U8; if (snd_pcm_format_mask_test(format_mask, SND_PCM_FORMAT_S16)) caps->formats |= SFMT_S16; if (snd_pcm_format_mask_test(format_mask, SND_PCM_FORMAT_U16)) caps->formats |= SFMT_U16; #if 0 if (snd_pcm_format_mask_test(format_mask, SND_PCM_FORMAT_S24)) caps->formats |= SFMT_S32; /* conversion needed */ #endif if (snd_pcm_format_mask_test(format_mask, SND_PCM_FORMAT_S32)) caps->formats |= SFMT_S32; if (snd_pcm_format_mask_test(format_mask, SND_PCM_FORMAT_U32)) caps->formats |= SFMT_U32; snd_pcm_format_mask_free (format_mask); snd_pcm_hw_params_free (hw_params); snd_pcm_close (handle); handle = NULL; return 1; }
bool AlsaBackend::ProbeDevice(DeviceInfo* device, StreamSpec& spec) { char *name; if (devicesList.size() <= 1) UpdateDevicesList(); size_t i = 1; // find device in device pool by given ID for (; i < devicesList.size(); ++i) { if ((device->guid[0] == 0 && device->id == devicesList.at(i).id) || strncmp(devicesList.at(i).guid, device->guid, DEVICE_NAME_MAXLEN) == 0) { Log("found device"); name = device->displayName; break; } } if (i >= devicesList.size()) { Log("ProbeDevice failed!"); return false; } // If device is BLUETOOTH type, don't waste time and start with 8KHz immediately. A2DP is not supported. if (device->type == DeviceInfo::TYPE_BLUETOOTH) { spec.rate = 8000; spec.channels = 1; } int result; char hwctl[8]; snd_ctl_t *chandle = 0; int openMode = SND_PCM_ASYNC; snd_pcm_stream_t stream; snd_pcm_info_t *pcminfo; snd_pcm_info_alloca(&pcminfo); snd_pcm_t *phandle = 0; snd_pcm_hw_params_t *params; snd_pcm_hw_params_alloca(¶ms); // Probe mixer for device if (!device->mixer) device->mixer = new AlsaVolumeControl; static_cast<AlsaVolumeControl*>(device->mixer)->ProbeMixer(device); // First try for playback stream = SND_PCM_STREAM_PLAYBACK; bool skipPlayback = false; if (device->type == DeviceInfo::TYPE_HW) { snprintf(hwctl, 8, "hw:%d", device->id); Log("Trying to open ctl %s", hwctl); result = snd_ctl_open(&chandle, hwctl, SND_CTL_NONBLOCK); //@todo snd_ctl_card_info() call? if (result >= 0) { snd_pcm_info_set_device(pcminfo, device->id); snd_pcm_info_set_subdevice(pcminfo, 0); snd_pcm_info_set_stream(pcminfo, stream); result = snd_ctl_pcm_info(chandle, pcminfo); if (result < 0) { Log("Device probably doesn't support playback (dev %d): %s", device->id, snd_strerror(result)); skipPlayback = true; } } else { Log("Device probably doesn't have ctl interface."); chandle = 0; } } if (!skipPlayback) { result = snd_pcm_open(&phandle, device->guid, stream, openMode | SND_PCM_NONBLOCK); if (result >= 0) { // The device is open ... fill the parameter structure. result = snd_pcm_hw_params_any(phandle, params); if (result >= 0) { // Get output channel information. unsigned int value; result = snd_pcm_hw_params_get_channels_max(params, &value); if (result >= 0) { device->outMaxChannels = value; snd_pcm_close(phandle); } else { snd_pcm_close(phandle); // @todo unify handle closing calls in case of errors Log("error getting device %s output channels: %s", name, snd_strerror(result)); } } else { snd_pcm_close(phandle); // @todo unify handle closing calls in case of errors (_EE_CALL) Log("snd_pcm_hw_params error for device %s: %s", name, snd_strerror(result)); } } else { Log("snd_pcm_open error for playback on device %s: %s", name, snd_strerror(result)); } } // Now try for capture stream = SND_PCM_STREAM_CAPTURE; snd_pcm_info_set_stream(pcminfo, stream); if (device->type == DeviceInfo::TYPE_HW && chandle) { result = snd_ctl_pcm_info(chandle, pcminfo); snd_ctl_close(chandle); if (result < 0) { Log("Device probably doesn't support capture: %s", snd_strerror(result)); if (device->outMaxChannels == 0) // cannot playback/capture at all { Log("ProbeDevice failed!"); return false; } return ProbeParameters(device, spec, phandle, stream, pcminfo, name, params); } } result = snd_pcm_open(&phandle, device->guid, stream, openMode | SND_PCM_NONBLOCK); if (result < 0) { Log("snd_pcm_open error for capture on device %s: %s", name, snd_strerror(result)); if (device->outMaxChannels == 0) { Log("ProbeDevice failed!"); return false; } return ProbeParameters(device, spec, phandle, stream, pcminfo, name, params); } // The device is open ... fill the parameter structure. result = snd_pcm_hw_params_any(phandle, params); if (result < 0) { snd_pcm_close(phandle); Log("snd_pcm_hw_params error for device %s: %s", name, snd_strerror(result)); if (device->outMaxChannels == 0) { Log("ProbeDevice failed!"); return false; } return ProbeParameters(device, spec, phandle, stream, pcminfo, name, params); } unsigned int value; result = snd_pcm_hw_params_get_channels_max(params, &value); if (result < 0) { snd_pcm_close(phandle); Log("error getting device %s input channels: %s", name, snd_strerror(result)); if (device->outMaxChannels == 0) { Log("ProbeDevice failed!"); return false; } return ProbeParameters(device, spec, phandle, stream, pcminfo, name, params); } device->inMaxChannels = value; snd_pcm_close(phandle); return ProbeParameters(device, spec, phandle, stream, pcminfo, name, params); }