*
* After this call, #snd_pcm_prepare() is called automatically and
* the stream is brought to \c #SND_PCM_STATE_PREPARED state.
+ *
+ * The hardware parameters cannot be changed when the stream is
+ * running (active). The software parameters can be changed
+ * at any time.
*/
int snd_pcm_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
{
* \param pcm PCM handle
* \param params Configuration container
* \return 0 on success otherwise a negative error code
+ *
+ * The software parameters can be changed at any time.
+ * The hardware parameters cannot be changed when the stream is
+ * running (active).
*/
int snd_pcm_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t *params)
{
OBSOLETE1(snd_pcm_sw_params_get_silence_size, ALSA_0.9, ALSA_0.9.0rc4);
#endif /* DOC_HIDDEN */
+
+/*
+ * basic helpers
+ */
+
+
+/**
+ * \brief Recover the stream state from an error or suspend
+ * \param pcm PCM handle
+ * \param err error number
+ * \param silent do not print error reason
+ * \return 0 when error code was handled successfuly, otherwise a negative error code
+ *
+ * This functions handles -EINTR (interrupted system call),
+ * -EPIPE (overrun or underrun) and -ESTRPIPE (stream is suspended)
+ * error codes trying to prepare given stream for next I/O.
+ *
+ * Note that this function returs the original error code when it is not
+ * handled inside this function (for example -EAGAIN is returned back).
+ */
+int snd_pcm_recover(snd_pcm_t *pcm, int err, int silent)
+{
+ if (err > 0)
+ err = -err;
+ if (err == -EINTR) /* nothing to do, continue */
+ return 0;
+ if (err == -EPIPE) {
+ const char *s;
+ if (snd_pcm_stream(pcm) == SND_PCM_STREAM_PLAYBACK)
+ s = "underrun";
+ else
+ s = "overrun";
+ if (!silent)
+ SNDERR("%s occured", s);
+ err = snd_pcm_prepare(pcm);
+ if (err < 0) {
+ SNDERR("cannot recovery from %s, prepare failed: %s", s, snd_strerror(err));
+ return err;
+ }
+ return 0;
+ }
+ if (err == -ESTRPIPE) {
+ while ((err = snd_pcm_resume(pcm)) == -EAGAIN)
+ /* wait until suspend flag is released */
+ poll(NULL, 0, 1000);
+ if (err < 0) {
+ err = snd_pcm_prepare(pcm);
+ if (err < 0) {
+ SNDERR("cannot recovery from suspend, prepare failed: %s", snd_strerror(err));
+ return err;
+ }
+ }
+ return 0;
+ }
+ return err;
+}
+
+/**
+ * \brief Set the hardware and software parameters in a simple way
+ * \param pcm PCM handle
+ * \param format required PCM format
+ * \param access required PCM access
+ * \param channels required PCM channels
+ * \param rate required sample rate in Hz
+ * \param soft_resample 0 = disallow alsa-lib resample stream, 1 = allow resampling
+ * \param latency required overall latency in us (0 = optimum latency for players)
+ * \return 0 on success otherwise a negative error code
+ */
+int snd_pcm_set_params(snd_pcm_t *pcm,
+ snd_pcm_format_t format,
+ snd_pcm_access_t access,
+ unsigned int channels,
+ unsigned int rate,
+ int soft_resample,
+ unsigned int latency)
+{
+ snd_pcm_hw_params_t *params;
+ snd_pcm_sw_params_t *swparams;
+ const char *s = snd_pcm_stream_name(snd_pcm_stream(pcm));
+ snd_pcm_uframes_t buffer_size, period_size;
+ unsigned int rrate, period_time;
+ int err;
+
+ snd_pcm_hw_params_alloca(¶ms);
+ snd_pcm_sw_params_alloca(&swparams);
+
+ assert(pcm);
+ /* choose all parameters */
+ err = snd_pcm_hw_params_any(pcm, params);
+ if (err < 0) {
+ SNDERR("Broken configuration for %s: no configurations available", s);
+ return err;
+ }
+ /* set software resampling */
+ err = snd_pcm_hw_params_set_rate_resample(pcm, params, soft_resample);
+ if (err < 0) {
+ SNDERR("Resampling setup failed for %s: %s", s, snd_strerror(err));
+ return err;
+ }
+ /* set the selected read/write format */
+ err = snd_pcm_hw_params_set_access(pcm, params, access);
+ if (err < 0) {
+ SNDERR("Access type not available for %s: %s", s, snd_strerror(err));
+ return err;
+ }
+ /* set the sample format */
+ err = snd_pcm_hw_params_set_format(pcm, params, format);
+ if (err < 0) {
+ SNDERR("Sample format not available for %s: %s", s, snd_strerror(err));
+ return err;
+ }
+ /* set the count of channels */
+ err = snd_pcm_hw_params_set_channels(pcm, params, channels);
+ if (err < 0) {
+ SNDERR("Channels count (%i) not available for %s: %s", s, channels, snd_strerror(err));
+ return err;
+ }
+ /* set the stream rate */
+ rrate = rate;
+ err = INTERNAL(snd_pcm_hw_params_set_rate_near)(pcm, params, &rrate, 0);
+ if (err < 0) {
+ SNDERR("Rate %iHz not available for playback: %s", rate, snd_strerror(err));
+ return err;
+ }
+ if (rrate != rate) {
+ SNDERR("Rate doesn't match (requested %iHz, get %iHz)", rate, err);
+ return -EINVAL;
+ }
+ /* set the buffer time */
+ err = INTERNAL(snd_pcm_hw_params_set_buffer_time_near)(pcm, params, &latency, NULL);
+ if (err < 0) {
+ SNDERR("Unable to set buffer time (latency) %i for %s: %s", latency, s, snd_strerror(err));
+ return err;
+ }
+ err = INTERNAL(snd_pcm_hw_params_get_buffer_size)(params, &buffer_size);
+ if (err < 0) {
+ SNDERR("Unable to get buffer size for %s: %s", s, snd_strerror(err));
+ return err;
+ }
+ err = INTERNAL(snd_pcm_hw_params_get_buffer_time)(params, &latency, NULL);
+ if (err < 0) {
+ SNDERR("Unable to get buffer time (latency) for %s: %s", s, snd_strerror(err));
+ return err;
+ }
+ /* set the period time */
+ period_time = latency / 4;
+ err = INTERNAL(snd_pcm_hw_params_set_period_time_near)(pcm, params, &period_time, NULL);
+ if (err < 0) {
+ SNDERR("Unable to set period time %i for %s: %s", s, period_time, snd_strerror(err));
+ return err;
+ }
+ err = INTERNAL(snd_pcm_hw_params_get_period_size)(params, &period_size, NULL);
+ if (err < 0) {
+ SNDERR("Unable to get period size for %s: %s", s, snd_strerror(err));
+ return err;
+ }
+ /* write the parameters to device */
+ err = snd_pcm_hw_params(pcm, params);
+ if (err < 0) {
+ SNDERR("Unable to set hw params for %s: %s", s, snd_strerror(err));
+ return err;
+ }
+
+ /* get the current swparams */
+ err = snd_pcm_sw_params_current(pcm, swparams);
+ if (err < 0) {
+ SNDERR("Unable to determine current swparams for %s: %s", s, snd_strerror(err));
+ return err;
+ }
+ /* start the transfer when the buffer is almost full: */
+ /* (buffer_size / avail_min) * avail_min */
+ err = snd_pcm_sw_params_set_start_threshold(pcm, swparams, (buffer_size / period_size) * period_size);
+ if (err < 0) {
+ SNDERR("Unable to set start threshold mode for %s: %s", s, snd_strerror(err));
+ return err;
+ }
+ /* allow the transfer when at least period_size samples can be processed */
+ err = snd_pcm_sw_params_set_avail_min(pcm, swparams, period_size);
+ if (err < 0) {
+ SNDERR("Unable to set avail min for %s: %s", s, snd_strerror(err));
+ return err;
+ }
+ /* align all transfers to 1 sample */
+ err = snd_pcm_sw_params_set_xfer_align(pcm, swparams, 1);
+ if (err < 0) {
+ SNDERR("Unable to set transfer align for %s: %s", s, snd_strerror(err));
+ return err;
+ }
+ /* write the parameters to the playback device */
+ err = snd_pcm_sw_params(pcm, swparams);
+ if (err < 0) {
+ SNDERR("Unable to set sw params for %s: %s", s, snd_strerror(err));
+ return err;
+ }
+ return 0;
+}
+
+/**
+ * \brief Get the transfer size parameters in a simple way
+ * \param pcm PCM handle
+ * \param buffer_size PCM ring buffer size in frames
+ * \param period_size PCM period size in frames
+ * \return 0 on success otherwise a negative error code
+ */
+int snd_pcm_get_params(snd_pcm_t *pcm,
+ snd_pcm_uframes_t *buffer_size,
+ snd_pcm_uframes_t *period_size)
+{
+ assert(pcm);
+ snd_pcm_hw_params_t *hw;
+ int err;
+
+ snd_pcm_hw_params_alloca(&hw);
+ err = snd_pcm_hw_params_current(pcm, hw);
+ if (err < 0)
+ return err;
+ err = INTERNAL(snd_pcm_hw_params_get_buffer_size)(hw, buffer_size);
+ if (err < 0)
+ return err;
+ err = INTERNAL(snd_pcm_hw_params_get_period_size)(hw, period_size, NULL);
+ if (err < 0)
+ return err;
+ return 0;
+}