static void get_format_range(Instance *pi, Range *range) { //int i; //int rc; ALSAio_private *priv = (ALSAio_private *)pi; if (!priv->c.handle) { fprintf(stderr, "*** device is not open!\n"); return; } Range_clear(range); range->type = RANGE_STRINGS; #if 0 for (i=0; i < table_size(formats); i++) { rc = snd_pcm_hw_params_test_format(priv->c.handle, priv->c.hwparams, formats[i].value); if (rc == 0) { fprintf(stderr, " %s sampling available\n", formats[i].label); IndexedSet_add(range->strings, String_new(formats[i].label)); } } #else fprintf(stderr, "*** %s is disabled or now\n", __func__); #endif }
int audio_get_formats(struct audio_info_struct *ai) { snd_pcm_hw_params_t *hw; unsigned int rate; int supported_formats, i; snd_pcm_hw_params_alloca(&hw); if (snd_pcm_hw_params_any(ai->handle, hw) < 0) { fprintf(stderr, "audio_get_formats(): no configuration available\n"); return -1; } if (snd_pcm_hw_params_set_access(ai->handle, hw, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) return -1; if (snd_pcm_hw_params_set_channels(ai->handle, hw, ai->channels) < 0) return 0; rate = ai->rate; if (snd_pcm_hw_params_set_rate_near(ai->handle, hw, &rate, NULL) < 0) return -1; if (!rates_match(ai->rate, rate)) return 0; supported_formats = 0; for (i = 0; i < NUM_FORMATS; ++i) { if (snd_pcm_hw_params_test_format(ai->handle, hw, format_map[i].alsa) == 0) supported_formats |= format_map[i].mpg123; } return supported_formats; }
static int get_formats_alsa(audio_output_t *ao) { snd_pcm_t *pcm=(snd_pcm_t*)ao->userptr; snd_pcm_hw_params_t *hw; unsigned int rate; int supported_formats, i; snd_pcm_hw_params_alloca(&hw); if (snd_pcm_hw_params_any(pcm, hw) < 0) { if(!0) printf("get_formats_alsa(): no configuration available"); return -1; } if (snd_pcm_hw_params_set_access(pcm, hw, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) return -1; if (snd_pcm_hw_params_set_channels(pcm, hw, ao->channels) < 0) return 0; rate = ao->rate; if (snd_pcm_hw_params_set_rate_near(pcm, hw, &rate, NULL) < 0) return -1; if (!rates_match(ao->rate, rate)) return 0; supported_formats = 0; for (i = 0; i < NUM_FORMATS; ++i) { if (snd_pcm_hw_params_test_format(pcm, hw, format_map[i].alsa) == 0) supported_formats |= format_map[i].mpg123; } return supported_formats; }
/** * Checks if sound card supports the chosen parameters. * * @returns true if hardware supports it * @throws AudioOutputException - if device cannot be accessed */ bool AudioOutputDeviceAlsa::HardwareParametersSupported(String card, uint channels, int samplerate, uint numfragments, uint fragmentsize) throw (AudioOutputException) { pcm_name = "hw:" + card; int err; if ((err = snd_pcm_open(&pcm_handle, pcm_name.c_str(), stream, SND_PCM_NONBLOCK)) < 0) { throw AudioOutputException(String("Error opening PCM device ") + pcm_name + ": " + snd_strerror(err)); } snd_pcm_hw_params_alloca(&hwparams); if (snd_pcm_hw_params_any(pcm_handle, hwparams) < 0) { snd_pcm_close(pcm_handle); return false; } if (snd_pcm_hw_params_test_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) { snd_pcm_close(pcm_handle); return false; } #if WORDS_BIGENDIAN if (snd_pcm_hw_params_test_format(pcm_handle, hwparams, SND_PCM_FORMAT_S16_BE) < 0) #else // little endian if (snd_pcm_hw_params_test_format(pcm_handle, hwparams, SND_PCM_FORMAT_S16_LE) < 0) #endif { snd_pcm_close(pcm_handle); return false; } int dir = 0; if (snd_pcm_hw_params_test_rate(pcm_handle, hwparams, samplerate, dir) < 0) { snd_pcm_close(pcm_handle); return false; } if (snd_pcm_hw_params_test_channels(pcm_handle, hwparams, channels) < 0) { snd_pcm_close(pcm_handle); return false; } if (snd_pcm_hw_params_test_periods(pcm_handle, hwparams, numfragments, dir) < 0) { snd_pcm_close(pcm_handle); return false; } if (snd_pcm_hw_params_test_buffer_size(pcm_handle, hwparams, (fragmentsize * numfragments)) < 0) { snd_pcm_close(pcm_handle); return false; } snd_pcm_close(pcm_handle); return true; }
static bool find_float_format(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) { if (snd_pcm_hw_params_test_format(pcm, params, SND_PCM_FORMAT_FLOAT) == 0) { RARCH_LOG("ALSA: Using floating point format.\n"); return true; } RARCH_LOG("ALSA: Using signed 16-bit format.\n"); return false; }
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); }
static int alsaio_setup(t_alsa_dev *dev, int out, int *channels, int *rate, int nfrags, int frag_size) { int bufsizeforthis, err; snd_pcm_hw_params_t* hw_params; unsigned int tmp_uint; snd_pcm_uframes_t tmp_snd_pcm_uframes; if (sys_verbose) { if (out) post("configuring sound output..."); else post("configuring sound input..."); } /* set hardware parameters... */ snd_pcm_hw_params_alloca(&hw_params); /* get the default params */ err = snd_pcm_hw_params_any(dev->a_handle, hw_params); check_error(err, "snd_pcm_hw_params_any"); /* try to set interleaved access */ err = snd_pcm_hw_params_set_access(dev->a_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED); if (err < 0) return (-1); check_error(err, "snd_pcm_hw_params_set_access"); #if 0 /* enable this to print out which formats are available */ { int i; for (i = 0; i <= SND_PCM_FORMAT_LAST; i++) fprintf(stderr, "%d -> %d\n", i, snd_pcm_hw_params_test_format(dev->a_handle, hw_params, i)); } #endif /* Try to set 32 bit format first */ err = snd_pcm_hw_params_set_format(dev->a_handle, hw_params, SND_PCM_FORMAT_S32); if (err < 0) { /* fprintf(stderr, "PD-ALSA: 32 bit format not available - trying 24\n"); */ err = snd_pcm_hw_params_set_format(dev->a_handle, hw_params, SND_PCM_FORMAT_S24_3LE); if (err < 0) { /* fprintf(stderr, "PD-ALSA: 32/24 bit format not available - using 16\n"); */ err = snd_pcm_hw_params_set_format(dev->a_handle, hw_params, SND_PCM_FORMAT_S16); check_error(err, "snd_pcm_hw_params_set_format"); dev->a_sampwidth = 2; } else dev->a_sampwidth = 3; } else dev->a_sampwidth = 4; if (sys_verbose) post("Sample width set to %d bytes", dev->a_sampwidth); /* set the subformat */ err = snd_pcm_hw_params_set_subformat(dev->a_handle, hw_params, SND_PCM_SUBFORMAT_STD); check_error(err, "snd_pcm_hw_params_set_subformat"); /* set the number of channels */ tmp_uint = *channels; err = snd_pcm_hw_params_set_channels_min(dev->a_handle, hw_params, &tmp_uint); check_error(err, "snd_pcm_hw_params_set_channels"); if (tmp_uint != (unsigned)*channels) post("ALSA: set %s channels to %d", (out?"output":"input"), tmp_uint); *channels = tmp_uint; dev->a_channels = *channels; /* set the sampling rate */ err = snd_pcm_hw_params_set_rate_min(dev->a_handle, hw_params, (unsigned int *)rate, 0); check_error(err, "snd_pcm_hw_params_set_rate_min (input)"); #if 0 err = snd_pcm_hw_params_get_rate(hw_params, &subunitdir); post("input sample rate %d", err); #endif /* post("frag size %d, nfrags %d", frag_size, nfrags); */ /* set "period size" */ #ifdef ALSAAPI9 err = snd_pcm_hw_params_set_period_size_near(dev->a_handle, hw_params, (snd_pcm_uframes_t)frag_size, 0); #else tmp_snd_pcm_uframes = frag_size; err = snd_pcm_hw_params_set_period_size_near(dev->a_handle, hw_params, &tmp_snd_pcm_uframes, 0); #endif check_error(err, "snd_pcm_hw_params_set_period_size_near (input)"); /* set the buffer size */ #ifdef ALSAAPI9 err = snd_pcm_hw_params_set_buffer_size_near(dev->a_handle, hw_params, nfrags * frag_size); #else tmp_snd_pcm_uframes = nfrags * frag_size; err = snd_pcm_hw_params_set_buffer_size_near(dev->a_handle, hw_params, &tmp_snd_pcm_uframes); #endif check_error(err, "snd_pcm_hw_params_set_buffer_size_near (input)"); err = snd_pcm_hw_params(dev->a_handle, hw_params); check_error(err, "snd_pcm_hw_params (input)"); /* set up the buffer */ bufsizeforthis = DEFDACBLKSIZE * dev->a_sampwidth * *channels; if (alsa_snd_buf) { if (alsa_snd_bufsize < bufsizeforthis) { if (!(alsa_snd_buf = realloc(alsa_snd_buf, bufsizeforthis))) { post("out of memory"); return (0); } memset(alsa_snd_buf, 0, bufsizeforthis); alsa_snd_bufsize = bufsizeforthis; } } else { if (!(alsa_snd_buf = (void *)malloc(bufsizeforthis))) { post("out of memory"); return (0); } memset(alsa_snd_buf, 0, bufsizeforthis); alsa_snd_bufsize = bufsizeforthis; } return (1); }
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 ALCboolean alsa_reset_playback(ALCdevice *device) { alsa_data *data = (alsa_data*)device->ExtraData; snd_pcm_uframes_t periodSizeInFrames; unsigned int periodLen, bufferLen; snd_pcm_sw_params_t *sp = NULL; snd_pcm_hw_params_t *hp = NULL; snd_pcm_access_t access; snd_pcm_format_t format; unsigned int periods; unsigned int rate; const char *funcerr; int allowmmap; int err; format = -1; switch(device->FmtType) { case DevFmtByte: format = SND_PCM_FORMAT_S8; break; case DevFmtUByte: format = SND_PCM_FORMAT_U8; break; case DevFmtShort: format = SND_PCM_FORMAT_S16; break; case DevFmtUShort: format = SND_PCM_FORMAT_U16; break; case DevFmtInt: format = SND_PCM_FORMAT_S32; break; case DevFmtUInt: format = SND_PCM_FORMAT_U32; break; case DevFmtFloat: format = SND_PCM_FORMAT_FLOAT; break; } allowmmap = GetConfigValueBool("alsa", "mmap", 1); periods = device->NumUpdates; periodLen = (ALuint64)device->UpdateSize * 1000000 / device->Frequency; bufferLen = periodLen * periods; rate = device->Frequency; snd_pcm_hw_params_malloc(&hp); #define CHECK(x) if((funcerr=#x),(err=(x)) < 0) goto error CHECK(snd_pcm_hw_params_any(data->pcmHandle, hp)); /* set interleaved access */ if(!allowmmap || snd_pcm_hw_params_set_access(data->pcmHandle, hp, SND_PCM_ACCESS_MMAP_INTERLEAVED) < 0) { if(periods > 2) { periods--; bufferLen = periodLen * periods; } CHECK(snd_pcm_hw_params_set_access(data->pcmHandle, hp, SND_PCM_ACCESS_RW_INTERLEAVED)); } /* test and set format (implicitly sets sample bits) */ if(snd_pcm_hw_params_test_format(data->pcmHandle, hp, format) < 0) { static const struct { snd_pcm_format_t format; enum DevFmtType fmttype; } formatlist[] = { { SND_PCM_FORMAT_FLOAT, DevFmtFloat }, { SND_PCM_FORMAT_S32, DevFmtInt }, { SND_PCM_FORMAT_U32, DevFmtUInt }, { SND_PCM_FORMAT_S16, DevFmtShort }, { SND_PCM_FORMAT_U16, DevFmtUShort }, { SND_PCM_FORMAT_S8, DevFmtByte }, { SND_PCM_FORMAT_U8, DevFmtUByte }, }; size_t k; for(k = 0;k < COUNTOF(formatlist);k++) { format = formatlist[k].format; if(snd_pcm_hw_params_test_format(data->pcmHandle, hp, format) >= 0) { device->FmtType = formatlist[k].fmttype; break; } } } CHECK(snd_pcm_hw_params_set_format(data->pcmHandle, hp, format)); /* test and set channels (implicitly sets frame bits) */ if(snd_pcm_hw_params_test_channels(data->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans)) < 0) { static const enum DevFmtChannels channellist[] = { DevFmtStereo, DevFmtQuad, DevFmtX51, DevFmtX71, DevFmtMono, }; size_t k; for(k = 0;k < COUNTOF(channellist);k++) { if(snd_pcm_hw_params_test_channels(data->pcmHandle, hp, ChannelsFromDevFmt(channellist[k])) >= 0) { device->FmtChans = channellist[k]; break; } } } CHECK(snd_pcm_hw_params_set_channels(data->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans))); /* set rate (implicitly constrains period/buffer parameters) */ if(snd_pcm_hw_params_set_rate_resample(data->pcmHandle, hp, 0) < 0) ERR("Failed to disable ALSA resampler\n"); CHECK(snd_pcm_hw_params_set_rate_near(data->pcmHandle, hp, &rate, NULL)); /* set buffer time (implicitly constrains period/buffer parameters) */ CHECK(snd_pcm_hw_params_set_buffer_time_near(data->pcmHandle, hp, &bufferLen, NULL)); /* set period time (implicitly sets buffer size/bytes/time and period size/bytes) */ CHECK(snd_pcm_hw_params_set_period_time_near(data->pcmHandle, hp, &periodLen, NULL)); /* install and prepare hardware configuration */ CHECK(snd_pcm_hw_params(data->pcmHandle, hp)); /* retrieve configuration info */ CHECK(snd_pcm_hw_params_get_access(hp, &access)); CHECK(snd_pcm_hw_params_get_period_size(hp, &periodSizeInFrames, NULL)); CHECK(snd_pcm_hw_params_get_periods(hp, &periods, NULL)); snd_pcm_hw_params_free(hp); hp = NULL; snd_pcm_sw_params_malloc(&sp); CHECK(snd_pcm_sw_params_current(data->pcmHandle, sp)); CHECK(snd_pcm_sw_params_set_avail_min(data->pcmHandle, sp, periodSizeInFrames)); CHECK(snd_pcm_sw_params_set_stop_threshold(data->pcmHandle, sp, periodSizeInFrames*periods)); CHECK(snd_pcm_sw_params(data->pcmHandle, sp)); #undef CHECK snd_pcm_sw_params_free(sp); sp = NULL; /* Increase periods by one, since the temp buffer counts as an extra * period */ if(access == SND_PCM_ACCESS_RW_INTERLEAVED) device->NumUpdates = periods+1; else device->NumUpdates = periods; device->UpdateSize = periodSizeInFrames; device->Frequency = rate; SetDefaultChannelOrder(device); return ALC_TRUE; error: ERR("%s failed: %s\n", funcerr, snd_strerror(err)); if(hp) snd_pcm_hw_params_free(hp); if(sp) snd_pcm_sw_params_free(sp); return ALC_FALSE; }
/* open & setup audio device return: 1=success 0=fail */ static int init(int rate_hz, int channels, int format, int flags) { unsigned int alsa_buffer_time = 500000; /* 0.5 s */ unsigned int alsa_fragcount = 16; int err; int block; strarg_t device; snd_pcm_uframes_t chunk_size; snd_pcm_uframes_t bufsize; snd_pcm_uframes_t boundary; const opt_t subopts[] = { {"block", OPT_ARG_BOOL, &block, NULL}, {"device", OPT_ARG_STR, &device, str_maxlen}, {NULL} }; char alsa_device[ALSA_DEVICE_SIZE + 1]; // make sure alsa_device is null-terminated even when using strncpy etc. memset(alsa_device, 0, ALSA_DEVICE_SIZE + 1); mp_msg(MSGT_AO,MSGL_V,"alsa-init: requested format: %d Hz, %d channels, %x\n", rate_hz, channels, format); alsa_handler = NULL; #if SND_LIB_VERSION >= 0x010005 mp_msg(MSGT_AO,MSGL_V,"alsa-init: using ALSA %s\n", snd_asoundlib_version()); #else mp_msg(MSGT_AO,MSGL_V,"alsa-init: compiled for ALSA-%s\n", SND_LIB_VERSION_STR); #endif snd_lib_error_set_handler(alsa_error_handler); ao_data.samplerate = rate_hz; ao_data.format = format; ao_data.channels = channels; switch (format) { case AF_FORMAT_S8: alsa_format = SND_PCM_FORMAT_S8; break; case AF_FORMAT_U8: alsa_format = SND_PCM_FORMAT_U8; break; case AF_FORMAT_U16_LE: alsa_format = SND_PCM_FORMAT_U16_LE; break; case AF_FORMAT_U16_BE: alsa_format = SND_PCM_FORMAT_U16_BE; break; case AF_FORMAT_AC3_LE: case AF_FORMAT_S16_LE: case AF_FORMAT_IEC61937_LE: alsa_format = SND_PCM_FORMAT_S16_LE; break; case AF_FORMAT_AC3_BE: case AF_FORMAT_S16_BE: case AF_FORMAT_IEC61937_BE: alsa_format = SND_PCM_FORMAT_S16_BE; break; case AF_FORMAT_U32_LE: alsa_format = SND_PCM_FORMAT_U32_LE; break; case AF_FORMAT_U32_BE: alsa_format = SND_PCM_FORMAT_U32_BE; break; case AF_FORMAT_S32_LE: alsa_format = SND_PCM_FORMAT_S32_LE; break; case AF_FORMAT_S32_BE: alsa_format = SND_PCM_FORMAT_S32_BE; break; case AF_FORMAT_U24_LE: alsa_format = SND_PCM_FORMAT_U24_3LE; break; case AF_FORMAT_U24_BE: alsa_format = SND_PCM_FORMAT_U24_3BE; break; case AF_FORMAT_S24_LE: alsa_format = SND_PCM_FORMAT_S24_3LE; break; case AF_FORMAT_S24_BE: alsa_format = SND_PCM_FORMAT_S24_3BE; break; case AF_FORMAT_FLOAT_LE: alsa_format = SND_PCM_FORMAT_FLOAT_LE; break; case AF_FORMAT_FLOAT_BE: alsa_format = SND_PCM_FORMAT_FLOAT_BE; break; case AF_FORMAT_MU_LAW: alsa_format = SND_PCM_FORMAT_MU_LAW; break; case AF_FORMAT_A_LAW: alsa_format = SND_PCM_FORMAT_A_LAW; break; default: alsa_format = SND_PCM_FORMAT_MPEG; //? default should be -1 break; } //subdevice parsing // set defaults block = 1; /* switch for spdif * sets opening sequence for SPDIF * sets also the playback and other switches 'on the fly' * while opening the abstract alias for the spdif subdevice * 'iec958' */ if (AF_FORMAT_IS_IEC61937(format)) { device.str = "iec958"; mp_msg(MSGT_AO,MSGL_V,"alsa-spdif-init: playing AC3/iec61937/iec958, %i channels\n", channels); } else /* in any case for multichannel playback we should select * appropriate device */ switch (channels) { case 1: case 2: device.str = "default"; mp_msg(MSGT_AO,MSGL_V,"alsa-init: setup for 1/2 channel(s)\n"); break; case 4: if (alsa_format == SND_PCM_FORMAT_FLOAT_LE) // hack - use the converter plugin device.str = "plug:surround40"; else device.str = "surround40"; mp_msg(MSGT_AO,MSGL_V,"alsa-init: device set to surround40\n"); break; case 6: if (alsa_format == SND_PCM_FORMAT_FLOAT_LE) device.str = "plug:surround51"; else device.str = "surround51"; mp_msg(MSGT_AO,MSGL_V,"alsa-init: device set to surround51\n"); break; case 8: if (alsa_format == SND_PCM_FORMAT_FLOAT_LE) device.str = "plug:surround71"; else device.str = "surround71"; mp_msg(MSGT_AO,MSGL_V,"alsa-init: device set to surround71\n"); break; default: device.str = "default"; mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_ChannelsNotSupported,channels); } device.len = strlen(device.str); if (subopt_parse(ao_subdevice, subopts) != 0) { print_help(); return 0; } parse_device(alsa_device, device.str, device.len); mp_msg(MSGT_AO,MSGL_V,"alsa-init: using device %s\n", alsa_device); if (!alsa_handler) { int open_mode = block ? 0 : SND_PCM_NONBLOCK; int isac3 = AF_FORMAT_IS_IEC61937(format); //modes = 0, SND_PCM_NONBLOCK, SND_PCM_ASYNC mp_msg(MSGT_AO,MSGL_V,"alsa-init: opening device in %sblocking mode\n", block ? "" : "non-"); if ((err = try_open_device(alsa_device, open_mode, isac3)) < 0) { if (err != -EBUSY && !block) { mp_msg(MSGT_AO,MSGL_INFO,MSGTR_AO_ALSA_OpenInNonblockModeFailed); if ((err = try_open_device(alsa_device, 0, isac3)) < 0) { mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_PlaybackOpenError, snd_strerror(err)); return 0; } } else { mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_PlaybackOpenError, snd_strerror(err)); return 0; } } if ((err = snd_pcm_nonblock(alsa_handler, 0)) < 0) { mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_ErrorSetBlockMode, snd_strerror(err)); } else { mp_msg(MSGT_AO,MSGL_V,"alsa-init: device reopened in blocking mode\n"); } snd_pcm_hw_params_alloca(&alsa_hwparams); snd_pcm_sw_params_alloca(&alsa_swparams); // setting hw-parameters if ((err = snd_pcm_hw_params_any(alsa_handler, alsa_hwparams)) < 0) { mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToGetInitialParameters, snd_strerror(err)); return 0; } err = snd_pcm_hw_params_set_access(alsa_handler, alsa_hwparams, SND_PCM_ACCESS_RW_INTERLEAVED); if (err < 0) { mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToSetAccessType, snd_strerror(err)); return 0; } /* workaround for nonsupported formats sets default format to S16_LE if the given formats aren't supported */ if ((err = snd_pcm_hw_params_test_format(alsa_handler, alsa_hwparams, alsa_format)) < 0) { mp_msg(MSGT_AO,MSGL_INFO, MSGTR_AO_ALSA_FormatNotSupportedByHardware, af_fmt2str_short(format)); alsa_format = SND_PCM_FORMAT_S16_LE; if (AF_FORMAT_IS_AC3(ao_data.format)) ao_data.format = AF_FORMAT_AC3_LE; else if (AF_FORMAT_IS_IEC61937(ao_data.format)) ao_data.format = AF_FORMAT_IEC61937_LE; else ao_data.format = AF_FORMAT_S16_LE; } if ((err = snd_pcm_hw_params_set_format(alsa_handler, alsa_hwparams, alsa_format)) < 0) { mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToSetFormat, snd_strerror(err)); return 0; } if ((err = snd_pcm_hw_params_set_channels_near(alsa_handler, alsa_hwparams, &ao_data.channels)) < 0) { mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToSetChannels, snd_strerror(err)); return 0; } /* workaround for buggy rate plugin (should be fixed in ALSA 1.0.11) prefer our own resampler, since that allows users to choose the resampler, even per file if desired */ #if SND_LIB_VERSION >= 0x010009 if ((err = snd_pcm_hw_params_set_rate_resample(alsa_handler, alsa_hwparams, 0)) < 0) { mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToDisableResampling, snd_strerror(err)); return 0; } #endif if ((err = snd_pcm_hw_params_set_rate_near(alsa_handler, alsa_hwparams, &ao_data.samplerate, NULL)) < 0) { mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToSetSamplerate2, snd_strerror(err)); return 0; } bytes_per_sample = af_fmt2bits(ao_data.format) / 8; bytes_per_sample *= ao_data.channels; ao_data.bps = ao_data.samplerate * bytes_per_sample; if ((err = snd_pcm_hw_params_set_buffer_time_near(alsa_handler, alsa_hwparams, &alsa_buffer_time, NULL)) < 0) { mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToSetBufferTimeNear, snd_strerror(err)); return 0; } if ((err = snd_pcm_hw_params_set_periods_near(alsa_handler, alsa_hwparams, &alsa_fragcount, NULL)) < 0) { mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToSetPeriods, snd_strerror(err)); return 0; } /* finally install hardware parameters */ if ((err = snd_pcm_hw_params(alsa_handler, alsa_hwparams)) < 0) { mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToSetHwParameters, snd_strerror(err)); return 0; } // end setting hw-params // gets buffersize for control if ((err = snd_pcm_hw_params_get_buffer_size(alsa_hwparams, &bufsize)) < 0) { mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToGetBufferSize, snd_strerror(err)); return 0; } else { ao_data.buffersize = bufsize * bytes_per_sample; mp_msg(MSGT_AO,MSGL_V,"alsa-init: got buffersize=%i\n", ao_data.buffersize); } if ((err = snd_pcm_hw_params_get_period_size(alsa_hwparams, &chunk_size, NULL)) < 0) { mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToGetPeriodSize, snd_strerror(err)); return 0; } else { mp_msg(MSGT_AO,MSGL_V,"alsa-init: got period size %li\n", chunk_size); } ao_data.outburst = chunk_size * bytes_per_sample; /* setting software parameters */ if ((err = snd_pcm_sw_params_current(alsa_handler, alsa_swparams)) < 0) { mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToGetSwParameters, snd_strerror(err)); return 0; } #if SND_LIB_VERSION >= 0x000901 if ((err = snd_pcm_sw_params_get_boundary(alsa_swparams, &boundary)) < 0) { mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToGetBoundary, snd_strerror(err)); return 0; } #else boundary = 0x7fffffff; #endif /* start playing when one period has been written */ if ((err = snd_pcm_sw_params_set_start_threshold(alsa_handler, alsa_swparams, chunk_size)) < 0) { mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToSetStartThreshold, snd_strerror(err)); return 0; } /* disable underrun reporting */ if ((err = snd_pcm_sw_params_set_stop_threshold(alsa_handler, alsa_swparams, boundary)) < 0) { mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToSetStopThreshold, snd_strerror(err)); return 0; } #if SND_LIB_VERSION >= 0x000901 /* play silence when there is an underrun */ if ((err = snd_pcm_sw_params_set_silence_size(alsa_handler, alsa_swparams, boundary)) < 0) { mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToSetSilenceSize, snd_strerror(err)); return 0; } #endif if ((err = snd_pcm_sw_params(alsa_handler, alsa_swparams)) < 0) { mp_msg(MSGT_AO,MSGL_ERR,MSGTR_AO_ALSA_UnableToGetSwParameters, snd_strerror(err)); return 0; } /* end setting sw-params */ mp_msg(MSGT_AO,MSGL_V,"alsa: %d Hz/%d channels/%d bpf/%d bytes buffer/%s\n", ao_data.samplerate, ao_data.channels, (int)bytes_per_sample, ao_data.buffersize, snd_pcm_format_description(alsa_format)); } // end switch alsa_handler (spdif) alsa_can_pause = snd_pcm_hw_params_can_pause(alsa_hwparams); return 1; } // end init
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; }
int AlsaIO::Initialize(uint Channels, uint Samplerate, uint Fragments, uint FragmentSize, String Card) { this->uiChannels = Channels; this->uiSamplerate = Samplerate; this->uiMaxSamplesPerCycle = FragmentSize; this->bInterleaved = true; if (HardwareParametersSupported(Channels, Samplerate, Fragments, FragmentSize)) { pcm_name = "hw:" + Card; } else { printf("Warning: your soundcard doesn't support chosen hardware parameters; "); printf("trying to compensate support lack with plughw..."); fflush(stdout); pcm_name = "plughw:" + Card; } int err; snd_pcm_hw_params_alloca(&hwparams); // Allocate the snd_pcm_hw_params_t structure on the stack. /* Open PCM. The last parameter of this function is the mode. */ /* If this is set to 0, the standard mode is used. Possible */ /* other values are SND_PCM_NONBLOCK and SND_PCM_ASYNC. */ /* If SND_PCM_NONBLOCK is used, read / write access to the */ /* PCM device will return immediately. If SND_PCM_ASYNC is */ /* specified, SIGIO will be emitted whenever a period has */ /* been completely processed by the soundcard. */ if ((err = snd_pcm_open(&pcm_handle, pcm_name.c_str(), stream, 0)) < 0) { fprintf(stderr, "Error opening PCM device %s: %s\n", pcm_name.c_str(), snd_strerror(err)); return -1; } if ((err = snd_pcm_hw_params_any(pcm_handle, hwparams)) < 0) { fprintf(stderr, "Error, cannot initialize hardware parameter structure: %s.\n", snd_strerror(err)); return -1; } /* Set access type. This can be either */ /* SND_PCM_ACCESS_RW_INTERLEAVED or */ /* SND_PCM_ACCESS_RW_NONINTERLEAVED. */ if ((err = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { fprintf(stderr, "Error snd_pcm_hw_params_set_access: %s.\n", snd_strerror(err)); return -1; } /* Set sample format */ #if WORDS_BIGENDIAN if ((err = snd_pcm_hw_params_set_format(pcm_handle, hwparams, SND_PCM_FORMAT_S16_BE)) < 0) { #else // little endian if ((err = snd_pcm_hw_params_set_format(pcm_handle, hwparams, SND_PCM_FORMAT_S16_LE)) < 0) { #endif fprintf(stderr, "Error setting sample format. : %s\n", snd_strerror(err)); return -1; } int dir = 0; /* Set sample rate. If the exact rate is not supported */ /* by the hardware, use nearest possible rate. */ #if ALSA_MAJOR > 0 if((err = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &Samplerate, &dir)) < 0) { #else if((err = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, Samplerate, &dir)) < 0) { #endif fprintf(stderr, "Error setting sample rate. : %s\n", snd_strerror(err)); return -1; } if ((err = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, Channels)) < 0) { fprintf(stderr, "Error setting number of channels. : %s\n", snd_strerror(err)); return -1; } /* Set number of periods. Periods used to be called fragments. */ if ((err = snd_pcm_hw_params_set_periods(pcm_handle, hwparams, Fragments, dir)) < 0) { fprintf(stderr, "Error setting number of periods. : %s\n", snd_strerror(err)); return -1; } /* Set buffer size (in frames). The resulting latency is given by */ /* latency = periodsize * periods / (rate * bytes_per_frame) */ if ((err = snd_pcm_hw_params_set_buffer_size(pcm_handle, hwparams, (FragmentSize * Fragments))) < 0) { fprintf(stderr, "Error setting buffersize. : %s\n", snd_strerror(err)); return -1; } /* Apply HW parameter settings to */ /* PCM device and prepare device */ if ((err = snd_pcm_hw_params(pcm_handle, hwparams)) < 0) { fprintf(stderr, "Error setting HW params. : %s\n", snd_strerror(err)); return -1; } if (snd_pcm_sw_params_malloc(&swparams) != 0) { fprintf(stderr, "Error in snd_pcm_sw_params_malloc. : %s\n", snd_strerror(err)); return -1; } if (snd_pcm_sw_params_current(pcm_handle, swparams) != 0) { fprintf(stderr, "Error in snd_pcm_sw_params_current. : %s\n", snd_strerror(err)); return -1; } if (snd_pcm_sw_params_set_stop_threshold(pcm_handle, swparams, 0xffffffff) != 0) { fprintf(stderr, "Error in snd_pcm_sw_params_set_stop_threshold. : %s\n", snd_strerror(err)); return -1; } if (snd_pcm_sw_params(pcm_handle, swparams) != 0) { fprintf(stderr, "Error in snd_pcm_sw_params. : %s\n", snd_strerror(err)); return -1; } if ((err = snd_pcm_prepare(pcm_handle)) < 0) { fprintf(stderr, "Error snd_pcm_prepare : %s\n", snd_strerror(err)); return -1; } // allocate the audio output buffer pOutputBuffer = new int16_t[Channels * FragmentSize]; this->bInitialized = true; return 0; } /** * Checks if sound card supports the chosen parameters. * * @returns true if hardware supports it */ bool AlsaIO::HardwareParametersSupported(uint channels, int samplerate, uint numfragments, uint fragmentsize) { pcm_name = "hw:0,0"; if (snd_pcm_open(&pcm_handle, pcm_name.c_str(), stream, 0) < 0) return false; snd_pcm_hw_params_alloca(&hwparams); if (snd_pcm_hw_params_any(pcm_handle, hwparams) < 0) { snd_pcm_close(pcm_handle); return false; } if (snd_pcm_hw_params_test_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) { snd_pcm_close(pcm_handle); return false; } #if WORDS_BIGENDIAN if (snd_pcm_hw_params_test_format(pcm_handle, hwparams, SND_PCM_FORMAT_S16_BE) < 0) { #else // little endian if (snd_pcm_hw_params_test_format(pcm_handle, hwparams, SND_PCM_FORMAT_S16_LE) < 0) { #endif snd_pcm_close(pcm_handle); return false; } int dir = 0; if (snd_pcm_hw_params_test_rate(pcm_handle, hwparams, samplerate, dir) < 0) { snd_pcm_close(pcm_handle); return false; } if (snd_pcm_hw_params_test_channels(pcm_handle, hwparams, channels) < 0) { snd_pcm_close(pcm_handle); return false; } if (snd_pcm_hw_params_test_periods(pcm_handle, hwparams, numfragments, dir) < 0) { snd_pcm_close(pcm_handle); return false; } if (snd_pcm_hw_params_test_buffer_size(pcm_handle, hwparams, (fragmentsize * numfragments)) < 0) { snd_pcm_close(pcm_handle); return false; } snd_pcm_close(pcm_handle); return true; } void AlsaIO::Activate() { this->StartThread(); } int AlsaIO::Main() { if (!pEngine) { fprintf(stderr, "AlsaIO: No Sampler Engine assigned, exiting.\n"); exit(EXIT_FAILURE); } if (!bInitialized) { fprintf(stderr, "AlsaIO: Not yet intitialized, exiting.\n"); exit(EXIT_FAILURE); } while (true) { // let the engine render audio for the current audio fragment pEngine->RenderAudio(uiMaxSamplesPerCycle); // check clipping in the audio sum, convert to sample_type // (from 32bit to 16bit sample) and copy to output buffer float sample_point; uint o = 0; for (uint s = 0; s < uiMaxSamplesPerCycle; s++) { for (uint c = 0; c < uiChannels; c++) { sample_point = pEngine->GetAudioSumBuffer(c)[s] * pEngine->Volume; if (sample_point < -32768.0) sample_point = -32768.0; if (sample_point > 32767.0) sample_point = 32767.0; this->pOutputBuffer[o++] = (int32_t) sample_point; } } // output sound int res = Output(); if (res < 0) { fprintf(stderr, "AlsaIO: Audio output error, exiting.\n"); exit(EXIT_FAILURE); } } } /** * Will be called after every audio fragment cycle, to output the audio data * of the current fragment to the soundcard. * * @returns 0 on success */ int AlsaIO::Output() { int err = snd_pcm_writei(pcm_handle, pOutputBuffer, uiMaxSamplesPerCycle); if (err < 0) { fprintf(stderr, "Error snd_pcm_writei failed. : %s\n", snd_strerror(err)); return -1; } return 0; } void AlsaIO::Close() { if (bInitialized) { //dmsg(0,("Stopping Alsa Thread...")); //StopThread(); //FIXME: commented out due to a bug in thread.cpp (StopThread() doesn't return at all) //dmsg(0,("OK\n")); if (pcm_handle) { //FIXME: currently commented out due to segfault //snd_pcm_close(pcm_handle); pcm_handle = NULL; } if (pOutputBuffer) { //FIXME: currently commented out due to segfault //delete[] pOutputBuffer; pOutputBuffer = NULL; } bInitialized = false; } } void* AlsaIO::GetInterleavedOutputBuffer() { return pOutputBuffer; } void* AlsaIO::GetChannelOutputBufer(uint Channel) { fprintf(stderr, "AlsaIO::GetChannelOutputBufer(): Only interleaved access allowed so far, exiting.\n"); exit(EXIT_FAILURE); // just to avoid compiler warnings return NULL; }
static SCM open_card(SCM device) { // soundcard acquisition and configuration char *dname; snd_pcm_t *handle; ALSA_CARD *card; snd_pcm_sw_params_t *sparams; SOURCE_HANDLE *src; snd_pcm_format_t f, format; SCM smob; int i, ret, dir; unsigned int rate, buffer_time; dname = scm_to_locale_string(device); for (i = 0; i < 10; i++) { ret = snd_pcm_open(&handle, dname, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK); if (ret >= 0) break; } if (ret < 0) { log_msg("ALSA: can't open %s (%s)\n", dname, snd_strerror(ret)); free(dname); return SCM_BOOL_F; } card = (ALSA_CARD *)my_malloc(sizeof(ALSA_CARD), "alsa card"); card->device = dname; card->name = card_name(card->device); card->ringbuf = NULL; init_source(&card->base); card->handle = handle; card->running = 0; snd_pcm_hw_params_malloc(&card->hparams); snd_pcm_hw_params_any(card->handle, card->hparams); for (f = format = 0; f < SND_PCM_FORMAT_LAST; f++) { ret = snd_pcm_hw_params_test_format(card->handle, card->hparams, f); if (ret == 0) { log_msg("ALSA: - %s\n", snd_pcm_format_name(f)); format = f; } } ret = snd_pcm_hw_params_set_access(card->handle, card->hparams, SND_PCM_ACCESS_RW_INTERLEAVED); if (ret < 0) log_msg("ALSA: access %s\n", snd_strerror(ret)); ret = snd_pcm_hw_params_set_format(card->handle, card->hparams, format); log_msg("ALSA: format: %s\n", snd_pcm_format_description(format)); if (ret < 0) log_msg("ALSA: format error %s\n", snd_strerror(ret)); snd_pcm_hw_params_get_buffer_time_max(card->hparams, &buffer_time, 0); rate = sampling_rate; ret = snd_pcm_hw_params_set_rate(card->handle, card->hparams, rate, 0); log_msg("ALSA: rate %d\n", rate); if (ret < 0) log_msg("ALSA: rate error %s\n", snd_strerror(ret)); snd_pcm_hw_params_set_channels(card->handle, card->hparams, QMX_CHANNELS); card->blocksize = BLOCKSIZE; snd_pcm_hw_params_set_period_size(card->handle, card->hparams, card->blocksize, 0); log_msg("ALSA: period %ld\n", card->blocksize); snd_pcm_hw_params_set_buffer_time_near(card->handle, card->hparams, &buffer_time, &dir); log_msg("ALSA: buffer time %u\n", buffer_time); if ((ret = snd_pcm_hw_params(card->handle, card->hparams)) < 0) { log_msg("ALSA: can't set hardware: %s\n", snd_strerror(ret)); close_card(card); return SCM_BOOL_F; } else log_msg("ALSA: hardware configd\n"); snd_pcm_sw_params_malloc(&sparams); snd_pcm_sw_params_current(card->handle, sparams); snd_pcm_sw_params_set_avail_min(card->handle, sparams, card->blocksize); snd_pcm_sw_params_set_start_threshold(card->handle, sparams, 0U); if ((ret = snd_pcm_sw_params(card->handle, sparams)) < 0) { log_msg("ALSA: can't set software: %s\n", snd_strerror(ret)); } else log_msg("ALSA: software configd\n"); snd_pcm_sw_params_free(sparams); card->link = cards; cards = card; src = (SOURCE_HANDLE *)my_gc_malloc(sizeof(SOURCE_HANDLE), "alsa_card", "alsa card handle"); src->body = (void *)card; src->src = &card->base; log_msg("ALSA: opened %s '%s'\n", card->device, card->name); SCM_NEWSMOB(smob, alsa_card_tag, src); return smob; }
void CAESinkALSA::EnumerateDevicesEx(AEDeviceInfoList &list) { /* ensure that ALSA has been initialized */ if(!snd_config) snd_config_update(); snd_ctl_t *ctlhandle; snd_pcm_t *pcmhandle; snd_ctl_card_info_t *ctlinfo; snd_ctl_card_info_alloca(&ctlinfo); memset(ctlinfo, 0, snd_ctl_card_info_sizeof()); snd_pcm_hw_params_t *hwparams; snd_pcm_hw_params_alloca(&hwparams); memset(hwparams, 0, snd_pcm_hw_params_sizeof()); snd_pcm_info_t *pcminfo; snd_pcm_info_alloca(&pcminfo); memset(pcminfo, 0, snd_pcm_info_sizeof()); /* get the sound config */ snd_config_t *config; snd_config_copy(&config, snd_config); std::string strHwName; int n_cards = -1; while (snd_card_next(&n_cards) == 0 && n_cards != -1) { std::stringstream sstr; sstr << "hw:" << n_cards; std::string strHwName = sstr.str(); if (snd_ctl_open_lconf(&ctlhandle, strHwName.c_str(), 0, config) != 0) { CLog::Log(LOGDEBUG, "CAESinkALSA::EnumerateDevicesEx - Unable to open control for device %s", strHwName.c_str()); continue; } if (snd_ctl_card_info(ctlhandle, ctlinfo) != 0) { CLog::Log(LOGDEBUG, "CAESinkALSA::EnumerateDevicesEx - Unable to get card control info for device %s", strHwName.c_str()); snd_ctl_close(ctlhandle); continue; } snd_hctl_t *hctl; if (snd_hctl_open_ctl(&hctl, ctlhandle) != 0) hctl = NULL; snd_hctl_load(hctl); int pcm_index = 0; int iec958_index = 0; int hdmi_index = 0; int dev = -1; while (snd_ctl_pcm_next_device(ctlhandle, &dev) == 0 && dev != -1) { snd_pcm_info_set_device (pcminfo, dev); snd_pcm_info_set_subdevice(pcminfo, 0 ); snd_pcm_info_set_stream (pcminfo, SND_PCM_STREAM_PLAYBACK); if (snd_ctl_pcm_info(ctlhandle, pcminfo) < 0) { CLog::Log(LOGDEBUG, "CAESinkALSA::EnumerateDevicesEx - Skipping device %s,%d as it does not have PCM playback ability", strHwName.c_str(), dev); continue; } int dev_index; sstr.str(std::string()); CAEDeviceInfo info; std::string devname = snd_pcm_info_get_name(pcminfo); bool maybeHDMI = false; /* detect HDMI */ if (devname.find("HDMI") != std::string::npos) { info.m_deviceType = AE_DEVTYPE_HDMI; dev_index = hdmi_index++; sstr << "hdmi"; } else { /* detect IEC958 */ /* some HDMI devices (intel) report Digital for HDMI also */ if (devname.find("Digital") != std::string::npos) maybeHDMI = true; if (maybeHDMI || devname.find("IEC958" ) != std::string::npos) { info.m_deviceType = AE_DEVTYPE_IEC958; dev_index = iec958_index; /* dont increment, it might be HDMI */ sstr << "iec958"; } else { info.m_deviceType = AE_DEVTYPE_PCM; dev_index = pcm_index++; sstr << "hw"; } } /* build the driver string to pass to ALSA */ sstr << ":CARD=" << snd_ctl_card_info_get_id(ctlinfo) << ",DEV=" << dev_index; info.m_deviceName = sstr.str(); /* get the friendly display name*/ info.m_displayName = snd_ctl_card_info_get_name(ctlinfo); info.m_displayNameExtra = devname; /* open the device for testing */ int err = snd_pcm_open_lconf(&pcmhandle, info.m_deviceName.c_str(), SND_PCM_STREAM_PLAYBACK, 0, config); /* if open of possible IEC958 failed and it could be HDMI, try as HDMI */ if (err < 0 && maybeHDMI) { /* check for HDMI if it failed */ sstr.str(std::string()); dev_index = hdmi_index; sstr << "hdmi"; sstr << ":CARD=" << snd_ctl_card_info_get_id(ctlinfo) << ",DEV=" << dev_index; info.m_deviceName = sstr.str(); err = snd_pcm_open_lconf(&pcmhandle, info.m_deviceName.c_str(), SND_PCM_STREAM_PLAYBACK, 0, config); /* if it was valid, increment the index and set the type */ if (err >= 0) { ++hdmi_index; info.m_deviceType = AE_DEVTYPE_HDMI; } } /* if it's still IEC958, increment the index */ if (info.m_deviceType == AE_DEVTYPE_IEC958) ++iec958_index; /* final error check */ if (err < 0) { CLog::Log(LOGINFO, "CAESinkALSA::EnumerateDevicesEx - Unable to open %s for capability detection", strHwName.c_str()); continue; } /* see if we can get ELD for this device */ if (info.m_deviceType == AE_DEVTYPE_HDMI) { bool badHDMI = false; if (hctl && !GetELD(hctl, dev, info, badHDMI)) CLog::Log(LOGDEBUG, "CAESinkALSA::EnumerateDevicesEx - Unable to obtain ELD information for device %s, make sure you have ALSA >= 1.0.25", info.m_deviceName.c_str()); if (badHDMI) { CLog::Log(LOGDEBUG, "CAESinkALSA::EnumerateDevicesEx - Skipping HDMI device %s as it has no ELD data", info.m_deviceName.c_str()); continue; } } /* ensure we can get a playback configuration for the device */ if (snd_pcm_hw_params_any(pcmhandle, hwparams) < 0) { CLog::Log(LOGINFO, "CAESinkALSA::EnumerateDevicesEx - No playback configurations available for device %s", info.m_deviceName.c_str()); snd_pcm_close(pcmhandle); continue; } /* 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 = 1; i <= ALSA_MAX_CHANNELS; ++i) if (snd_pcm_hw_params_test_channels(pcmhandle, hwparams, i) >= 0) channels = i; 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); } /* snd_hctl_close also closes ctlhandle */ if (hctl) snd_hctl_close(hctl); else snd_ctl_close(ctlhandle); } }
static int Open (vlc_object_t *obj) { demux_t *demux = (demux_t *)obj; demux_sys_t *sys = vlc_obj_malloc(obj, sizeof (*sys)); if (unlikely(sys == NULL)) return VLC_ENOMEM; /* Open the device */ const char *device = demux->psz_location; if (device == NULL || !device[0]) device = "default"; const int mode = SND_PCM_NONBLOCK /*| SND_PCM_NO_AUTO_RESAMPLE*/ | SND_PCM_NO_AUTO_CHANNELS /*| SND_PCM_NO_AUTO_FORMAT*/; snd_pcm_t *pcm; int val = snd_pcm_open (&pcm, device, SND_PCM_STREAM_CAPTURE, mode); if (val != 0) { msg_Err (demux, "cannot open ALSA device \"%s\": %s", device, snd_strerror (val)); return VLC_EGENERIC; } sys->pcm = pcm; msg_Dbg (demux, "using ALSA device: %s", device); DumpDevice (VLC_OBJECT(demux), pcm); /* Negotiate capture parameters */ snd_pcm_hw_params_t *hw; es_format_t fmt; unsigned param; int dir; snd_pcm_hw_params_alloca (&hw); snd_pcm_hw_params_any (pcm, hw); Dump (demux, "initial hardware setup:\n", snd_pcm_hw_params_dump, hw); val = snd_pcm_hw_params_set_rate_resample (pcm, hw, 0); if (val) { msg_Err (demux, "cannot disable resampling: %s", snd_strerror (val)); goto error; } val = snd_pcm_hw_params_set_access (pcm, hw, SND_PCM_ACCESS_RW_INTERLEAVED); if (val) { msg_Err (demux, "cannot set access mode: %s", snd_strerror (val)); goto error; } snd_pcm_format_t format = SND_PCM_FORMAT_UNKNOWN; for (size_t i = 0; i < sizeof (choices) / sizeof (choices[0]); i++) if (snd_pcm_hw_params_test_format (pcm, hw, choices[i]) == 0) { val = snd_pcm_hw_params_set_format (pcm, hw, choices[i]); if (val) { msg_Err (demux, "cannot set sample format: %s", snd_strerror (val)); goto error; } format = choices[i]; break; } if (format == SND_PCM_FORMAT_UNKNOWN) { msg_Err (demux, "no supported sample format"); goto error; } assert ((size_t)format < (sizeof (formats) / sizeof (formats[0]))); es_format_Init (&fmt, AUDIO_ES, formats[format]); fmt.audio.i_format = fmt.i_codec; param = 1 + var_InheritBool (demux, "alsa-stereo"); val = snd_pcm_hw_params_set_channels_max (pcm, hw, ¶m); if (val) { msg_Err (demux, "cannot restrict channels count: %s", snd_strerror (val)); goto error; } val = snd_pcm_hw_params_set_channels_last (pcm, hw, ¶m); if (val) { msg_Err (demux, "cannot set channels count: %s", snd_strerror (val)); goto error; } assert (param > 0); assert (param < (sizeof (channel_maps) / sizeof (channel_maps[0]))); fmt.audio.i_channels = param; fmt.audio.i_physical_channels = channel_maps[param - 1]; param = var_InheritInteger (demux, "alsa-samplerate"); val = snd_pcm_hw_params_set_rate_max (pcm, hw, ¶m, NULL); if (val) { msg_Err (demux, "cannot restrict rate to %u Hz or less: %s", 192000, snd_strerror (val)); goto error; } val = snd_pcm_hw_params_set_rate_last (pcm, hw, ¶m, &dir); if (val) { msg_Err (demux, "cannot set sample rate: %s", snd_strerror (val)); goto error; } if (dir) msg_Warn (demux, "sample rate is not integral"); fmt.audio.i_rate = param; sys->rate = param; sys->start = mdate (); sys->caching = INT64_C(1000) * var_InheritInteger (demux, "live-caching"); param = sys->caching; val = snd_pcm_hw_params_set_buffer_time_near (pcm, hw, ¶m, NULL); if (val) { msg_Err (demux, "cannot set buffer duration: %s", snd_strerror (val)); goto error; } param /= 4; val = snd_pcm_hw_params_set_period_time_near (pcm, hw, ¶m, NULL); if (val) { msg_Err (demux, "cannot set period: %s", snd_strerror (val)); goto error; } val = snd_pcm_hw_params_get_period_size (hw, &sys->period_size, &dir); if (val) { msg_Err (demux, "cannot get period size: %s", snd_strerror (val)); goto error; } if (dir > 0) sys->period_size++; /* Commit hardware parameters */ val = snd_pcm_hw_params (pcm, hw); if (val) { msg_Err (demux, "cannot commit hardware parameters: %s", snd_strerror (val)); goto error; } Dump (demux, "final HW setup:\n", snd_pcm_hw_params_dump, hw); /* Kick recording */ aout_FormatPrepare (&fmt.audio); sys->es = es_out_Add (demux->out, &fmt); demux->p_sys = sys; if (vlc_clone (&sys->thread, Thread, demux, VLC_THREAD_PRIORITY_INPUT)) { es_out_Del (demux->out, sys->es); goto error; } demux->pf_demux = NULL; demux->pf_control = Control; return VLC_SUCCESS; error: snd_pcm_close (pcm); return VLC_EGENERIC; }
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); }
static int capture(lua_State *lstate) { char *card; snd_pcm_sw_params_t *sparams; int ret, dir, i; snd_pcm_format_t f, format; unsigned int rate, buffer_time; if (rbuf == NULL) { rbuf = new_ringbuf(jack_sr, CHANNELS, BUFSECS, 0.333, 0.667); } getstring(lstate, "card", &card); lua_pop(lstate, 1); for (i = 0; i < 20; i++) { ret = snd_pcm_open(&handle, card, SND_PCM_STREAM_CAPTURE, 0); if (ret < 0) { logmsg("can't open %s (%s)\n", card, snd_strerror(ret)); } else { break; } } free(card); if (ret < 0) return 0; if (hparams != NULL) snd_pcm_hw_params_free(hparams); snd_pcm_hw_params_malloc(&hparams); snd_pcm_hw_params_any(handle, hparams); for (f = format = 0; f < SND_PCM_FORMAT_LAST; f++) { ret = snd_pcm_hw_params_test_format(handle, hparams, f); if (ret == 0) { logmsg("- %s\n", snd_pcm_format_name(f)); format = f; } } ret = snd_pcm_hw_params_set_access(handle, hparams, SND_PCM_ACCESS_RW_INTERLEAVED); if (ret < 0) { logmsg("access %s\n", snd_strerror(ret)); } ret = snd_pcm_hw_params_set_format(handle, hparams, format); logmsg("format: %s\n", snd_pcm_format_description(format)); if (ret < 0) { logmsg("format error %s\n", snd_strerror(ret)); } snd_pcm_hw_params_get_buffer_time_max(hparams, &buffer_time, 0); rate = jack_sr; ret = snd_pcm_hw_params_set_rate(handle, hparams, rate, 0); logmsg("rate %d\n", rate); if (ret < 0) logmsg("rate error %s\n", snd_strerror(ret)); snd_pcm_hw_params_set_channels(handle, hparams, CHANNELS); blocksize = BLOCKSIZE; snd_pcm_hw_params_set_period_size(handle, hparams, blocksize, 0); logmsg("period %ld\n", blocksize); snd_pcm_hw_params_set_buffer_time_near(handle, hparams, &buffer_time, &dir); logmsg("buffer time %u\n", buffer_time); if ((ret = snd_pcm_hw_params(handle, hparams)) < 0) { logmsg("can't set hardware: %s\n", snd_strerror(ret)); return 0; } else logmsg("hardware configd\n"); snd_pcm_sw_params_malloc(&sparams); snd_pcm_sw_params_current(handle, sparams); snd_pcm_sw_params_set_avail_min(handle, sparams, blocksize); snd_pcm_sw_params_set_start_threshold(handle, sparams, 0U); if ((ret = snd_pcm_sw_params(handle, sparams)) < 0) { logmsg("can't set software: %s\n", snd_strerror(ret)); } else logmsg("software configd\n"); snd_pcm_sw_params_free(sparams); pthread_create(&thread_id, NULL, dev_thread, NULL); return 0; }
bool AlsaBackend::ProbeParameters(DeviceInfo* device, StreamSpec& spec, snd_pcm_t* phandle, snd_pcm_stream_t stream, snd_pcm_info_t *pcminfo, char *name, snd_pcm_hw_params_t *params) { // At this point, we just need to figure out the supported data // formats and sample rates. We'll proceed by opening the device in // the direction with the maximum number of channels, or playback if // they are equal. This might limit our sample rate options, but so // be it. int result; // FIXME: specify the direction in options beforehand? if (device->outMaxChannels >= device->inMaxChannels) stream = SND_PCM_STREAM_PLAYBACK; else stream = SND_PCM_STREAM_CAPTURE; snd_pcm_info_set_stream(pcminfo, stream); result = snd_pcm_open(&phandle, device->guid, stream, SND_PCM_ASYNC | SND_PCM_NONBLOCK); if (result < 0) { Log("snd_pcm_open error for device %s: %s", name, snd_strerror(result)); Log("ProbeParameters failed!"); return false; } // 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)); Log("ProbeParameters failed!"); return false; } // Test if exact required rate is supported. if (snd_pcm_hw_params_test_rate(phandle, params, spec.rate, 0) == 0) { Log("Matching rate %d found", spec.rate); } else { // try to set rate near the requested one //..... Log("Matching rate NOT found."); unsigned int exactRate = spec.rate; result = snd_pcm_hw_params_set_rate_near(phandle, params, &exactRate, 0); if (result < 0) { snd_pcm_close(phandle); Log("no supported sample rates found for device %s: %s", name, snd_strerror(result)); Log("ProbeParameters failed!"); return false; } if (spec.rate != exactRate) { Log("The rate %d Hz is not supported by your hardware. Using %d Hz instead.", spec.rate, exactRate); spec.rate = exactRate; } } // Probe the supported data formats ... we don't care about endian-ness just yet // Check the requested format first and be happy if it works. snd_pcm_format_t format; device->supportedFormats = 0; format = FormatToAlsa(spec.format); if (snd_pcm_hw_params_test_format(phandle, params, format) == 0) { device->supportedFormats |= spec.format; snd_pcm_close(phandle); device->probed = true; return true; } // Check that we have at least one supported format if (device->supportedFormats == 0) { Log("pcm device %s data format is not supported, cannot open", name); Log("ProbeParameters failed!"); return false; } // That's all ... close the device and return snd_pcm_close(phandle); device->probed = true; return true; }