From 23f7e58fa1e36b06cfcaf0ff5d9d392735235fc7 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Fri, 6 Jan 2006 19:59:56 +0000 Subject: [PATCH] implementation of pcm simple helper function - snd_pcm_recovery() - snd_pcm_set_params() - snd_pcm_get_params() --- include/pcm.h | 17 ++++ src/Versions | 8 ++ src/pcm/pcm.c | 232 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 257 insertions(+) diff --git a/include/pcm.h b/include/pcm.h index 4927add2..5aca759b 100644 --- a/include/pcm.h +++ b/include/pcm.h @@ -437,6 +437,23 @@ int snd_pcm_wait(snd_pcm_t *pcm, int timeout); int snd_pcm_link(snd_pcm_t *pcm1, snd_pcm_t *pcm2); int snd_pcm_unlink(snd_pcm_t *pcm); +/* + * application helpers - these functions are implemented on top + * of the basic API + */ + +int snd_pcm_recover(snd_pcm_t *pcm, int err, int silent); +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); +int snd_pcm_get_params(snd_pcm_t *pcm, + snd_pcm_uframes_t *buffer_size, + snd_pcm_uframes_t *period_size); + /** \} */ /** diff --git a/src/Versions b/src/Versions index 02e86ba4..cb4cc229 100644 --- a/src/Versions +++ b/src/Versions @@ -266,3 +266,11 @@ ALSA_1.0.10 { snd_ctl_ext_delete; } ALSA_1.0.9; + +ALSA_1.0.11 { + global: + + snd_pcm_recover; + snd_pcm_set_params; + snd_pcm_get_params; +} ALSA_1.0.10; diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c index 79102203..b3f3e6bd 100644 --- a/src/pcm/pcm.c +++ b/src/pcm/pcm.c @@ -778,6 +778,10 @@ int snd_pcm_hw_params_current(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) * * 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) { @@ -817,6 +821,10 @@ int snd_pcm_hw_free(snd_pcm_t *pcm) * \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) { @@ -6985,3 +6993,227 @@ OBSOLETE1(snd_pcm_sw_params_get_silence_threshold, ALSA_0.9, ALSA_0.9.0rc4); 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; +} -- 2.47.1