]> git.alsa-project.org Git - alsa-lib.git/commitdiff
implementation of pcm simple helper function
authorJaroslav Kysela <perex@perex.cz>
Fri, 6 Jan 2006 19:59:56 +0000 (19:59 +0000)
committerJaroslav Kysela <perex@perex.cz>
Fri, 6 Jan 2006 19:59:56 +0000 (19:59 +0000)
- snd_pcm_recovery()
- snd_pcm_set_params()
- snd_pcm_get_params()

include/pcm.h
src/Versions
src/pcm/pcm.c

index 4927add25ce931b015c4d6a6686883a010dcf6f6..5aca759bebae85bcdc7515412785f419a9118923 100644 (file)
@@ -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);
+
 /** \} */
 
 /**
index 02e86ba4f8a28e1db53898b2a9f87a380bd1b5b5..cb4cc22980420a1ccc2412920c9f172b329bdd36 100644 (file)
@@ -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;
index 791022037e2fd4e0084c49290b1c95ab2a697742..b3f3e6bd5b415632108a07ad95ddec80bc12a672 100644 (file)
@@ -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(&params);
+        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;
+}