#include "pcm_local.h"
diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c
index 9aec52d104506632fce4fe5a6915bcb25479af6d..62e76e7e04c313f83d548ddbcc8c49d219f7fe6e 100644
--- a/src/pcm/pcm.c
+++ b/src/pcm/pcm.c
@@ -96,7 +96,7 @@ standard C open function - see 'man 2 open'). In non-blocked behaviour,
these I/O functions never stops, they return -EAGAIN error code, when no
data can be transferred (the ring buffer is full in our case). In blocked
behaviour, these I/O functions stop and wait until there is a room in the
-ring buffer (playback) or until there are a new samples (capture). The ALSA
+ring buffer (playback) or until there are new samples (capture). The ALSA
implementation can be found in the \ref alsa_pcm_rw section.
\subsection pcm_transfer_event Event waiting routines
@@ -215,7 +215,8 @@ range, thus you may get the significant bits for linear samples via
#snd_pcm_hw_params_get_sbits() function. The example: ICE1712
chips support 32-bit sample processing, but low byte is ignored (playback)
or zero (capture). The function snd_pcm_hw_params_get_sbits()
-returns 24 in this case.
+returns 24 in this case. The significant bits are related to the usable
+sample bits (width) not the physical sample space.
\section alsa_transfers ALSA transfers
@@ -223,7 +224,7 @@ There are two methods to transfer samples in application. The first method
is the standard read / write one. The second method, uses the direct audio
buffer to communicate with the device while ALSA library manages this space
itself. You can find examples of all communication schemes for playback
-in \ref example_test_pcm "Sine-wave generator example". To complete the
+in \link example_test_pcm Sine-wave generator example \endlink. To complete the
list, we should note that #snd_pcm_wait() function contains
embedded poll waiting implementation.
@@ -351,9 +352,9 @@ enumeration.
These parameters - #snd_pcm_sw_params_t can be modified at
any time including the running state.
-\par Minimum available count of samples
+\par Minimum available count of frames
-This parameter controls the wakeup point. If the count of available samples
+This parameter controls the wakeup point. If the count of available frames
is equal or greater than this value, then application will be activated.
\par Timestamp mode
@@ -372,29 +373,29 @@ is ignored by device. Usually, this value is set to one (no align).
\par Start threshold
The start threshold parameter is used to determine the start point in
-stream. For playback, if samples in ring buffer is equal or greater than
-the start threshold parameters and the stream is not running, the stream will
-be started automatically from the device. For capture, if the application wants
-to read count of samples equal or greater then the stream will be started.
-If you want to use explicit start (#snd_pcm_start), you can
-set this value greater than ring buffer size (in samples), but use the
-constant MAXINT is not a bad idea.
+stream. For playback, if the frame count in the ring buffer is equal or greater
+than the start threshold parameter and the stream is not running, the stream
+will be started automatically from the device. For capture, if the application
+wants to read count of frames equal or greater then the stream will be started.
+If you want to use explicit start (#snd_pcm_start), you can set this value
+greater than the ring buffer size (in frames). For that simply using a large
+constant such as LONG_MAX or the boundary value is not a bad idea.
\par Stop threshold
Similarly, the stop threshold parameter is used to automatically stop
-the running stream, when the available samples crosses this boundary.
+the running stream, when the available frames crosses this boundary.
It means, for playback, the empty samples in ring buffer and for capture,
the filled (used) samples in ring buffer.
\par Silence threshold
-The silence threshold specifies count of samples filled with silence
-ahead of the current application pointer for playback. It is usable
-for applications when an overrun is possible (like tasks depending on
-network I/O etc.). If application wants to manage the ahead samples itself,
-the #snd_pcm_rewind() function allows to forget the last
-samples in the stream.
+The silence threshold specifies the count of frames before an underrun when the
+buffer gets filled with frames of silence according to the silence size parameter
+ahead of the current application pointer for playback. It is usable for applications
+when an underrun is possible (like tasks depending on network I/O etc.). If
+application wants to manage the ahead samples itself, the #snd_pcm_rewind() function
+allows to forget the last samples in the stream.
\section pcm_status Obtaining stream status
@@ -402,11 +403,11 @@ The stream status is stored in #snd_pcm_status_t structure.
These parameters can be obtained: the current stream state -
#snd_pcm_status_get_state(), timestamp of trigger -
#snd_pcm_status_get_trigger_tstamp(), timestamp of last
-pointer update #snd_pcm_status_get_tstamp(), delay in samples -
-#snd_pcm_status_get_delay(), available count in samples -
-#snd_pcm_status_get_avail(), maximum available samples -
+pointer update #snd_pcm_status_get_tstamp(), delay in frames -
+#snd_pcm_status_get_delay(), available count in frames -
+#snd_pcm_status_get_avail(), maximum available frames -
#snd_pcm_status_get_avail_max(), ADC over-range count in
-samples - #snd_pcm_status_get_overrange(). The last two
+frames - #snd_pcm_status_get_overrange(). The last two
parameters - avail_max and overrange are reset to zero after the status
call.
@@ -414,7 +415,7 @@ call.
The function #snd_pcm_avail_update() updates the current
-available count of samples for writing (playback) or filled samples for
+available count of frames for writing (playback) or filled frames for
reading (capture). This call is mandatory for updating actual r/w pointer.
Using standalone, it is a light method to obtain current stream position,
because it does not require the user <-> kernel context switch, but the value
@@ -427,10 +428,10 @@ The function #snd_pcm_avail() reads the current hardware pointer
in the ring buffer from hardware and calls #snd_pcm_avail_update() then.
-The function #snd_pcm_delay() returns the delay in samples.
-For playback, it means count of samples in the ring buffer before
-the next sample will be sent to DAC. For capture, it means count of samples
-in the ring buffer before the next sample will be captured from ADC. It works
+The function #snd_pcm_delay() returns the delay in frames.
+For playback, it means count of frames in the ring buffer before
+the next frames will be sent to DAC. For capture, it means count of frames
+in the ring buffer before the next frames will be captured from ADC. It works
only when the stream is in the running or draining (playback only) state.
Note that this function does not update the current r/w pointer for applications,
so the function #snd_pcm_avail_update() must be called afterwards
@@ -632,42 +633,53 @@ The null device is null plugin. This device has not any arguments.
The full featured examples with cross-links can be found in Examples section
(see top of page):
-\anchor example_test_pcm
\par Sine-wave generator
\par
-alsa-lib/test/pcm.c example shows various transfer methods for the playback direction.
+\link example_test_pcm alsa-lib/test/pcm.c \endlink
+example shows various transfer methods for the playback direction.
\par Minimalistic PCM playback code
\par
-alsa-lib/test/pcm_min.c example shows the minimal code to produce a sound.
+\link example_test_minimal alsa-lib/test/pcm_min.c \endlink
+example shows the minimal code to produce a sound.
\par Latency measuring tool
\par
-alsa-lib/test/latency.c example shows the measuring of minimal latency between capture and
+\link example_test_latency alsa-lib/test/latency.c \endlink
+example shows the measuring of minimal latency between capture and
playback devices.
*/
/**
\example ../../test/pcm.c
+\anchor example_test_pcm
+Shows various transfer methods for the playback direction.
*/
/**
\example ../../test/pcm_min.c
+\anchor example_test_minimal
+Shows the minimal code to produce a sound.
*/
/**
\example ../../test/latency.c
+\anchor example_test_latency
+Shows the measuring of minimal latency between capture and
+playback devices.
*/
+#include "pcm_local.h"
#include
#include
+#if HAVE_MALLOC_H
#include
+#endif
#include
#include
#include
#include
#include
#include
-#include "pcm_local.h"
#ifndef DOC_HIDDEN
/* return specific error codes for known bad PCM states */
@@ -883,6 +895,7 @@ int snd_pcm_info(snd_pcm_t *pcm, snd_pcm_info_t *info)
* \param pcm PCM handle
* \param params Configuration space definition container
* \return 0 on success otherwise a negative error code
+ * \retval -EBADFD no hardware configuration is set
*/
int snd_pcm_hw_params_current(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
{
@@ -949,6 +962,8 @@ int snd_pcm_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
/** \brief Remove PCM hardware configuration and free associated resources
* \param pcm PCM handle
* \return 0 on success otherwise a negative error code
+ *
+ * The function will also report success if no configuration is set.
*/
int snd_pcm_hw_free(snd_pcm_t *pcm)
{
@@ -1703,7 +1718,7 @@ int snd_pcm_link(snd_pcm_t *pcm1, snd_pcm_t *pcm2)
assert(pcm1);
assert(pcm2);
if (pcm1->fast_ops->link)
- err = pcm1->fast_ops->link(pcm1, pcm2);
+ err = pcm1->fast_ops->link(pcm1->fast_op_arg, pcm2);
else
err = -ENOSYS;
return err;
@@ -1720,7 +1735,7 @@ int snd_pcm_unlink(snd_pcm_t *pcm)
assert(pcm);
if (pcm->fast_ops->unlink)
- err = pcm->fast_ops->unlink(pcm);
+ err = pcm->fast_ops->unlink(pcm->fast_op_arg);
else
err = -ENOSYS;
return err;
@@ -1797,6 +1812,12 @@ static int __snd_pcm_poll_descriptors(snd_pcm_t *pcm, struct pollfd *pfds,
* corresponding FD_SET arrays and demangle events using
* \link ::snd_pcm_poll_descriptors_revents() \endlink .
*
+ * It is guaranteed that for the given PCM handle, the output poll
+ * descriptor structs (and their count) will not change after
+ * hardware and software parameters setup. Thus it is valid to call
+ * the function once when all parameters are set and reuse its output
+ * for the lifetime of the stream parameters.
+ *
* The function is thread-safe when built with the proper option.
*/
int snd_pcm_poll_descriptors(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space)
@@ -1832,6 +1853,13 @@ static int __snd_pcm_poll_revents(snd_pcm_t *pcm, struct pollfd *pfds,
* Note: Even if multiple poll descriptors are used (i.e. pfds > 1),
* this function returns only a single event.
*
+ * The passed in count of poll descriptors must be equal to
+ * \link ::snd_pcm_poll_descriptors_count() \endlink and the passed in array
+ * must match the array returned by \link ::snd_pcm_poll_descriptors() \endlink
+ * (in its full length and original order) with the revent fields updated
+ * according to the poll() result. This function will not modify the file
+ * descriptor or event field of any element of the given poll descriptor array.
+ *
* The function is thread-safe when built with the proper option.
*/
int snd_pcm_poll_descriptors_revents(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents)
@@ -2059,10 +2087,16 @@ static const char *const snd_pcm_type_names[] = {
static const char *const snd_pcm_subformat_names[] = {
SUBFORMAT(STD),
+ SUBFORMAT(MSBITS_MAX),
+ SUBFORMAT(MSBITS_20),
+ SUBFORMAT(MSBITS_24),
};
static const char *const snd_pcm_subformat_descriptions[] = {
SUBFORMATD(STD, "Standard"),
+ SUBFORMATD(MSBITS_MAX, "Maximum based on PCM format"),
+ SUBFORMATD(MSBITS_20, "20 most significant bits"),
+ SUBFORMATD(MSBITS_24, "24 most significant bits"),
};
static const char *const snd_pcm_start_mode_names[] = {
@@ -2186,6 +2220,30 @@ const char *snd_pcm_subformat_description(const snd_pcm_subformat_t subformat)
return snd_pcm_subformat_descriptions[subformat];
}
+/**
+ * \brief get PCM sample subformat from name
+ * \param name PCM sample subformat name (case insensitive)
+ * \return PCM sample subformat
+ */
+snd_pcm_subformat_t snd_pcm_subformat_value(const char* name)
+{
+ snd_pcm_subformat_t subformat;
+
+ for (subformat = 0; subformat <= SND_PCM_SUBFORMAT_LAST; subformat++) {
+ if (snd_pcm_subformat_names[subformat] &&
+ !strcasecmp(name, snd_pcm_subformat_names[subformat]))
+ return subformat;
+ }
+
+ for (subformat = 0; subformat <= SND_PCM_SUBFORMAT_LAST; subformat++) {
+ if (snd_pcm_subformat_descriptions[subformat] &&
+ !strcasecmp(name, snd_pcm_subformat_descriptions[subformat]))
+ return subformat;
+ }
+
+ return SND_PCM_SUBFORMAT_UNKNOWN;
+}
+
/**
* \brief (DEPRECATED) get name of PCM start mode setting
* \param mode PCM start mode
@@ -2232,7 +2290,7 @@ const char *snd_pcm_tstamp_mode_name(const snd_pcm_tstamp_t mode)
/**
* \brief get name of PCM tstamp type setting
- * \param mode PCM tstamp type
+ * \param type PCM tstamp type
* \return ascii name of PCM tstamp type setting
*/
const char *snd_pcm_tstamp_type_name(snd_pcm_tstamp_type_t type)
@@ -2832,7 +2890,8 @@ int snd_pcm_open_named_slave(snd_pcm_t **pcmp, const char *name,
* \brief Wait for a PCM to become ready
* \param pcm PCM handle
* \param timeout maximum time in milliseconds to wait,
- * a negative value means infinity
+ * a -1 value means infinity (SND_PCM_WAIT_INFINITE),
+ * see also SND_PCM_WAIT_IO and SND_PCM_WAIT_DRAIN
* \return a positive value on success otherwise a negative error code
* (-EPIPE for the xrun and -ESTRPIPE for the suspended status,
* others for general errors)
@@ -2867,6 +2926,37 @@ int __snd_pcm_wait_in_lock(snd_pcm_t *pcm, int timeout)
return snd_pcm_wait_nocheck(pcm, timeout);
}
+static int __snd_pcm_wait_io_timeout(snd_pcm_t *pcm)
+{
+ int timeout;
+
+ /* period size is the time boundary */
+ timeout = (pcm->period_size * 1000ULL) / pcm->rate;
+ /* should not happen */
+ if (timeout < 0)
+ timeout = 0;
+ /* add extra time of 200 milliseconds */
+ timeout += 200;
+ return timeout;
+}
+
+static int __snd_pcm_wait_drain_timeout(snd_pcm_t *pcm)
+{
+ int timeout;
+
+ /* for capture, there's no reason to wait, just one iteration */
+ if (snd_pcm_stream(pcm) == SND_PCM_STREAM_CAPTURE)
+ return 0;
+ /* result is in milliseconds */
+ timeout = (snd_pcm_mmap_playback_delay(pcm) * 1000LL) / pcm->rate;
+ /* should not happen */
+ if (timeout < 0)
+ timeout = 0;
+ /* add extra time of 200 milliseconds */
+ timeout += 200;
+ return timeout;
+}
+
/*
* like snd_pcm_wait() but doesn't check mmap_avail before calling poll()
*
@@ -2882,7 +2972,7 @@ int snd_pcm_wait_nocheck(snd_pcm_t *pcm, int timeout)
npfds = __snd_pcm_poll_descriptors_count(pcm);
if (npfds <= 0 || npfds >= 16) {
- SNDERR("Invalid poll_fds %d\n", npfds);
+ SNDERR("Invalid poll_fds %d", npfds);
return -EIO;
}
pfd = alloca(sizeof(*pfd) * npfds);
@@ -2890,15 +2980,21 @@ int snd_pcm_wait_nocheck(snd_pcm_t *pcm, int timeout)
if (err < 0)
return err;
if (err != npfds) {
- SNDMSG("invalid poll descriptors %d\n", err);
+ SNDMSG("invalid poll descriptors %d", err);
return -EIO;
}
+ if (timeout == SND_PCM_WAIT_IO)
+ timeout = __snd_pcm_wait_io_timeout(pcm);
+ else if (timeout == SND_PCM_WAIT_DRAIN)
+ timeout = __snd_pcm_wait_drain_timeout(pcm);
+ else if (timeout < -1)
+ SNDMSG("invalid snd_pcm_wait timeout argument %d", timeout);
do {
__snd_pcm_unlock(pcm->fast_op_arg);
err_poll = poll(pfd, npfds, timeout);
__snd_pcm_lock(pcm->fast_op_arg);
if (err_poll < 0) {
- if (errno == EINTR && !PCMINABORT(pcm))
+ if (errno == EINTR && !PCMINABORT(pcm) && !(pcm->mode & SND_PCM_EINTR))
continue;
return -errno;
}
@@ -3399,12 +3495,12 @@ int snd_pcm_areas_copy(const snd_pcm_channel_area_t *dst_areas, snd_pcm_uframes_
/**
* \brief Copy one or more areas
- * \param dst_areas destination areas specification (one for each channel)
+ * \param dst_channels destination areas specification (one for each channel)
* \param dst_offset offset in frames inside destination area
* \param dst_size size in frames of the destination buffer
- * \param src_areas source areas specification (one for each channel)
+ * \param src_channels source areas specification (one for each channel)
* \param src_offset offset in frames inside source area
- * \param dst_size size in frames of the source buffer
+ * \param src_size size in frames of the source buffer
* \param channels channels count
* \param frames frames to copy
* \param format PCM sample format
@@ -3705,6 +3801,29 @@ int snd_pcm_hw_params_can_disable_period_wakeup(const snd_pcm_hw_params_t *param
return !!(params->info & SNDRV_PCM_INFO_NO_PERIOD_WAKEUP);
}
+/**
+ * \brief Check if hardware is capable of perfect drain
+ * \param params Configuration space
+ * \retval 0 Hardware doesn't do perfect drain
+ * \retval 1 Hardware does perfect drain
+ *
+ * This function should only be called when the configuration space
+ * contains a single configuration. Call #snd_pcm_hw_params to choose
+ * a single configuration from the configuration space.
+ *
+ * Perfect drain means that the hardware does not use samples
+ * beyond the stream application pointer.
+ */
+int snd_pcm_hw_params_is_perfect_drain(const snd_pcm_hw_params_t *params)
+{
+ assert(params);
+ if (CHECK_SANITY(params->info == ~0U)) {
+ SNDMSG("invalid PCM info field");
+ return 0; /* FIXME: should be a negative error? */
+ }
+ return !!(params->info & SNDRV_PCM_INFO_PERFECT_DRAIN);
+}
+
/**
* \brief Check if hardware supports audio wallclock timestamps
* \param params Configuration space
@@ -3785,7 +3904,16 @@ int snd_pcm_hw_params_get_rate_numden(const snd_pcm_hw_params_t *params,
/**
* \brief Get sample resolution info from a configuration space
* \param params Configuration space
- * \return signification bits in sample otherwise a negative error code if the info is not available
+ * \return sample resolution (in bits) otherwise a negative error code if the info is not available
+ *
+ * For linear formats, this function returns sample resolution -
+ * used bits starting from the first usable significant bit defined by
+ * the format (e.g. bit 31 for S32_LE format or bit 23 for S24_LE format -
+ * starting from bit zero). Application may use full sample bit range defined
+ * by the format, but additional bits (outside this sample resolution) are
+ * stripped (not processed).
+ *
+ * For non-linear formats, this value may have a special meaning which may be defined in future.
*
* This function should only be called when the configuration space
* contains a single configuration. Call #snd_pcm_hw_params to choose
@@ -3827,6 +3955,11 @@ int snd_pcm_hw_params_get_fifo_size(const snd_pcm_hw_params_t *params)
*
* The configuration space will be filled with all possible ranges
* for the PCM device.
+ *
+ * Note that the configuration space may be constrained by the
+ * currently installed configuration on the PCM device. To remove
+ * any constrains, free the configuration with #snd_pcm_hw_free
+ * first.
*/
int snd_pcm_hw_params_any(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
{
@@ -4393,7 +4526,7 @@ EXPORT_SYMBOL int INTERNAL(snd_pcm_hw_params_get_subformat)(const snd_pcm_hw_par
int snd_pcm_hw_params_get_subformat(const snd_pcm_hw_params_t *params, snd_pcm_subformat_t *subformat)
#endif
{
- return snd_pcm_hw_param_get(params, SND_PCM_HW_PARAM_SUBFORMAT, subformat, NULL);
+ return snd_pcm_hw_param_get(params, SND_PCM_HW_PARAM_SUBFORMAT, (unsigned int *)subformat, NULL);
}
/**
@@ -4433,7 +4566,7 @@ EXPORT_SYMBOL int INTERNAL(snd_pcm_hw_params_set_subformat_first)(snd_pcm_t *pcm
int snd_pcm_hw_params_set_subformat_first(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_subformat_t *subformat)
#endif
{
- return snd_pcm_hw_param_set_first(pcm, params, SND_PCM_HW_PARAM_SUBFORMAT, subformat, NULL);
+ return snd_pcm_hw_param_set_first(pcm, params, SND_PCM_HW_PARAM_SUBFORMAT, (unsigned int *)subformat, NULL);
}
/**
@@ -4449,7 +4582,7 @@ EXPORT_SYMBOL int INTERNAL(snd_pcm_hw_params_set_subformat_last)(snd_pcm_t *pcm,
int snd_pcm_hw_params_set_subformat_last(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_subformat_t *subformat)
#endif
{
- return snd_pcm_hw_param_set_last(pcm, params, SND_PCM_HW_PARAM_SUBFORMAT, subformat, NULL);
+ return snd_pcm_hw_param_set_last(pcm, params, SND_PCM_HW_PARAM_SUBFORMAT, (unsigned int *)subformat, NULL);
}
/**
@@ -4933,6 +5066,43 @@ int snd_pcm_hw_params_get_period_wakeup(snd_pcm_t *pcm, snd_pcm_hw_params_t *par
return 0;
}
+/**
+ * \brief Restrict a configuration space to fill the end of playback stream with silence when drain() is invoked
+ * \param pcm PCM handle
+ * \param params Configuration space
+ * \param val 0 = disabled, 1 = enabled (default) fill the end of the playback stream with silence when drain() is invoked
+ * \return Zero on success, otherwise a negative error code.
+ *
+ * When disabled, the application should handle the end of stream gracefully
+ * (fill the silent samples to align to the period size plus some extra
+ * samples for hardware / driver without perfect drain). Note that the rewind
+ * may be used for this purpose or the sw_params silencing mechanism.
+ */
+int snd_pcm_hw_params_set_drain_silence(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val)
+{
+ assert(pcm && params);
+ if (val)
+ params->flags &= ~SND_PCM_HW_PARAMS_NO_DRAIN_SILENCE;
+ else
+ params->flags |= SND_PCM_HW_PARAMS_NO_DRAIN_SILENCE;
+ params->rmask = ~0;
+ return snd_pcm_hw_refine(pcm, params);
+}
+
+/**
+ * \brief Extract drain with the filling of silence samples from a configuration space
+ * \param pcm PCM handle
+ * \param params Configuration space
+ * \param val 0 = disabled, 1 = enabled
+ * \return 0 otherwise a negative error code
+ */
+int snd_pcm_hw_params_get_drain_silence(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val)
+{
+ assert(pcm && params && val);
+ *val = params->flags & SND_PCM_HW_PARAMS_NO_DRAIN_SILENCE ? 0 : 1;
+ return 0;
+}
+
/**
* \brief Extract period time from a configuration space
* \param params Configuration space
@@ -6165,6 +6335,25 @@ int snd_pcm_hw_params_get_min_align(const snd_pcm_hw_params_t *params, snd_pcm_u
return 0;
}
+#ifndef DOXYGEN
+void snd_pcm_sw_params_current_no_lock(snd_pcm_t *pcm, snd_pcm_sw_params_t *params)
+{
+ params->proto = SNDRV_PCM_VERSION;
+ params->tstamp_mode = pcm->tstamp_mode;
+ params->tstamp_type = pcm->tstamp_type;
+ params->period_step = pcm->period_step;
+ params->sleep_min = 0;
+ params->avail_min = pcm->avail_min;
+ sw_set_period_event(params, pcm->period_event);
+ params->xfer_align = 1;
+ params->start_threshold = pcm->start_threshold;
+ params->stop_threshold = pcm->stop_threshold;
+ params->silence_threshold = pcm->silence_threshold;
+ params->silence_size = pcm->silence_size;
+ params->boundary = pcm->boundary;
+}
+#endif
+
/**
* \brief Return current software configuration for a PCM
* \param pcm PCM handle
@@ -6181,19 +6370,7 @@ int snd_pcm_sw_params_current(snd_pcm_t *pcm, snd_pcm_sw_params_t *params)
return -EIO;
}
__snd_pcm_lock(pcm); /* forced lock due to pcm field changes */
- params->proto = SNDRV_PCM_VERSION;
- params->tstamp_mode = pcm->tstamp_mode;
- params->tstamp_type = pcm->tstamp_type;
- params->period_step = pcm->period_step;
- params->sleep_min = 0;
- params->avail_min = pcm->avail_min;
- sw_set_period_event(params, pcm->period_event);
- params->xfer_align = 1;
- params->start_threshold = pcm->start_threshold;
- params->stop_threshold = pcm->stop_threshold;
- params->silence_threshold = pcm->silence_threshold;
- params->silence_size = pcm->silence_size;
- params->boundary = pcm->boundary;
+ snd_pcm_sw_params_current_no_lock(pcm, params);
__snd_pcm_unlock(pcm);
return 0;
}
@@ -6292,7 +6469,7 @@ int snd_pcm_sw_params_set_start_mode(snd_pcm_t *pcm, snd_pcm_sw_params_t *params
params->start_threshold = pcm->boundary;
break;
default:
- SNDMSG("invalid start mode value %d\n", val);
+ SNDMSG("invalid start mode value %d", val);
return -EINVAL;
}
return 0;
@@ -6340,7 +6517,7 @@ int snd_pcm_sw_params_set_xrun_mode(snd_pcm_t *pcm, snd_pcm_sw_params_t *params,
params->stop_threshold = pcm->boundary;
break;
default:
- SNDMSG("invalid xrun mode value %d\n", val);
+ SNDMSG("invalid xrun mode value %d", val);
return -EINVAL;
}
return 0;
@@ -6728,6 +6905,10 @@ int snd_pcm_sw_params_get_silence_threshold(const snd_pcm_sw_params_t *params, s
* underrun is nearer than silence threshold (see
* #snd_pcm_sw_params_set_silence_threshold)
*
+ * When drain silence (see #snd_pcm_hw_params_get_drain_silence) is disabled,
+ * this will also apply for draining, i.e. silence is written also when the
+ * drain end is nearer than the silence threshold.
+ *
* The special case is when silence size value is equal or greater than
* boundary. The unused portion of the ring buffer (initial written samples
* are untouched) is filled with silence at start. Later, only just processed
@@ -6915,7 +7096,7 @@ void snd_pcm_status_get_driver_htstamp(const snd_pcm_status_t *obj, snd_htimesta
/**
* \brief Get audio_tstamp_report from a PCM status container
* \param obj pointer to #snd_pcm_status_t
- * \param ptr Pointer to returned report (valid fields are accuracy and type)
+ * \param audio_tstamp_report Pointer to returned report
*/
void snd_pcm_status_get_audio_htstamp_report(const snd_pcm_status_t *obj,
snd_pcm_audio_tstamp_report_t *audio_tstamp_report)
@@ -6929,7 +7110,7 @@ void snd_pcm_status_get_audio_htstamp_report(const snd_pcm_status_t *obj,
/**
* \brief set audio_tstamp_config from a PCM status container
* \param obj pointer to #snd_pcm_status_t
- * \param ptr Pointer to config (valid fields are type and report_analog_delay)
+ * \param audio_tstamp_config Pointer to config (valid fields are type_requested and report_delay)
*/
void snd_pcm_status_set_audio_htstamp_config(snd_pcm_status_t *obj,
snd_pcm_audio_tstamp_config_t *audio_tstamp_config)
@@ -7324,7 +7505,7 @@ int __snd_pcm_mmap_begin(snd_pcm_t *pcm, const snd_pcm_channel_area_t **areas,
_skip:
\endcode
*
- * Look to the \ref example_test_pcm "Sine-wave generator" example
+ * Look to the \link example_test_pcm Sine-wave generator \endlink example
* for more details about the generate_sine function.
*
* The function is thread-safe when built with the proper option.
@@ -7456,7 +7637,7 @@ snd_pcm_sframes_t snd_pcm_read_areas(snd_pcm_t *pcm, const snd_pcm_channel_area_
goto _end;
}
- err = __snd_pcm_wait_in_lock(pcm, -1);
+ err = __snd_pcm_wait_in_lock(pcm, SND_PCM_WAIT_IO);
if (err < 0)
break;
goto _again;
@@ -7525,7 +7706,7 @@ snd_pcm_sframes_t snd_pcm_write_areas(snd_pcm_t *pcm, const snd_pcm_channel_area
goto _end;
}
- err = snd_pcm_wait_nocheck(pcm, -1);
+ err = snd_pcm_wait_nocheck(pcm, SND_PCM_WAIT_IO);
if (err < 0)
break;
goto _again;
@@ -7554,7 +7735,8 @@ snd_pcm_sframes_t snd_pcm_write_areas(snd_pcm_t *pcm, const snd_pcm_channel_area
/* some plugins might automatically start the stream */
state = __snd_pcm_state(pcm);
if (state == SND_PCM_STATE_PREPARED &&
- hw_avail >= (snd_pcm_sframes_t) pcm->start_threshold) {
+ hw_avail >= 0 &&
+ (snd_pcm_uframes_t) hw_avail >= pcm->start_threshold) {
err = __snd_pcm_start(pcm);
if (err < 0)
goto _end;
diff --git a/src/pcm/pcm_adpcm.c b/src/pcm/pcm_adpcm.c
index ed065318f01e26f294635060708c2e6850fd5b56..efd4145170a7c3bbd103b63530248e994aac6bc0 100644
--- a/src/pcm/pcm_adpcm.c
+++ b/src/pcm/pcm_adpcm.c
@@ -56,11 +56,10 @@ IMA compatibility project proceedings, Vol 2, Issue 2, May 1992.
come across a good description of XA yet.
*/
-#include "bswap.h"
#include "pcm_local.h"
#include "pcm_plugin.h"
-
#include "plugin_ops.h"
+#include "bswap.h"
#ifndef PIC
/* entry for static linking */
diff --git a/src/pcm/pcm_alaw.c b/src/pcm/pcm_alaw.c
index 540ba25f9fffe62e515108c0dfe8de18ac7cfb9d..715b04c74f0f5110f6c76a6de7e6f48b3eb83dd0 100644
--- a/src/pcm/pcm_alaw.c
+++ b/src/pcm/pcm_alaw.c
@@ -26,8 +26,8 @@
*
*/
-#include "bswap.h"
#include "pcm_local.h"
+#include "bswap.h"
#include "pcm_plugin.h"
#include "plugin_ops.h"
diff --git a/src/pcm/pcm_copy.c b/src/pcm/pcm_copy.c
index 4c099acd577c8896945058c5c6a60b688f806884..1bf745d2bea9097d01fa3587c5815e6e374088d2 100644
--- a/src/pcm/pcm_copy.c
+++ b/src/pcm/pcm_copy.c
@@ -26,9 +26,9 @@
*
*/
-#include "bswap.h"
#include "pcm_local.h"
#include "pcm_plugin.h"
+#include "bswap.h"
#ifndef PIC
/* entry for static linking */
diff --git a/src/pcm/pcm_direct.c b/src/pcm/pcm_direct.c
index 90417b2f45d27bad8c00e35d2807b471ed249e58..e53e592381191ec6b924e771e9aa9712c279cf6b 100644
--- a/src/pcm/pcm_direct.c
+++ b/src/pcm/pcm_direct.c
@@ -19,6 +19,7 @@
*
*/
+#include "pcm_local.h"
#include
#include
#include
@@ -44,12 +45,16 @@
*
*/
+#if !defined(__OpenBSD__) && !defined(__DragonFly__) && !defined(__ANDROID__)
union semun {
int val; /* Value for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
+#if defined(__linux__)
struct seminfo *__buf; /* Buffer for IPC_INFO (Linux specific) */
+#endif
};
+#endif
/*
* FIXME:
@@ -560,8 +565,11 @@ int snd_pcm_direct_timer_stop(snd_pcm_direct_t *dmix)
return 0;
}
+#define RECOVERIES_FLAG_SUSPENDED (1U << 31)
+#define RECOVERIES_MASK ((1U << 31) - 1)
+
/*
- * Recover slave on XRUN.
+ * Recover slave on XRUN or SUSPENDED.
* Even if direct plugins disable xrun detection, there might be an xrun
* raised directly by some drivers.
* The first client recovers slave pcm.
@@ -569,6 +577,8 @@ int snd_pcm_direct_timer_stop(snd_pcm_direct_t *dmix)
*/
int snd_pcm_direct_slave_recover(snd_pcm_direct_t *direct)
{
+ unsigned int recoveries;
+ int state;
int ret;
int semerr;
@@ -579,7 +589,8 @@ int snd_pcm_direct_slave_recover(snd_pcm_direct_t *direct)
return semerr;
}
- if (snd_pcm_state(direct->spcm) != SND_PCM_STATE_XRUN) {
+ state = snd_pcm_state(direct->spcm);
+ if (state != SND_PCM_STATE_XRUN && state != SND_PCM_STATE_SUSPENDED) {
/* ignore... someone else already did recovery */
semerr = snd_pcm_direct_semaphore_up(direct,
DIRECT_IPC_SEM_CLIENT);
@@ -590,6 +601,24 @@ int snd_pcm_direct_slave_recover(snd_pcm_direct_t *direct)
return 0;
}
+ recoveries = direct->shmptr->s.recoveries;
+ recoveries = (recoveries + 1) & RECOVERIES_MASK;
+ if (state == SND_PCM_STATE_SUSPENDED)
+ recoveries |= RECOVERIES_FLAG_SUSPENDED;
+ direct->shmptr->s.recoveries = recoveries;
+
+ /* some buggy drivers require the device resumed before prepared;
+ * when a device has RESUME flag and is in SUSPENDED state, resume
+ * here but immediately drop to bring it to a sane active state.
+ */
+ if (state == SND_PCM_STATE_SUSPENDED &&
+ (direct->spcm->info & SND_PCM_INFO_RESUME)) {
+ snd_pcm_resume(direct->spcm);
+ snd_pcm_drop(direct->spcm);
+ snd_pcm_direct_timer_stop(direct);
+ snd_pcm_direct_clear_timer_queue(direct);
+ }
+
ret = snd_pcm_prepare(direct->spcm);
if (ret < 0) {
SNDERR("recover: unable to prepare slave");
@@ -621,7 +650,6 @@ int snd_pcm_direct_slave_recover(snd_pcm_direct_t *direct)
}
return ret;
}
- direct->shmptr->s.recoveries++;
semerr = snd_pcm_direct_semaphore_up(direct,
DIRECT_IPC_SEM_CLIENT);
if (semerr < 0) {
@@ -632,25 +660,49 @@ int snd_pcm_direct_slave_recover(snd_pcm_direct_t *direct)
}
/*
- * enter xrun state, if slave xrun occurred
- * @return: 0 - no xrun >0: xrun happened
+ * enter xrun or suspended state, if slave xrun occurred or suspended
+ * @return: 0 for no xrun/suspend or a negative error code for xrun/suspend
*/
-int snd_pcm_direct_client_chk_xrun(snd_pcm_direct_t *direct, snd_pcm_t *pcm)
+int snd_pcm_direct_check_xrun(snd_pcm_direct_t *direct, snd_pcm_t *pcm)
{
+ int err;
+
+ switch (snd_pcm_state(direct->spcm)) {
+ case SND_PCM_STATE_DISCONNECTED:
+ direct->state = SNDRV_PCM_STATE_DISCONNECTED;
+ return -ENODEV;
+ case SND_PCM_STATE_XRUN:
+ case SND_PCM_STATE_SUSPENDED:
+ if ((err = snd_pcm_direct_slave_recover(direct)) < 0)
+ return err;
+ break;
+ default:
+ break;
+ }
+
+ if (direct->state == SND_PCM_STATE_XRUN)
+ return -EPIPE;
+ else if (direct->state == SND_PCM_STATE_SUSPENDED)
+ return -ESTRPIPE;
if (direct->shmptr->s.recoveries != direct->recoveries) {
/* no matter how many xruns we missed -
* so don't increment but just update to actual counter
*/
direct->recoveries = direct->shmptr->s.recoveries;
- pcm->fast_ops->drop(pcm);
+ pcm->fast_ops->drop(pcm->fast_op_arg);
/* trigger_tstamp update is missing in drop callbacks */
gettimestamp(&direct->trigger_tstamp, pcm->tstamp_type);
/* no timer clear:
* if slave already entered xrun again the event is lost.
* snd_pcm_direct_clear_timer_queue(direct);
*/
- direct->state = SND_PCM_STATE_XRUN;
- return 1;
+ if (direct->recoveries & RECOVERIES_FLAG_SUSPENDED) {
+ direct->state = SND_PCM_STATE_SUSPENDED;
+ return -ESTRPIPE;
+ } else {
+ direct->state = SND_PCM_STATE_XRUN;
+ return -EPIPE;
+ }
}
return 0;
}
@@ -718,19 +770,11 @@ timer_changed:
}
empty = avail < pcm->avail_min;
}
- switch (snd_pcm_state(dmix->spcm)) {
- case SND_PCM_STATE_XRUN:
- /* recover slave and update client state to xrun
- * before returning POLLERR
- */
- snd_pcm_direct_slave_recover(dmix);
- snd_pcm_direct_client_chk_xrun(dmix, pcm);
- /* fallthrough */
- case SND_PCM_STATE_SUSPENDED:
- case SND_PCM_STATE_SETUP:
+
+ if (snd_pcm_direct_check_xrun(dmix, pcm) < 0 ||
+ snd_pcm_state(dmix->spcm) == SND_PCM_STATE_SETUP) {
events |= POLLERR;
- break;
- default:
+ } else {
if (empty) {
/* here we have a race condition:
* if period event arrived after the avail_update call
@@ -754,7 +798,6 @@ timer_changed:
break;
}
}
- break;
}
*revents = events;
return 0;
@@ -1028,7 +1071,34 @@ int snd_pcm_direct_munmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
snd_pcm_chmap_query_t **snd_pcm_direct_query_chmaps(snd_pcm_t *pcm)
{
snd_pcm_direct_t *dmix = pcm->private_data;
- return snd_pcm_query_chmaps(dmix->spcm);
+ snd_pcm_chmap_query_t **smaps, **maps;
+ unsigned int i, j;
+
+ if (dmix->bindings == NULL)
+ return snd_pcm_query_chmaps(dmix->spcm);
+
+ maps = calloc(2, sizeof(*maps));
+ if (!maps)
+ return NULL;
+ maps[0] = calloc(dmix->channels + 2, sizeof(int *));
+ if (!maps[0]) {
+ free(maps);
+ return NULL;
+ }
+ smaps = snd_pcm_query_chmaps(dmix->spcm);
+ if (smaps == NULL) {
+ snd_pcm_free_chmaps(maps);
+ return NULL;
+ }
+ maps[0]->type = SND_CHMAP_TYPE_FIXED;
+ maps[0]->map.channels = dmix->channels;
+ for (i = 0; i < dmix->channels; i++) {
+ j = dmix->bindings[i];
+ if (j == UINT_MAX || smaps[0]->map.channels < j)
+ continue;
+ maps[0]->map.pos[i] = smaps[0]->map.pos[j];
+ }
+ return maps;
}
snd_pcm_chmap_t *snd_pcm_direct_get_chmap(snd_pcm_t *pcm)
@@ -1073,27 +1143,10 @@ int snd_pcm_direct_prepare(snd_pcm_t *pcm)
int snd_pcm_direct_resume(snd_pcm_t *pcm)
{
snd_pcm_direct_t *dmix = pcm->private_data;
- snd_pcm_t *spcm = dmix->spcm;
+ int err;
- snd_pcm_direct_semaphore_down(dmix, DIRECT_IPC_SEM_CLIENT);
- /* some buggy drivers require the device resumed before prepared;
- * when a device has RESUME flag and is in SUSPENDED state, resume
- * here but immediately drop to bring it to a sane active state.
- */
- if ((spcm->info & SND_PCM_INFO_RESUME) &&
- snd_pcm_state(spcm) == SND_PCM_STATE_SUSPENDED) {
- snd_pcm_resume(spcm);
- snd_pcm_drop(spcm);
- snd_pcm_direct_timer_stop(dmix);
- snd_pcm_direct_clear_timer_queue(dmix);
- snd_pcm_areas_silence(snd_pcm_mmap_areas(spcm), 0,
- spcm->channels, spcm->buffer_size,
- spcm->format);
- snd_pcm_prepare(spcm);
- snd_pcm_start(spcm);
- }
- snd_pcm_direct_semaphore_up(dmix, DIRECT_IPC_SEM_CLIENT);
- return -ENOSYS;
+ err = snd_pcm_direct_slave_recover(dmix);
+ return err < 0 ? err : -ENOSYS;
}
#define COPY_SLAVE(field) (dmix->shmptr->s.field = spcm->field)
@@ -1689,7 +1742,7 @@ int snd_pcm_direct_parse_bindings(snd_pcm_direct_t *dmix,
continue;
err = safe_strtol(id, &cchannel);
if (err < 0 || cchannel < 0) {
- SNDERR("invalid client channel in binding: %s\n", id);
+ SNDERR("invalid client channel in binding: %s", id);
return -EINVAL;
}
if ((unsigned)cchannel >= count)
@@ -1714,7 +1767,7 @@ int snd_pcm_direct_parse_bindings(snd_pcm_direct_t *dmix,
continue;
safe_strtol(id, &cchannel);
if (snd_config_get_integer(n, &schannel) < 0) {
- SNDERR("unable to get slave channel (should be integer type) in binding: %s\n", id);
+ SNDERR("unable to get slave channel (should be integer type) in binding: %s", id);
free(bindings);
return -EINVAL;
}
@@ -1818,11 +1871,11 @@ static int _snd_pcm_direct_get_slave_ipc_offset(snd_config_t *root,
if (strcmp(id, "type") == 0) {
err = snd_config_get_string(n, &str);
if (err < 0) {
- SNDERR("Invalid value for PCM type definition\n");
+ SNDERR("Invalid value for PCM type definition");
return -EINVAL;
}
if (strcmp(str, "hw")) {
- SNDERR("Invalid type '%s' for slave PCM\n", str);
+ SNDERR("Invalid type '%s' for slave PCM", str);
return -EINVAL;
}
continue;
@@ -1936,7 +1989,7 @@ int snd_pcm_direct_parse_open_conf(snd_config_t *root, snd_config_t *conf,
SNDERR("Invalid type for %s", id);
return -EINVAL;
}
- if (strcmp(str, "no") == 0)
+ if (strcmp(str, "no") == 0 || strcmp(str, "off") == 0)
rec->hw_ptr_alignment = SND_PCM_HW_PTR_ALIGNMENT_NO;
else if (strcmp(str, "roundup") == 0)
rec->hw_ptr_alignment = SND_PCM_HW_PTR_ALIGNMENT_ROUNDUP;
@@ -2072,22 +2125,22 @@ int snd_pcm_direct_parse_open_conf(snd_config_t *root, snd_config_t *conf,
return 0;
}
-void snd_pcm_direct_reset_slave_ptr(snd_pcm_t *pcm, snd_pcm_direct_t *dmix)
+void snd_pcm_direct_reset_slave_ptr(snd_pcm_t *pcm, snd_pcm_direct_t *dmix,
+ snd_pcm_uframes_t hw_ptr)
{
-
+ dmix->slave_appl_ptr = dmix->slave_hw_ptr = hw_ptr;
if (dmix->hw_ptr_alignment == SND_PCM_HW_PTR_ALIGNMENT_ROUNDUP ||
- (dmix->hw_ptr_alignment == SND_PCM_HW_PTR_ALIGNMENT_AUTO &&
- pcm->buffer_size <= pcm->period_size * 2))
+ (dmix->hw_ptr_alignment == SND_PCM_HW_PTR_ALIGNMENT_AUTO &&
+ pcm->buffer_size <= pcm->period_size * 2))
dmix->slave_appl_ptr =
((dmix->slave_appl_ptr + dmix->slave_period_size - 1) /
- dmix->slave_period_size) * dmix->slave_period_size;
+ dmix->slave_period_size) * dmix->slave_period_size;
else if (dmix->hw_ptr_alignment == SND_PCM_HW_PTR_ALIGNMENT_ROUNDDOWN ||
- (dmix->hw_ptr_alignment == SND_PCM_HW_PTR_ALIGNMENT_AUTO &&
- (dmix->slave_period_size * SEC_TO_MS) /
- pcm->rate < LOW_LATENCY_PERIOD_TIME))
+ (dmix->hw_ptr_alignment == SND_PCM_HW_PTR_ALIGNMENT_AUTO &&
+ ((dmix->slave_period_size * SEC_TO_MS) / pcm->rate) < LOW_LATENCY_PERIOD_TIME))
dmix->slave_appl_ptr = dmix->slave_hw_ptr =
((dmix->slave_hw_ptr / dmix->slave_period_size) *
- dmix->slave_period_size);
+ dmix->slave_period_size);
}
int _snd_pcm_direct_new(snd_pcm_t **pcmp, snd_pcm_direct_t **_dmix, int type,
diff --git a/src/pcm/pcm_direct.h b/src/pcm/pcm_direct.h
index fb013a6666c2c697e6e3747600210e494d3cf56d..e7d89e5f462a61a7c2eefe69d2dea8b9b81c6e32 100644
--- a/src/pcm/pcm_direct.h
+++ b/src/pcm/pcm_direct.h
@@ -224,6 +224,8 @@ struct snd_pcm_direct {
snd1_pcm_direct_nonblock
#define snd_pcm_direct_async \
snd1_pcm_direct_async
+#define snd_pcm_direct_poll_descriptors \
+ snd1_pcm_direct_poll_descriptors
#define snd_pcm_direct_poll_revents \
snd1_pcm_direct_poll_revents
#define snd_pcm_direct_info \
@@ -264,6 +266,10 @@ struct snd_pcm_direct {
snd1_pcm_direct_set_chmap
#define snd_pcm_direct_reset_slave_ptr \
snd1_pcm_direct_reset_slave_ptr
+#define snd_pcm_direct_check_xrun \
+ snd1_pcm_direct_check_xrun
+#define snd_pcm_direct_slave_recover \
+ snd1_pcm_direct_slave_recover
int snd_pcm_direct_semaphore_create_or_connect(snd_pcm_direct_t *dmix);
@@ -345,10 +351,10 @@ snd_pcm_chmap_query_t **snd_pcm_direct_query_chmaps(snd_pcm_t *pcm);
snd_pcm_chmap_t *snd_pcm_direct_get_chmap(snd_pcm_t *pcm);
int snd_pcm_direct_set_chmap(snd_pcm_t *pcm, const snd_pcm_chmap_t *map);
int snd_pcm_direct_slave_recover(snd_pcm_direct_t *direct);
-int snd_pcm_direct_client_chk_xrun(snd_pcm_direct_t *direct, snd_pcm_t *pcm);
+int snd_pcm_direct_check_xrun(snd_pcm_direct_t *direct, snd_pcm_t *pcm);
int snd_timer_async(snd_timer_t *timer, int sig, pid_t pid);
struct timespec snd_pcm_hw_fast_tstamp(snd_pcm_t *pcm);
-void snd_pcm_direct_reset_slave_ptr(snd_pcm_t *pcm, snd_pcm_direct_t *dmix);
+void snd_pcm_direct_reset_slave_ptr(snd_pcm_t *pcm, snd_pcm_direct_t *dmix, snd_pcm_uframes_t hw_ptr);
struct snd_pcm_direct_open_conf {
key_t ipc_key;
diff --git a/src/pcm/pcm_dmix.c b/src/pcm/pcm_dmix.c
index 94dbb1e00621faf26f824397d5ae146fde69914a..55cae3e79a06f9c692e5e7f5c51a1cc03c6fb780 100644
--- a/src/pcm/pcm_dmix.c
+++ b/src/pcm/pcm_dmix.c
@@ -26,6 +26,7 @@
*
*/
+#include "pcm_local.h"
#include
#include
#include
@@ -424,25 +425,17 @@ static int snd_pcm_dmix_sync_ptr0(snd_pcm_t *pcm, snd_pcm_uframes_t slave_hw_ptr
static int snd_pcm_dmix_sync_ptr(snd_pcm_t *pcm)
{
snd_pcm_direct_t *dmix = pcm->private_data;
+ snd_pcm_uframes_t slave_hw_ptr;
int err;
- switch (snd_pcm_state(dmix->spcm)) {
- case SND_PCM_STATE_DISCONNECTED:
- dmix->state = SND_PCM_STATE_DISCONNECTED;
- return -ENODEV;
- case SND_PCM_STATE_XRUN:
- if ((err = snd_pcm_direct_slave_recover(dmix)) < 0)
- return err;
- break;
- default:
- break;
- }
- if (snd_pcm_direct_client_chk_xrun(dmix, pcm))
- return -EPIPE;
if (dmix->slowptr)
snd_pcm_hwsync(dmix->spcm);
+ slave_hw_ptr = *dmix->spcm->hw.ptr;
+ err = snd_pcm_direct_check_xrun(dmix, pcm);
+ if (err < 0)
+ return err;
- return snd_pcm_dmix_sync_ptr0(pcm, *dmix->spcm->hw.ptr);
+ return snd_pcm_dmix_sync_ptr0(pcm, slave_hw_ptr);
}
/*
@@ -452,22 +445,8 @@ static int snd_pcm_dmix_sync_ptr(snd_pcm_t *pcm)
static snd_pcm_state_t snd_pcm_dmix_state(snd_pcm_t *pcm)
{
snd_pcm_direct_t *dmix = pcm->private_data;
- int err;
- snd_pcm_state_t state;
- state = snd_pcm_state(dmix->spcm);
- switch (state) {
- case SND_PCM_STATE_SUSPENDED:
- case SND_PCM_STATE_DISCONNECTED:
- dmix->state = state;
- return state;
- case SND_PCM_STATE_XRUN:
- if ((err = snd_pcm_direct_slave_recover(dmix)) < 0)
- return err;
- break;
- default:
- break;
- }
- snd_pcm_direct_client_chk_xrun(dmix, pcm);
+
+ snd_pcm_direct_check_xrun(dmix, pcm);
if (dmix->state == STATE_RUN_PENDING)
return SNDRV_PCM_STATE_RUNNING;
return dmix->state;
@@ -553,8 +532,7 @@ static int snd_pcm_dmix_reset(snd_pcm_t *pcm)
snd_pcm_direct_t *dmix = pcm->private_data;
dmix->hw_ptr %= pcm->period_size;
dmix->appl_ptr = dmix->last_appl_ptr = dmix->hw_ptr;
- dmix->slave_appl_ptr = dmix->slave_hw_ptr = *dmix->spcm->hw.ptr;
- snd_pcm_direct_reset_slave_ptr(pcm, dmix);
+ snd_pcm_direct_reset_slave_ptr(pcm, dmix, *dmix->spcm->hw.ptr);
return 0;
}
@@ -563,8 +541,7 @@ static int snd_pcm_dmix_start_timer(snd_pcm_t *pcm, snd_pcm_direct_t *dmix)
int err;
snd_pcm_hwsync(dmix->spcm);
- dmix->slave_appl_ptr = dmix->slave_hw_ptr = *dmix->spcm->hw.ptr;
- snd_pcm_direct_reset_slave_ptr(pcm, dmix);
+ snd_pcm_direct_reset_slave_ptr(pcm, dmix, *dmix->spcm->hw.ptr);
err = snd_timer_start(dmix->timer);
if (err < 0)
return err;
@@ -647,7 +624,7 @@ static int __snd_pcm_dmix_drain(snd_pcm_t *pcm)
if (dmix->state == SND_PCM_STATE_DRAINING) {
snd_pcm_dmix_sync_area(pcm);
if ((pcm->mode & SND_PCM_NONBLOCK) == 0) {
- snd_pcm_wait_nocheck(pcm, -1);
+ snd_pcm_wait_nocheck(pcm, SND_PCM_WAIT_DRAIN);
snd_pcm_direct_clear_timer_queue(dmix); /* force poll to wait */
}
@@ -830,18 +807,9 @@ static snd_pcm_sframes_t snd_pcm_dmix_mmap_commit(snd_pcm_t *pcm,
snd_pcm_direct_t *dmix = pcm->private_data;
int err;
- switch (snd_pcm_state(dmix->spcm)) {
- case SND_PCM_STATE_XRUN:
- if ((err = snd_pcm_direct_slave_recover(dmix)) < 0)
- return err;
- break;
- case SND_PCM_STATE_SUSPENDED:
- return -ESTRPIPE;
- default:
- break;
- }
- if (snd_pcm_direct_client_chk_xrun(dmix, pcm))
- return -EPIPE;
+ err = snd_pcm_direct_check_xrun(dmix, pcm);
+ if (err < 0)
+ return err;
if (! size)
return 0;
snd_pcm_mmap_appl_forward(pcm, size);
@@ -1173,7 +1141,7 @@ pcm.name {
ipc_perm INT # IPC permissions (octal, default 0600)
hw_ptr_alignment STR # Slave application and hw pointer alignment type
# STR can be one of the below strings :
- # no
+ # no (or off)
# roundup
# rounddown
# auto (default)
diff --git a/src/pcm/pcm_dmix_generic.c b/src/pcm/pcm_dmix_generic.c
index 8a5b6f148556fa6414e8e2170624feba75cea92d..701c66c9479f3ac540656c3b02574bf3d15be0a2 100644
--- a/src/pcm/pcm_dmix_generic.c
+++ b/src/pcm/pcm_dmix_generic.c
@@ -330,7 +330,7 @@ static void generic_mix_areas_32_swap(unsigned int size,
register signed int sample;
for (;;) {
- sample = bswap_32(*src) >> 8;
+ sample = (signed int) bswap_32(*src) >> 8;
if (! *dst) {
*sum = sample;
*dst = *src;
@@ -364,7 +364,7 @@ static void generic_remix_areas_32_swap(unsigned int size,
register signed int sample;
for (;;) {
- sample = bswap_32(*src) >> 8;
+ sample = (signed int) bswap_32(*src) >> 8;
if (! *dst) {
*sum = -sample;
*dst = bswap_32(-sample);
diff --git a/src/pcm/pcm_dmix_i386.c b/src/pcm/pcm_dmix_i386.c
index 82a91c5c2897ca3eebd4c66c10806f9328884d94..ea55d8ec0bdf07cf1f52852727316a9a15e6bc9c 100644
--- a/src/pcm/pcm_dmix_i386.c
+++ b/src/pcm/pcm_dmix_i386.c
@@ -104,8 +104,7 @@ static void mix_select_callbacks(snd_pcm_direct_t *dmix)
/* try to determine the capabilities of the CPU */
in = fopen("/proc/cpuinfo", "r");
if (in) {
- while (!feof(in)) {
- fgets(line, sizeof(line), in);
+ while (!feof(in) && (fgets(line, sizeof(line), in) != NULL)) {
if (!strncmp(line, "processor", 9))
smp++;
else if (!strncmp(line, "flags", 5)) {
diff --git a/src/pcm/pcm_dmix_x86_64.c b/src/pcm/pcm_dmix_x86_64.c
index 4d882bfd01bfce336622d47d00e5a1d07469bf2f..1c80e1810f8425adedca221f9bac8f7a7ebb12c5 100644
--- a/src/pcm/pcm_dmix_x86_64.c
+++ b/src/pcm/pcm_dmix_x86_64.c
@@ -87,8 +87,7 @@ static void mix_select_callbacks(snd_pcm_direct_t *dmix)
/* try to determine, if we have SMP */
in = fopen("/proc/cpuinfo", "r");
if (in) {
- while (!feof(in)) {
- fgets(line, sizeof(line), in);
+ while (!feof(in) && (fgets(line, sizeof(line), in) != NULL)) {
if (!strncmp(line, "processor", 9))
smp++;
}
diff --git a/src/pcm/pcm_dshare.c b/src/pcm/pcm_dshare.c
index 01814dc8e70d69fea6b7f07e640edb8312cedb22..c03290985457c4b0036afb78643e16966ec33d60 100644
--- a/src/pcm/pcm_dshare.c
+++ b/src/pcm/pcm_dshare.c
@@ -26,6 +26,7 @@
*
*/
+#include "pcm_local.h"
#include
#include
#include
@@ -199,25 +200,17 @@ static int snd_pcm_dshare_sync_ptr0(snd_pcm_t *pcm, snd_pcm_uframes_t slave_hw_p
static int snd_pcm_dshare_sync_ptr(snd_pcm_t *pcm)
{
snd_pcm_direct_t *dshare = pcm->private_data;
+ snd_pcm_uframes_t slave_hw_ptr;
int err;
- switch (snd_pcm_state(dshare->spcm)) {
- case SND_PCM_STATE_DISCONNECTED:
- dshare->state = SNDRV_PCM_STATE_DISCONNECTED;
- return -ENODEV;
- case SND_PCM_STATE_XRUN:
- if ((err = snd_pcm_direct_slave_recover(dshare)) < 0)
- return err;
- break;
- default:
- break;
- }
- if (snd_pcm_direct_client_chk_xrun(dshare, pcm))
- return -EPIPE;
if (dshare->slowptr)
snd_pcm_hwsync(dshare->spcm);
+ slave_hw_ptr = *dshare->spcm->hw.ptr;
+ err = snd_pcm_direct_check_xrun(dshare, pcm);
+ if (err < 0)
+ return err;
- return snd_pcm_dshare_sync_ptr0(pcm, *dshare->spcm->hw.ptr);
+ return snd_pcm_dshare_sync_ptr0(pcm, slave_hw_ptr);
}
/*
@@ -237,7 +230,7 @@ static int snd_pcm_dshare_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
case SNDRV_PCM_STATE_DRAINING:
case SNDRV_PCM_STATE_RUNNING:
snd_pcm_dshare_sync_ptr0(pcm, status->hw_ptr);
- status->delay += snd_pcm_mmap_playback_delay(pcm);
+ status->delay = snd_pcm_mmap_playback_delay(pcm);
break;
default:
break;
@@ -255,22 +248,8 @@ static int snd_pcm_dshare_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
static snd_pcm_state_t snd_pcm_dshare_state(snd_pcm_t *pcm)
{
snd_pcm_direct_t *dshare = pcm->private_data;
- int err;
- snd_pcm_state_t state;
- state = snd_pcm_state(dshare->spcm);
- switch (state) {
- case SND_PCM_STATE_SUSPENDED:
- case SND_PCM_STATE_DISCONNECTED:
- dshare->state = state;
- return state;
- case SND_PCM_STATE_XRUN:
- if ((err = snd_pcm_direct_slave_recover(dshare)) < 0)
- return err;
- break;
- default:
- break;
- }
- snd_pcm_direct_client_chk_xrun(dshare, pcm);
+
+ snd_pcm_direct_check_xrun(dshare, pcm);
if (dshare->state == STATE_RUN_PENDING)
return SNDRV_PCM_STATE_RUNNING;
return dshare->state;
@@ -327,8 +306,7 @@ static int snd_pcm_dshare_reset(snd_pcm_t *pcm)
snd_pcm_direct_t *dshare = pcm->private_data;
dshare->hw_ptr %= pcm->period_size;
dshare->appl_ptr = dshare->last_appl_ptr = dshare->hw_ptr;
- dshare->slave_appl_ptr = dshare->slave_hw_ptr = *dshare->spcm->hw.ptr;
- snd_pcm_direct_reset_slave_ptr(pcm, dshare);
+ snd_pcm_direct_reset_slave_ptr(pcm, dshare, *dshare->spcm->hw.ptr);
return 0;
}
@@ -337,8 +315,7 @@ static int snd_pcm_dshare_start_timer(snd_pcm_t *pcm, snd_pcm_direct_t *dshare)
int err;
snd_pcm_hwsync(dshare->spcm);
- dshare->slave_appl_ptr = dshare->slave_hw_ptr = *dshare->spcm->hw.ptr;
- snd_pcm_direct_reset_slave_ptr(pcm, dshare);
+ snd_pcm_direct_reset_slave_ptr(pcm, dshare, *dshare->spcm->hw.ptr);
err = snd_timer_start(dshare->timer);
if (err < 0)
return err;
@@ -424,7 +401,7 @@ static int __snd_pcm_dshare_drain(snd_pcm_t *pcm)
}
if (dshare->state == SND_PCM_STATE_DRAINING) {
snd_pcm_dshare_sync_area(pcm);
- snd_pcm_wait_nocheck(pcm, -1);
+ snd_pcm_wait_nocheck(pcm, SND_PCM_WAIT_DRAIN);
snd_pcm_direct_clear_timer_queue(dshare); /* force poll to wait */
switch (snd_pcm_state(dshare->spcm)) {
@@ -529,18 +506,9 @@ static snd_pcm_sframes_t snd_pcm_dshare_mmap_commit(snd_pcm_t *pcm,
snd_pcm_direct_t *dshare = pcm->private_data;
int err;
- switch (snd_pcm_state(dshare->spcm)) {
- case SND_PCM_STATE_XRUN:
- if ((err = snd_pcm_direct_slave_recover(dshare)) < 0)
- return err;
- break;
- case SND_PCM_STATE_SUSPENDED:
- return -ESTRPIPE;
- default:
- break;
- }
- if (snd_pcm_direct_client_chk_xrun(dshare, pcm))
- return -EPIPE;
+ err = snd_pcm_direct_check_xrun(dshare, pcm);
+ if (err < 0)
+ return err;
if (! size)
return 0;
snd_pcm_mmap_appl_forward(pcm, size);
@@ -877,7 +845,7 @@ pcm.name {
ipc_perm INT # IPC permissions (octal, default 0600)
hw_ptr_alignment STR # Slave application and hw pointer alignment type
# STR can be one of the below strings :
- # no
+ # no (or off)
# roundup
# rounddown
# auto (default)
diff --git a/src/pcm/pcm_dsnoop.c b/src/pcm/pcm_dsnoop.c
index 3f28df9924a440a7e821037129a7d44448fea9d5..bf67c68a0dfa8e514b73a966543df35a89f6a674 100644
--- a/src/pcm/pcm_dsnoop.c
+++ b/src/pcm/pcm_dsnoop.c
@@ -26,6 +26,7 @@
*
*/
+#include "pcm_local.h"
#include
#include
#include
@@ -134,24 +135,14 @@ static int snd_pcm_dsnoop_sync_ptr(snd_pcm_t *pcm)
snd_pcm_sframes_t diff;
int err;
- switch (snd_pcm_state(dsnoop->spcm)) {
- case SND_PCM_STATE_DISCONNECTED:
- dsnoop->state = SNDRV_PCM_STATE_DISCONNECTED;
- return -ENODEV;
- case SND_PCM_STATE_XRUN:
- if ((err = snd_pcm_direct_slave_recover(dsnoop)) < 0)
- return err;
- break;
- default:
- break;
- }
- if (snd_pcm_direct_client_chk_xrun(dsnoop, pcm))
- return -EPIPE;
if (dsnoop->slowptr)
snd_pcm_hwsync(dsnoop->spcm);
old_slave_hw_ptr = dsnoop->slave_hw_ptr;
snoop_timestamp(pcm);
slave_hw_ptr = dsnoop->slave_hw_ptr;
+ err = snd_pcm_direct_check_xrun(dsnoop, pcm);
+ if (err < 0)
+ return err;
diff = pcm_frame_diff(slave_hw_ptr, old_slave_hw_ptr, dsnoop->slave_boundary);
if (diff == 0) /* fast path */
return 0;
@@ -206,22 +197,8 @@ static int snd_pcm_dsnoop_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
static snd_pcm_state_t snd_pcm_dsnoop_state(snd_pcm_t *pcm)
{
snd_pcm_direct_t *dsnoop = pcm->private_data;
- int err;
- snd_pcm_state_t state;
- state = snd_pcm_state(dsnoop->spcm);
- switch (state) {
- case SND_PCM_STATE_SUSPENDED:
- case SND_PCM_STATE_DISCONNECTED:
- dsnoop->state = state;
- return state;
- case SND_PCM_STATE_XRUN:
- if ((err = snd_pcm_direct_slave_recover(dsnoop)) < 0)
- return err;
- break;
- default:
- break;
- }
- snd_pcm_direct_client_chk_xrun(dsnoop, pcm);
+
+ snd_pcm_direct_check_xrun(dsnoop, pcm);
return dsnoop->state;
}
@@ -239,7 +216,7 @@ static int snd_pcm_dsnoop_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)
/* Fall through */
case SNDRV_PCM_STATE_PREPARED:
case SNDRV_PCM_STATE_SUSPENDED:
- *delayp = snd_pcm_mmap_capture_avail(pcm);
+ *delayp = snd_pcm_mmap_capture_delay(pcm);
return 0;
case SNDRV_PCM_STATE_XRUN:
return -EPIPE;
@@ -275,8 +252,7 @@ static int snd_pcm_dsnoop_reset(snd_pcm_t *pcm)
snd_pcm_direct_t *dsnoop = pcm->private_data;
dsnoop->hw_ptr %= pcm->period_size;
dsnoop->appl_ptr = dsnoop->hw_ptr;
- dsnoop->slave_appl_ptr = dsnoop->slave_hw_ptr;
- snd_pcm_direct_reset_slave_ptr(pcm, dsnoop);
+ snd_pcm_direct_reset_slave_ptr(pcm, dsnoop, dsnoop->slave_hw_ptr);
return 0;
}
@@ -289,8 +265,7 @@ static int snd_pcm_dsnoop_start(snd_pcm_t *pcm)
return -EBADFD;
snd_pcm_hwsync(dsnoop->spcm);
snoop_timestamp(pcm);
- dsnoop->slave_appl_ptr = dsnoop->slave_hw_ptr;
- snd_pcm_direct_reset_slave_ptr(pcm, dsnoop);
+ snd_pcm_direct_reset_slave_ptr(pcm, dsnoop, dsnoop->slave_hw_ptr);
err = snd_timer_start(dsnoop->timer);
if (err < 0)
return err;
@@ -327,7 +302,7 @@ static int __snd_pcm_dsnoop_drain(snd_pcm_t *pcm)
break;
if (pcm->mode & SND_PCM_NONBLOCK)
return -EAGAIN;
- __snd_pcm_wait_in_lock(pcm, -1);
+ __snd_pcm_wait_in_lock(pcm, SND_PCM_WAIT_DRAIN);
}
pcm->stop_threshold = stop_threshold;
return snd_pcm_dsnoop_drop(pcm);
@@ -420,18 +395,9 @@ static snd_pcm_sframes_t snd_pcm_dsnoop_mmap_commit(snd_pcm_t *pcm,
snd_pcm_direct_t *dsnoop = pcm->private_data;
int err;
- switch (snd_pcm_state(dsnoop->spcm)) {
- case SND_PCM_STATE_XRUN:
- if ((err = snd_pcm_direct_slave_recover(dsnoop)) < 0)
- return err;
- break;
- case SND_PCM_STATE_SUSPENDED:
- return -ESTRPIPE;
- default:
- break;
- }
- if (snd_pcm_direct_client_chk_xrun(dsnoop, pcm))
- return -EPIPE;
+ err = snd_pcm_direct_check_xrun(dsnoop, pcm);
+ if (err < 0)
+ return err;
if (dsnoop->state == SND_PCM_STATE_RUNNING) {
err = snd_pcm_dsnoop_sync_ptr(pcm);
if (err < 0)
@@ -733,7 +699,7 @@ pcm.name {
ipc_perm INT # IPC permissions (octal, default 0600)
hw_ptr_alignment STR # Slave application and hw pointer alignment type
# STR can be one of the below strings :
- # no
+ # no (or off)
# roundup
# rounddown
# auto (default)
diff --git a/src/pcm/pcm_extplug.c b/src/pcm/pcm_extplug.c
index 99455d9c8ede9189be23911578dba601f2a6edb2..feb32b99cd7217b7c760eca817242fefe8bf945e 100644
--- a/src/pcm/pcm_extplug.c
+++ b/src/pcm/pcm_extplug.c
@@ -650,8 +650,8 @@ parameter linked #snd_pcm_extplug_set_param_link() can be used for the
corresponding parameter. For example if the extplug does not support channel nor
format conversion the supported client parameters can be limited with
snd_pcm_extplug_set_param_*() and afterwards
-#snd_pcm_extplug_set_param_link(ext, SND_PCM_EXTPLUG_HW_FORMAT, 1) and
-#snd_pcm_extplug_set_param_link(ext, SND_PCM_EXTPLUG_HW_CHANNELS, 1) should be
+snd_pcm_extplug_set_param_link(ext, SND_PCM_EXTPLUG_HW_FORMAT, 1) and
+snd_pcm_extplug_set_param_link(ext, SND_PCM_EXTPLUG_HW_CHANNELS, 1) should be
called to keep the client and slave parameters the same.
*/
@@ -690,7 +690,7 @@ int snd_pcm_extplug_create(snd_pcm_extplug_t *extplug, const char *name,
/* We support 1.0.0 to current */
if (extplug->version < 0x010000 ||
extplug->version > SND_PCM_EXTPLUG_VERSION) {
- SNDERR("extplug: Plugin version mismatch: 0x%x\n",
+ SNDERR("extplug: Plugin version mismatch: 0x%x",
extplug->version);
return -ENXIO;
}
diff --git a/src/pcm/pcm_file.c b/src/pcm/pcm_file.c
index 7709a5549dbe18cb3b4f37e865ee997d2481e724..90b3f3f568ceca67d1faf99b80a4b25b556e49df 100644
--- a/src/pcm/pcm_file.c
+++ b/src/pcm/pcm_file.c
@@ -26,11 +26,11 @@
*
*/
+#include "pcm_local.h"
+#include "pcm_plugin.h"
#include "bswap.h"
#include
#include
-#include "pcm_local.h"
-#include "pcm_plugin.h"
#ifndef PIC
/* entry for static linking */
diff --git a/src/pcm/pcm_generic.c b/src/pcm/pcm_generic.c
index 4a10c6b40e7866e6f366705ca8e4ab914483913e..8177adc73568e4a748db64db6c38ebaa8b39171a 100644
--- a/src/pcm/pcm_generic.c
+++ b/src/pcm/pcm_generic.c
@@ -26,10 +26,10 @@
*
*/
-#include
-#include
#include "pcm_local.h"
#include "pcm_generic.h"
+#include
+#include
#ifndef DOC_HIDDEN
diff --git a/src/pcm/pcm_hw.c b/src/pcm/pcm_hw.c
index b3f9d1579d2978b51b4902872e95b09e8891d9c2..f3fbcb6e842276c14143776068d822e04ea4b423 100644
--- a/src/pcm/pcm_hw.c
+++ b/src/pcm/pcm_hw.c
@@ -27,6 +27,9 @@
*
*/
+#include "pcm_local.h"
+#include "../control/control_local.h"
+#include "../timer/timer_local.h"
#include
#include
#include
@@ -37,9 +40,6 @@
#include
#include
#include
-#include "pcm_local.h"
-#include "../control/control_local.h"
-#include "../timer/timer_local.h"
//#define DEBUG_RW /* use to debug readi/writei/readn/writen */
//#define DEBUG_MMAP /* debug mmap_commit */
@@ -98,14 +98,21 @@ typedef struct {
bool mmap_control_fallbacked;
struct snd_pcm_sync_ptr *sync_ptr;
+ bool prepare_reset_sw_params;
+ bool perfect_drain;
+
int period_event;
snd_timer_t *period_timer;
struct pollfd period_timer_pfd;
int period_timer_need_poll;
/* restricted parameters */
snd_pcm_format_t format;
- int rate;
+ struct {
+ int min;
+ int max;
+ } rates;
int channels;
+ int drain_silence;
/* for chmap */
unsigned int chmap_caps;
snd_pcm_chmap_query_t **chmap_override;
@@ -320,6 +327,9 @@ static int snd_pcm_hw_info(snd_pcm_t *pcm, snd_pcm_info_t * info)
SYSMSG("SNDRV_PCM_IOCTL_INFO failed (%i)", err);
return err;
}
+ /* may be configurable (optional) */
+ if (__snd_pcm_info_eld_fixup_check(info))
+ return __snd_pcm_info_eld_fixup(info);
return 0;
}
@@ -347,9 +357,9 @@ static int snd_pcm_hw_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
if (err < 0)
return err;
}
- if (hw->rate > 0) {
+ if (hw->rates.min > 0) {
err = _snd_pcm_hw_param_set_minmax(params, SND_PCM_HW_PARAM_RATE,
- hw->rate, 0, hw->rate + 1, -1);
+ hw->rates.min, 0, hw->rates.max + 1, -1);
if (err < 0)
return err;
}
@@ -369,12 +379,28 @@ static int snd_pcm_hw_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
return 0;
}
-static inline int hw_params_call(snd_pcm_hw_t *pcm_hw, snd_pcm_hw_params_t *params)
+#define hw_param_mask(params,var) \
+ &((params)->masks[(var) - SND_PCM_HW_PARAM_FIRST_MASK])
+
+static int hw_params_call(snd_pcm_hw_t *pcm_hw, snd_pcm_hw_params_t *params)
{
+ int err;
+
/* check for new hw_params structure; it's available from 2.0.2 version of PCM API */
if (SNDRV_PROTOCOL_VERSION(2, 0, 2) <= pcm_hw->version)
- return ioctl(pcm_hw->fd, SNDRV_PCM_IOCTL_HW_PARAMS, params);
- return use_old_hw_params_ioctl(pcm_hw->fd, SND_PCM_IOCTL_HW_PARAMS_OLD, params);
+ err = ioctl(pcm_hw->fd, SNDRV_PCM_IOCTL_HW_PARAMS, params);
+ else
+ err = use_old_hw_params_ioctl(pcm_hw->fd, SND_PCM_IOCTL_HW_PARAMS_OLD, params);
+ if (err >= 0 && pcm_hw->version < SNDRV_PROTOCOL_VERSION(2, 0, 17) && params->msbits > 0) {
+ snd_mask_t *m = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+ if (snd_mask_single(m)) {
+ snd_pcm_format_t format = snd_mask_min(m);
+ int width = snd_pcm_format_width(format);
+ if (width > 0 && params->msbits > (unsigned int)width)
+ params->msbits = width;
+ }
+ }
+ return err;
}
static int snd_pcm_hw_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
@@ -389,6 +415,8 @@ static int snd_pcm_hw_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
params->info &= ~0xf0000000;
if (pcm->tstamp_type != SND_PCM_TSTAMP_TYPE_GETTIMEOFDAY)
params->info |= SND_PCM_INFO_MONOTONIC;
+ hw->perfect_drain = !!(params->info & SND_PCM_INFO_PERFECT_DRAIN) ||
+ !!(params->flags & SND_PCM_HW_PARAMS_NO_DRAIN_SILENCE);
return query_status_data(hw);
}
@@ -528,13 +556,14 @@ static int snd_pcm_hw_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params)
SYSMSG("SNDRV_PCM_IOCTL_SW_PARAMS failed (%i)", err);
goto out;
}
+ hw->prepare_reset_sw_params = false;
if ((snd_pcm_tstamp_type_t) params->tstamp_type != pcm->tstamp_type) {
if (hw->version < SNDRV_PROTOCOL_VERSION(2, 0, 12)) {
int on = (snd_pcm_tstamp_type_t) params->tstamp_type ==
SND_PCM_TSTAMP_TYPE_MONOTONIC;
if (ioctl(fd, SNDRV_PCM_IOCTL_TSTAMP, &on) < 0) {
err = -errno;
- SNDMSG("TSTAMP failed\n");
+ SNDMSG("TSTAMP failed");
goto out;
}
}
@@ -654,7 +683,18 @@ static int snd_pcm_hw_hwsync(snd_pcm_t *pcm)
static int snd_pcm_hw_prepare(snd_pcm_t *pcm)
{
snd_pcm_hw_t *hw = pcm->private_data;
+ snd_pcm_sw_params_t sw_params;
int fd = hw->fd, err;
+
+ if (hw->prepare_reset_sw_params) {
+ snd_pcm_sw_params_current_no_lock(pcm, &sw_params);
+ if (ioctl(hw->fd, SNDRV_PCM_IOCTL_SW_PARAMS, &sw_params) < 0) {
+ err = -errno;
+ SYSMSG("SNDRV_PCM_IOCTL_SW_PARAMS failed (%i)", err);
+ return err;
+ }
+ hw->prepare_reset_sw_params = false;
+ }
if (ioctl(fd, SNDRV_PCM_IOCTL_PREPARE) < 0) {
err = -errno;
SYSMSG("SNDRV_PCM_IOCTL_PREPARE failed (%i)", err);
@@ -712,7 +752,52 @@ static int snd_pcm_hw_drop(snd_pcm_t *pcm)
static int snd_pcm_hw_drain(snd_pcm_t *pcm)
{
snd_pcm_hw_t *hw = pcm->private_data;
+ snd_pcm_sw_params_t sw_params;
+ snd_pcm_uframes_t silence_size;
int err;
+
+ if (pcm->stream != SND_PCM_STREAM_PLAYBACK)
+ goto __skip_silence;
+ /* stream probably in SETUP, prevent divide by zero */
+ if (pcm->period_size == 0)
+ goto __skip_silence;
+ if (hw->drain_silence == 0 || hw->perfect_drain)
+ goto __skip_silence;
+ snd_pcm_sw_params_current_no_lock(pcm, &sw_params);
+ if (hw->drain_silence > 0) {
+ silence_size = (pcm->rate * hw->drain_silence) / 1000;
+ goto __manual_silence;
+ }
+ /* compute end silence size, align to period size + extra time */
+ if ((pcm->boundary % pcm->period_size) == 0) {
+ silence_size = pcm->period_size - (*pcm->appl.ptr % pcm->period_size);
+ if (silence_size == pcm->period_size)
+ silence_size = 0;
+ } else {
+ /* it not not easy to compute the period crossing point
+ * in this case because the period is not aligned to the boundary
+ * - use the full range (one period) in this case
+ */
+ silence_size = pcm->period_size;
+ }
+ silence_size += pcm->rate / 10; /* 1/10th of second */
+__manual_silence:
+ if (sw_params.silence_size < silence_size) {
+ /* fill the silence soon as possible (in the bellow ioctl
+ * or the next period wake up)
+ */
+ sw_params.silence_threshold = pcm->buffer_size;
+ if (silence_size > pcm->buffer_size)
+ silence_size = pcm->buffer_size;
+ sw_params.silence_size = silence_size;
+ if (ioctl(hw->fd, SNDRV_PCM_IOCTL_SW_PARAMS, &sw_params) < 0) {
+ err = -errno;
+ SYSMSG("SNDRV_PCM_IOCTL_SW_PARAMS failed (%i)", err);
+ return err;
+ }
+ hw->prepare_reset_sw_params = true;
+ }
+__skip_silence:
if (ioctl(hw->fd, SNDRV_PCM_IOCTL_DRAIN) < 0) {
err = -errno;
SYSMSG("SNDRV_PCM_IOCTL_DRAIN failed (%i)", err);
@@ -832,7 +917,7 @@ static int snd_pcm_hw_link(snd_pcm_t *pcm1, snd_pcm_t *pcm2)
{
if (pcm2->type != SND_PCM_TYPE_HW) {
if (pcm2->fast_ops->link_slaves)
- return pcm2->fast_ops->link_slaves(pcm2, pcm1);
+ return pcm2->fast_ops->link_slaves(pcm2->fast_op_arg, pcm1);
return -ENOSYS;
}
return hw_link(pcm1, pcm2);
@@ -1087,7 +1172,7 @@ static int snd_pcm_hw_close(snd_pcm_t *pcm)
int err = 0;
if (close(hw->fd)) {
err = -errno;
- SYSMSG("close failed (%i)\n", err);
+ SYSMSG("close failed (%i)", err);
}
unmap_status_and_control_data(hw);
@@ -1210,7 +1295,7 @@ snd_pcm_query_chmaps_from_hw(int card, int dev, int subdev,
ret = snd_ctl_hw_open(&ctl, NULL, card, 0);
if (ret < 0) {
- SYSMSG("Cannot open the associated CTL\n");
+ SYSMSG("Cannot open the associated CTL");
return NULL;
}
@@ -1218,7 +1303,7 @@ snd_pcm_query_chmaps_from_hw(int card, int dev, int subdev,
ret = snd_ctl_elem_tlv_read(ctl, &id, tlv, sizeof(tlv));
snd_ctl_close(ctl);
if (ret < 0) {
- SYSMSG("Cannot read Channel Map TLV\n");
+ SYSMSG("Cannot read Channel Map TLV");
return NULL;
}
@@ -1232,7 +1317,7 @@ snd_pcm_query_chmaps_from_hw(int card, int dev, int subdev,
type = tlv[SNDRV_CTL_TLVO_TYPE];
if (type != SND_CTL_TLVT_CONTAINER) {
if (!is_chmap_type(type)) {
- SYSMSG("Invalid TLV type %d\n", type);
+ SYSMSG("Invalid TLV type %d", type);
return NULL;
}
start = tlv;
@@ -1245,7 +1330,7 @@ snd_pcm_query_chmaps_from_hw(int card, int dev, int subdev,
nums = 0;
for (p = start; size > 0; ) {
if (!is_chmap_type(p[0])) {
- SYSMSG("Invalid TLV type %d\n", p[0]);
+ SYSMSG("Invalid TLV type %d", p[0]);
return NULL;
}
nums++;
@@ -1336,7 +1421,7 @@ static snd_pcm_chmap_t *snd_pcm_hw_get_chmap(snd_pcm_t *pcm)
case SNDRV_PCM_STATE_SUSPENDED:
break;
default:
- SYSMSG("Invalid PCM state for chmap_get: %s\n",
+ SYSMSG("Invalid PCM state for chmap_get: %s",
snd_pcm_state_name(FAST_PCM_STATE(hw)));
return NULL;
}
@@ -1347,7 +1432,7 @@ static snd_pcm_chmap_t *snd_pcm_hw_get_chmap(snd_pcm_t *pcm)
ret = snd_ctl_hw_open(&ctl, NULL, hw->card, 0);
if (ret < 0) {
free(map);
- SYSMSG("Cannot open the associated CTL\n");
+ SYSMSG("Cannot open the associated CTL");
chmap_caps_set_error(hw, CHMAP_CTL_GET);
return NULL;
}
@@ -1357,7 +1442,7 @@ static snd_pcm_chmap_t *snd_pcm_hw_get_chmap(snd_pcm_t *pcm)
snd_ctl_close(ctl);
if (ret < 0) {
free(map);
- SYSMSG("Cannot read Channel Map ctl\n");
+ SYSMSG("Cannot read Channel Map ctl");
chmap_caps_set_error(hw, CHMAP_CTL_GET);
return NULL;
}
@@ -1383,17 +1468,17 @@ static int snd_pcm_hw_set_chmap(snd_pcm_t *pcm, const snd_pcm_chmap_t *map)
return -ENXIO;
if (map->channels > 128) {
- SYSMSG("Invalid number of channels %d\n", map->channels);
+ SYSMSG("Invalid number of channels %d", map->channels);
return -EINVAL;
}
if (FAST_PCM_STATE(hw) != SNDRV_PCM_STATE_PREPARED) {
- SYSMSG("Invalid PCM state for chmap_set: %s\n",
+ SYSMSG("Invalid PCM state for chmap_set: %s",
snd_pcm_state_name(FAST_PCM_STATE(hw)));
return -EBADFD;
}
ret = snd_ctl_hw_open(&ctl, NULL, hw->card, 0);
if (ret < 0) {
- SYSMSG("Cannot open the associated CTL\n");
+ SYSMSG("Cannot open the associated CTL");
chmap_caps_set_error(hw, CHMAP_CTL_SET);
return ret;
}
@@ -1411,7 +1496,7 @@ static int snd_pcm_hw_set_chmap(snd_pcm_t *pcm, const snd_pcm_chmap_t *map)
ret = -ENXIO;
}
if (ret < 0)
- SYSMSG("Cannot write Channel Map ctl\n");
+ SYSMSG("Cannot write Channel Map ctl");
return ret;
}
@@ -1575,7 +1660,7 @@ int snd_pcm_hw_open_fd(snd_pcm_t **pcmp, const char *name, int fd,
unsigned int user_ver = SNDRV_PCM_VERSION;
if (ioctl(fd, SNDRV_PCM_IOCTL_USER_PVERSION, &user_ver) < 0) {
ret = -errno;
- SNDMSG("USER_PVERSION failed\n");
+ SNDMSG("USER_PVERSION failed");
return ret;
}
}
@@ -1587,7 +1672,7 @@ int snd_pcm_hw_open_fd(snd_pcm_t **pcmp, const char *name, int fd,
int on = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC;
if (ioctl(fd, SNDRV_PCM_IOCTL_TTSTAMP, &on) < 0) {
ret = -errno;
- SNDMSG("TTSTAMP failed\n");
+ SNDMSG("TTSTAMP failed");
return ret;
}
tstamp_type = SND_PCM_TSTAMP_TYPE_MONOTONIC;
@@ -1598,7 +1683,7 @@ int snd_pcm_hw_open_fd(snd_pcm_t **pcmp, const char *name, int fd,
int on = 1;
if (ioctl(fd, SNDRV_PCM_IOCTL_TSTAMP, &on) < 0) {
ret = -errno;
- SNDMSG("TSTAMP failed\n");
+ SNDMSG("TSTAMP failed");
return ret;
}
}
@@ -1616,7 +1701,7 @@ int snd_pcm_hw_open_fd(snd_pcm_t **pcmp, const char *name, int fd,
hw->fd = fd;
/* no restriction */
hw->format = SND_PCM_FORMAT_UNKNOWN;
- hw->rate = 0;
+ hw->rates.min = hw->rates.max = 0;
hw->channels = 0;
ret = snd_pcm_new(&pcm, SND_PCM_TYPE_HW, name, info.stream, mode);
@@ -1763,7 +1848,9 @@ pcm.name {
[format STR] # Restrict only to the given format
[channels INT] # Restrict only to the given channels
[rate INT] # Restrict only to the given rate
+ or [rate [INT INT]] # Restrict only to the given rate range (min max)
[chmap MAP] # Override channel maps; MAP is a string array
+ [drain_silence INT] # Add silence in drain (-1 = auto /default/, 0 = off, > 0 milliseconds)
}
\endcode
@@ -1796,7 +1883,7 @@ int _snd_pcm_hw_open(snd_pcm_t **pcmp, const char *name,
long card = -1, device = 0, subdevice = -1;
const char *str;
int err, sync_ptr_ioctl = 0;
- int rate = 0, channels = 0;
+ int min_rate = 0, max_rate = 0, channels = 0, drain_silence = -1;
snd_pcm_format_t format = SND_PCM_FORMAT_UNKNOWN;
snd_config_t *n;
int nonblock = 1; /* non-block per default */
@@ -1854,13 +1941,58 @@ int _snd_pcm_hw_open(snd_pcm_t **pcmp, const char *name,
continue;
}
if (strcmp(id, "rate") == 0) {
+ long val;
+ if (snd_config_get_type(n) == SND_CONFIG_TYPE_COMPOUND &&
+ snd_config_is_array(n)) {
+ snd_config_t *m;
+ err = snd_config_search(n, "0", &m);
+ if (err < 0) {
+ SNDERR("array expected for rate compound");
+ goto fail;
+ }
+ err = snd_config_get_integer(m, &val);
+ if (err < 0) {
+ SNDERR("Invalid type for rate.0");
+ goto fail;
+ }
+ min_rate = max_rate = val;
+ err = snd_config_search(n, "1", &m);
+ if (err >= 0) {
+ err = snd_config_get_integer(m, &val);
+ if (err < 0) {
+ SNDERR("Invalid type for rate.0");
+ goto fail;
+ }
+ max_rate = val;
+ }
+ } else {
+ err = snd_config_get_integer(n, &val);
+ if (err < 0) {
+ SNDERR("Invalid type for %s", id);
+ goto fail;
+ }
+ min_rate = max_rate = val;
+ }
+ continue;
+ }
+ if (strcmp(id, "min_rate") == 0) {
+ long val;
+ err = snd_config_get_integer(n, &val);
+ if (err < 0) {
+ SNDERR("Invalid type for %s", id);
+ goto fail;
+ }
+ min_rate = val;
+ continue;
+ }
+ if (strcmp(id, "max_rate") == 0) {
long val;
err = snd_config_get_integer(n, &val);
if (err < 0) {
SNDERR("Invalid type for %s", id);
goto fail;
}
- rate = val;
+ max_rate = val;
continue;
}
if (strcmp(id, "format") == 0) {
@@ -1892,6 +2024,16 @@ int _snd_pcm_hw_open(snd_pcm_t **pcmp, const char *name,
}
continue;
}
+ if (strcmp(id, "drain_silence") == 0) {
+ long val;
+ err = snd_config_get_integer(n, &val);
+ if (err < 0) {
+ SNDERR("Invalid type for %s", id);
+ goto fail;
+ }
+ drain_silence = val;
+ continue;
+ }
SNDERR("Unknown field %s", id);
err = -EINVAL;
goto fail;
@@ -1901,6 +2043,11 @@ int _snd_pcm_hw_open(snd_pcm_t **pcmp, const char *name,
err = -EINVAL;
goto fail;
}
+ if ((min_rate < 0) || (max_rate < min_rate)) {
+ SNDERR("min_rate - max_rate configuration invalid");
+ err = -EINVAL;
+ goto fail;
+ }
err = snd_pcm_hw_open(pcmp, name, card, device, subdevice, stream,
mode | (nonblock ? SND_PCM_NONBLOCK : 0),
0, sync_ptr_ioctl);
@@ -1923,10 +2070,13 @@ int _snd_pcm_hw_open(snd_pcm_t **pcmp, const char *name,
hw->format = format;
if (channels > 0)
hw->channels = channels;
- if (rate > 0)
- hw->rate = rate;
+ if (min_rate > 0) {
+ hw->rates.min = min_rate;
+ hw->rates.max = max_rate;
+ }
if (chmap)
hw->chmap_override = chmap;
+ hw->drain_silence = drain_silence;
return 0;
diff --git a/src/pcm/pcm_iec958.c b/src/pcm/pcm_iec958.c
index a11a043924f6c7d72911c51cd1ddc702ec8d407b..7b8459fbe0701efd6dbd5564adc25ac648b08bf1 100644
--- a/src/pcm/pcm_iec958.c
+++ b/src/pcm/pcm_iec958.c
@@ -26,11 +26,10 @@
*
*/
-#include "bswap.h"
#include "pcm_local.h"
#include "pcm_plugin.h"
-
#include "plugin_ops.h"
+#include "bswap.h"
#ifndef PIC
/* entry for static linking */
diff --git a/src/pcm/pcm_ioplug.c b/src/pcm/pcm_ioplug.c
index 981843982caf8d66263c0241250db57ff7dca561..df2c7f81f0a2b377bf4c3800b7d163d1f13c07c8 100644
--- a/src/pcm/pcm_ioplug.c
+++ b/src/pcm/pcm_ioplug.c
@@ -522,7 +522,7 @@ static int ioplug_drain_via_poll(snd_pcm_t *pcm)
/* in non-blocking mode, let application to poll() by itself */
if (io->data->nonblock)
return -EAGAIN;
- if (snd_pcm_wait_nocheck(pcm, -1) < 0)
+ if (snd_pcm_wait_nocheck(pcm, SND_PCM_WAIT_DRAIN) < 0)
break;
}
@@ -1086,7 +1086,7 @@ int snd_pcm_ioplug_create(snd_pcm_ioplug_t *ioplug, const char *name,
/* We support 1.0.0 to current */
if (ioplug->version < 0x010000 ||
ioplug->version > SND_PCM_IOPLUG_VERSION) {
- SNDERR("ioplug: Plugin version mismatch: 0x%x\n",
+ SNDERR("ioplug: Plugin version mismatch: 0x%x",
ioplug->version);
return -ENXIO;
}
diff --git a/src/pcm/pcm_ladspa.c b/src/pcm/pcm_ladspa.c
index ad73347d2930bf878a68a6268e79896792a8fd28..25eac76fe4aee97f915843e5509f0830948997e1 100644
--- a/src/pcm/pcm_ladspa.c
+++ b/src/pcm/pcm_ladspa.c
@@ -32,11 +32,11 @@
* http://www.medianet.ag
*/
+#include "pcm_local.h"
+#include "pcm_plugin.h"
#include
#include
#include
-#include "pcm_local.h"
-#include "pcm_plugin.h"
#include "ladspa.h"
@@ -1147,7 +1147,7 @@ static int snd_pcm_ladspa_check_dir(snd_pcm_ladspa_plugin_t * const plugin,
const unsigned long ladspa_id)
{
DIR *dir;
- struct dirent * dirent;
+ struct dirent64 * dirent;
int len = strlen(path), err;
int need_slash;
char *filename;
@@ -1161,7 +1161,7 @@ static int snd_pcm_ladspa_check_dir(snd_pcm_ladspa_plugin_t * const plugin,
return -ENOENT;
while (1) {
- dirent = readdir(dir);
+ dirent = readdir64(dir);
if (!dirent) {
closedir(dir);
return 0;
@@ -1210,7 +1210,7 @@ static int snd_pcm_ladspa_look_for_plugin(snd_pcm_ladspa_plugin_t * const plugin
return err;
err = snd_pcm_ladspa_check_dir(plugin, fullpath, label, ladspa_id);
free(fullpath);
- if (err < 0)
+ if (err < 0 && err != -ENOENT)
return err;
if (err > 0)
return 0;
diff --git a/src/pcm/pcm_lfloat.c b/src/pcm/pcm_lfloat.c
index f32a82a7037b13be8806600185c5ff2847951cc6..d9aa136d10e7810610a878a281536a9d19a31d61 100644
--- a/src/pcm/pcm_lfloat.c
+++ b/src/pcm/pcm_lfloat.c
@@ -26,11 +26,10 @@
*
*/
-#include "bswap.h"
#include "pcm_local.h"
#include "pcm_plugin.h"
-
#include "plugin_ops.h"
+#include "bswap.h"
#ifndef DOC_HIDDEN
diff --git a/src/pcm/pcm_linear.c b/src/pcm/pcm_linear.c
index 33e7fc4578f3f0f5062f908ae4b67e98965f64ce..81edccaaccc7931199fb7c08b70d0c59ed682a8f 100644
--- a/src/pcm/pcm_linear.c
+++ b/src/pcm/pcm_linear.c
@@ -26,11 +26,10 @@
*
*/
-#include "bswap.h"
#include "pcm_local.h"
#include "pcm_plugin.h"
-
#include "plugin_ops.h"
+#include "bswap.h"
#ifndef PIC
/* entry for static linking */
diff --git a/src/pcm/pcm_local.h b/src/pcm/pcm_local.h
index 6f03365ccd09964ce0885c1658b26ff03b9c7e85..152c92c300e1eec324415009d5b61e86e2dcaa5e 100644
--- a/src/pcm/pcm_local.h
+++ b/src/pcm/pcm_local.h
@@ -20,6 +20,11 @@
*
*/
+#ifndef __PCM_LOCAL_H
+#define __PCM_LOCAL_H
+
+#include "config.h"
+
#include
#include
#include
@@ -46,12 +51,9 @@
#include "mask.h"
#define SND_PCM_HW_PARAM_ACCESS SNDRV_PCM_HW_PARAM_ACCESS
-#define SND_PCM_HW_PARAM_FIRST_MASK SNDRV_PCM_HW_PARAM_FIRST_MASK
#define SND_PCM_HW_PARAM_FORMAT SNDRV_PCM_HW_PARAM_FORMAT
#define SND_PCM_HW_PARAM_SUBFORMAT SNDRV_PCM_HW_PARAM_SUBFORMAT
-#define SND_PCM_HW_PARAM_LAST_MASK SNDRV_PCM_HW_PARAM_LAST_MASK
#define SND_PCM_HW_PARAM_SAMPLE_BITS SNDRV_PCM_HW_PARAM_SAMPLE_BITS
-#define SND_PCM_HW_PARAM_FIRST_INTERVAL SNDRV_PCM_HW_PARAM_FIRST_INTERVAL
#define SND_PCM_HW_PARAM_FRAME_BITS SNDRV_PCM_HW_PARAM_FRAME_BITS
#define SND_PCM_HW_PARAM_CHANNELS SNDRV_PCM_HW_PARAM_CHANNELS
#define SND_PCM_HW_PARAM_RATE SNDRV_PCM_HW_PARAM_RATE
@@ -63,7 +65,6 @@
#define SND_PCM_HW_PARAM_BUFFER_SIZE SNDRV_PCM_HW_PARAM_BUFFER_SIZE
#define SND_PCM_HW_PARAM_BUFFER_BYTES SNDRV_PCM_HW_PARAM_BUFFER_BYTES
#define SND_PCM_HW_PARAM_TICK_TIME SNDRV_PCM_HW_PARAM_TICK_TIME
-#define SND_PCM_HW_PARAM_LAST_INTERVAL SNDRV_PCM_HW_PARAM_LAST_INTERVAL
#define SND_PCM_HW_PARAM_LAST_MASK SNDRV_PCM_HW_PARAM_LAST_MASK
#define SND_PCM_HW_PARAM_FIRST_MASK SNDRV_PCM_HW_PARAM_FIRST_MASK
#define SND_PCM_HW_PARAM_LAST_INTERVAL SNDRV_PCM_HW_PARAM_LAST_INTERVAL
@@ -77,6 +78,8 @@
#define SND_PCM_INFO_DOUBLE SNDRV_PCM_INFO_DOUBLE
/** device transfers samples in batch */
#define SND_PCM_INFO_BATCH SNDRV_PCM_INFO_BATCH
+/** device does perfect drain (silencing not required) */
+#define SND_PCM_INFO_PERFECT_DRAIN SNDRV_PCM_INFO_PERFECT_DRAIN
/** device accepts interleaved samples */
#define SND_PCM_INFO_INTERLEAVED SNDRV_PCM_INFO_INTERLEAVED
/** device accepts non-interleaved samples */
@@ -103,6 +106,7 @@
#define SND_PCM_HW_PARAMS_NORESAMPLE SNDRV_PCM_HW_PARAMS_NORESAMPLE
#define SND_PCM_HW_PARAMS_EXPORT_BUFFER SNDRV_PCM_HW_PARAMS_EXPORT_BUFFER
#define SND_PCM_HW_PARAMS_NO_PERIOD_WAKEUP SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP
+#define SND_PCM_HW_PARAMS_NO_DRAIN_SILENCE SNDRV_PCM_HW_PARAMS_NO_DRAIN_SILENCE
#define SND_PCM_INFO_MONOTONIC 0x80000000
@@ -364,6 +368,8 @@ struct _snd_pcm {
snd1_pcm_hw_param_get_max
#define snd_pcm_hw_param_name \
snd1_pcm_hw_param_name
+#define snd_pcm_sw_params_current_no_lock \
+ snd1_pcm_sw_params_current_no_lock
int snd_pcm_new(snd_pcm_t **pcmp, snd_pcm_type_t type, const char *name,
snd_pcm_stream_t stream, int mode);
@@ -388,6 +394,8 @@ void snd_pcm_mmap_appl_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames);
void snd_pcm_mmap_hw_backward(snd_pcm_t *pcm, snd_pcm_uframes_t frames);
void snd_pcm_mmap_hw_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames);
+void snd_pcm_sw_params_current_no_lock(snd_pcm_t *pcm, snd_pcm_sw_params_t *params);
+
snd_pcm_sframes_t snd_pcm_mmap_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size);
snd_pcm_sframes_t snd_pcm_mmap_readi(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size);
snd_pcm_sframes_t snd_pcm_mmap_writen(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size);
@@ -1142,7 +1150,7 @@ static inline int snd_pcm_may_wait_for_avail_min(snd_pcm_t *pcm, snd_pcm_uframes
if (avail >= pcm->avail_min)
return 0;
if (pcm->fast_ops->may_wait_for_avail_min)
- return pcm->fast_ops->may_wait_for_avail_min(pcm, avail);
+ return pcm->fast_ops->may_wait_for_avail_min(pcm->fast_op_arg, avail);
return 1;
}
@@ -1218,3 +1226,5 @@ static inline void snd_pcm_unlock(snd_pcm_t *pcm)
#define snd_pcm_lock(pcm) do {} while (0)
#define snd_pcm_unlock(pcm) do {} while (0)
#endif /* THREAD_SAFE_API */
+
+#endif /* __PCM_LOCAL_H */
diff --git a/src/pcm/pcm_meter.c b/src/pcm/pcm_meter.c
index 2dcf8e45b4d00643aab05cf492874db7642ff8ba..68c369de97e62fca6abdd803fe08137c0bf2fb14 100644
--- a/src/pcm/pcm_meter.c
+++ b/src/pcm/pcm_meter.c
@@ -27,16 +27,18 @@
*/
+#include "pcm_local.h"
+#include "pcm_plugin.h"
#include "bswap.h"
#include
#include
#include
-#include "pcm_local.h"
-#include "pcm_plugin.h"
+#ifndef DOC_HIDDEN
#define atomic_read(ptr) __atomic_load_n(ptr, __ATOMIC_SEQ_CST )
#define atomic_add(ptr, n) __atomic_add_fetch(ptr, n, __ATOMIC_SEQ_CST)
#define atomic_dec(ptr) __atomic_sub_fetch(ptr, 1, __ATOMIC_SEQ_CST)
+#endif
#ifndef PIC
/* entry for static linking */
diff --git a/src/pcm/pcm_misc.c b/src/pcm/pcm_misc.c
index ce8d8189aee278031951ac910c9f39c388e31f70..3cff4326f94ed5e03fafb8c410bc13943e7e6700 100644
--- a/src/pcm/pcm_misc.c
+++ b/src/pcm/pcm_misc.c
@@ -19,12 +19,12 @@
*
*/
+#include "pcm_local.h"
+#include "bswap.h"
#include
#include
#include
#include
-#include "bswap.h"
-#include "pcm_local.h"
/**
@@ -203,9 +203,9 @@ int snd_pcm_format_cpu_endian(snd_pcm_format_t format)
}
/**
- * \brief Return nominal bits per a PCM sample
+ * \brief Return the bit-width of the format
* \param format Sample format
- * \return bits per sample, a negative error code if not applicable
+ * \return the bit-width of the format, or a negative error code if not applicable
*/
int snd_pcm_format_width(snd_pcm_format_t format)
{
@@ -270,9 +270,9 @@ int snd_pcm_format_width(snd_pcm_format_t format)
}
/**
- * \brief Return bits needed to store a PCM sample
+ * \brief Return the physical bit-width of the format (bits needed to store a PCM sample)
* \param format Sample format
- * \return bits per sample, a negative error code if not applicable
+ * \return the physical bit-width of the format, or a negative error code if not applicable
*/
int snd_pcm_format_physical_width(snd_pcm_format_t format)
{
diff --git a/src/pcm/pcm_mmap.c b/src/pcm/pcm_mmap.c
index 9cbaae05250ac17ff13b5d5f17e8e1e909c3e84d..0b62978e47eacefb41006583a5b1034603d36f71 100644
--- a/src/pcm/pcm_mmap.c
+++ b/src/pcm/pcm_mmap.c
@@ -18,16 +18,17 @@
*
*/
-#include "config.h"
+#include "pcm_local.h"
#include
+#if HAVE_MALLOC_H
#include
+#endif
#include
#include
#include
#ifdef HAVE_SYS_SHM_H
#include
#endif
-#include "pcm_local.h"
void snd_pcm_mmap_appl_backward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
{
diff --git a/src/pcm/pcm_mulaw.c b/src/pcm/pcm_mulaw.c
index 73b0c3bcdf354aeeb47548bc954f619f90a77ebe..177a61bb02d1508972444e76b775cd110f430aee 100644
--- a/src/pcm/pcm_mulaw.c
+++ b/src/pcm/pcm_mulaw.c
@@ -26,11 +26,10 @@
*
*/
-#include "bswap.h"
#include "pcm_local.h"
#include "pcm_plugin.h"
-
#include "plugin_ops.h"
+#include "bswap.h"
#ifndef PIC
/* entry for static linking */
diff --git a/src/pcm/pcm_multi.c b/src/pcm/pcm_multi.c
index 7fb212765a1e364e22e58a86366c70a63d5d2ce5..74e1e3f144564f9566e697cad5c6f39df854296c 100644
--- a/src/pcm/pcm_multi.c
+++ b/src/pcm/pcm_multi.c
@@ -26,13 +26,13 @@
*
*/
+#include "pcm_local.h"
+#include "pcm_generic.h"
#include
#include
#include
#include
#include
-#include "pcm_local.h"
-#include "pcm_generic.h"
#ifndef PIC
/* entry for static linking */
@@ -388,11 +388,21 @@ static int snd_pcm_multi_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t *params)
return 0;
}
+static snd_pcm_sframes_t snd_pcm_multi_avail_update(snd_pcm_t *pcm);
static int snd_pcm_multi_status(snd_pcm_t *pcm, snd_pcm_status_t *status)
{
snd_pcm_multi_t *multi = pcm->private_data;
snd_pcm_t *slave = multi->slaves[multi->master_slave].pcm;
- return snd_pcm_status(slave, status);
+
+ int err = snd_pcm_status(slave, status);
+ if (err < 0)
+ return err;
+ snd_pcm_sframes_t avail = snd_pcm_multi_avail_update(pcm);
+ if (avail < 0)
+ return avail;
+ status->hw_ptr = *pcm->hw.ptr;
+ status->avail = avail;
+ return 0;
}
static snd_pcm_state_t snd_pcm_multi_state(snd_pcm_t *pcm)
@@ -749,8 +759,9 @@ static int snd_pcm_multi_link_slaves(snd_pcm_t *pcm, snd_pcm_t *master)
static int snd_pcm_multi_link(snd_pcm_t *pcm1, snd_pcm_t *pcm2)
{
snd_pcm_multi_t *multi = pcm1->private_data;
- if (multi->slaves[0].pcm->fast_ops->link)
- return multi->slaves[0].pcm->fast_ops->link(multi->slaves[0].pcm, pcm2);
+ snd_pcm_t *main_pcm = multi->slaves[0].pcm;
+ if (main_pcm->fast_ops->link)
+ return main_pcm->fast_ops->link(main_pcm->fast_op_arg, pcm2);
return -ENOSYS;
}
@@ -1292,7 +1303,7 @@ int _snd_pcm_multi_open(snd_pcm_t **pcmp, const char *name,
++slaves_count;
}
if (master_slave < 0 || master_slave >= (long)slaves_count) {
- SNDERR("Master slave is out of range (0-%u)\n", slaves_count-1);
+ SNDERR("Master slave is out of range (0-%u)", slaves_count-1);
return -EINVAL;
}
snd_config_for_each(i, inext, bindings) {
diff --git a/src/pcm/pcm_null.c b/src/pcm/pcm_null.c
index c8ea9b38785f3c65d15501fb7403b273f7113050..f7b096bcd4b89a0c2e6cd44fa6b54e7bb8c593b7 100644
--- a/src/pcm/pcm_null.c
+++ b/src/pcm/pcm_null.c
@@ -26,10 +26,10 @@
*
*/
-#include "bswap.h"
-#include
#include "pcm_local.h"
#include "pcm_plugin.h"
+#include "bswap.h"
+#include
#ifndef PIC
/* entry for static linking */
diff --git a/src/pcm/pcm_params.c b/src/pcm/pcm_params.c
index ceb3b1aafb53489a147edb37ed86debc5418b79e..05bfe3b2a27afdacb1ba30dfe7153c195dae7705 100644
--- a/src/pcm/pcm_params.c
+++ b/src/pcm/pcm_params.c
@@ -2008,7 +2008,10 @@ static const snd_mask_t refine_masks[SND_PCM_HW_PARAM_LAST_MASK - SND_PCM_HW_PAR
},
[SND_PCM_HW_PARAM_SUBFORMAT - SND_PCM_HW_PARAM_FIRST_MASK] = {
.bits = {
- PCM_BIT(SNDRV_PCM_SUBFORMAT_STD)
+ PCM_BIT(SNDRV_PCM_SUBFORMAT_STD) |
+ PCM_BIT(SNDRV_PCM_SUBFORMAT_MSBITS_MAX) |
+ PCM_BIT(SNDRV_PCM_SUBFORMAT_MSBITS_20) |
+ PCM_BIT(SNDRV_PCM_SUBFORMAT_MSBITS_24),
},
},
};
@@ -2072,6 +2075,7 @@ int snd_pcm_hw_refine_soft(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t
{
unsigned int k;
snd_interval_t *i;
+ snd_mask_t *m;
unsigned int rstamps[RULES];
unsigned int vstamps[SND_PCM_HW_PARAM_LAST_INTERVAL + 1];
unsigned int stamp = 2;
@@ -2156,6 +2160,11 @@ int snd_pcm_hw_refine_soft(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t
i = hw_param_interval(params, SND_PCM_HW_PARAM_SAMPLE_BITS);
if (snd_interval_single(i))
params->msbits = snd_interval_value(i);
+ m = hw_param_mask_c(params, SNDRV_PCM_HW_PARAM_FORMAT);
+ if (snd_mask_single(m)) {
+ snd_pcm_format_t format = snd_mask_min(m);
+ params->msbits = snd_pcm_format_width(format);
+ }
}
if (!params->rate_den) {
@@ -2335,6 +2344,9 @@ static int snd_pcm_sw_params_default(snd_pcm_t *pcm, snd_pcm_sw_params_t *params
params->silence_threshold = 0;
params->silence_size = 0;
params->boundary = pcm->buffer_size;
+ /* this should not happen (bad child?) */
+ if (params->boundary == 0)
+ return -EINVAL;
while (params->boundary * 2 <= LONG_MAX - pcm->buffer_size)
params->boundary *= 2;
return 0;
@@ -2407,6 +2419,8 @@ int _snd_pcm_hw_params_internal(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
INTERNAL(snd_pcm_hw_params_get_subformat)(params, &pcm->subformat);
INTERNAL(snd_pcm_hw_params_get_channels)(params, &pcm->channels);
INTERNAL(snd_pcm_hw_params_get_rate)(params, &pcm->rate, 0);
+ snd_interval_copy(&pcm->periods, ¶ms->intervals[SND_PCM_HW_PARAM_PERIODS - SND_PCM_HW_PARAM_FIRST_INTERVAL]);
+ snd_interval_copy(&pcm->buffer_time, ¶ms->intervals[SND_PCM_HW_PARAM_BUFFER_TIME - SND_PCM_HW_PARAM_FIRST_INTERVAL]);
INTERNAL(snd_pcm_hw_params_get_period_time)(params, &pcm->period_time, 0);
INTERNAL(snd_pcm_hw_params_get_period_size)(params, &pcm->period_size, 0);
INTERNAL(snd_pcm_hw_params_get_buffer_size)(params, &pcm->buffer_size);
@@ -2429,7 +2443,9 @@ int _snd_pcm_hw_params_internal(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
/* Default sw params */
memset(&sw, 0, sizeof(sw));
- snd_pcm_sw_params_default(pcm, &sw);
+ err = snd_pcm_sw_params_default(pcm, &sw);
+ if (err < 0)
+ return err;
err = snd_pcm_sw_params(pcm, &sw);
if (err < 0)
return err;
diff --git a/src/pcm/pcm_plug.c b/src/pcm/pcm_plug.c
index abf6f1ab975119735c89a485fb92668766cb525c..bd681a9f3c497858795774d2315c33ebb43870ce 100644
--- a/src/pcm/pcm_plug.c
+++ b/src/pcm/pcm_plug.c
@@ -186,7 +186,8 @@ static const snd_pcm_format_t linear_preferred_formats[] = {
#if defined(BUILD_PCM_PLUGIN_MULAW) || \
defined(BUILD_PCM_PLUGIN_ALAW) || \
- defined(BUILD_PCM_PLUGIN_ADPCM)
+ defined(BUILD_PCM_PLUGIN_ADPCM) || \
+ defined(BUILD_PCM_PLUGIN_IEC958)
#define BUILD_PCM_NONLINEAR
#endif
@@ -201,6 +202,10 @@ static const snd_pcm_format_t nonlinear_preferred_formats[] = {
#ifdef BUILD_PCM_PLUGIN_ADPCM
SND_PCM_FORMAT_IMA_ADPCM,
#endif
+#ifdef BUILD_PCM_PLUGIN_IEC958
+ SND_PCM_FORMAT_IEC958_SUBFRAME_LE,
+ SND_PCM_FORMAT_IEC958_SUBFRAME_BE,
+#endif
};
#endif
@@ -490,6 +495,18 @@ static int snd_pcm_plug_change_channels(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm
}
#endif
+#ifdef BUILD_PCM_PLUGIN_IEC958
+static int iec958_open(snd_pcm_t **pcmp, const char *name,
+ snd_pcm_format_t sformat, snd_pcm_t *slave,
+ int close_slave)
+{
+ unsigned char preamble_vals[3] = {
+ 0x08, 0x02, 0x04 /* Z, X, Y */
+ };
+ return snd_pcm_iec958_open(pcmp, name, sformat, slave, close_slave, NULL, preamble_vals, 0);
+}
+#endif
+
static int snd_pcm_plug_change_format(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_plug_params_t *clt, snd_pcm_plug_params_t *slv)
{
snd_pcm_plug_t *plug = pcm->private_data;
@@ -526,6 +543,12 @@ static int snd_pcm_plug_change_format(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_p
case SND_PCM_FORMAT_IMA_ADPCM:
f = snd_pcm_adpcm_open;
break;
+#endif
+#ifdef BUILD_PCM_PLUGIN_IEC958
+ case SND_PCM_FORMAT_IEC958_SUBFRAME_LE:
+ case SND_PCM_FORMAT_IEC958_SUBFRAME_BE:
+ f = iec958_open;
+ break;
#endif
default:
#ifdef BUILD_PCM_PLUGIN_LFLOAT
@@ -566,6 +589,12 @@ static int snd_pcm_plug_change_format(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_p
case SND_PCM_FORMAT_IMA_ADPCM:
f = snd_pcm_adpcm_open;
break;
+#endif
+#ifdef BUILD_PCM_PLUGIN_IEC958
+ case SND_PCM_FORMAT_IEC958_SUBFRAME_LE:
+ case SND_PCM_FORMAT_IEC958_SUBFRAME_BE:
+ f = iec958_open;
+ break;
#endif
default:
return -EINVAL;
@@ -1065,6 +1094,18 @@ static int snd_pcm_plug_hw_free(snd_pcm_t *pcm)
return err;
}
+static int snd_pcm_plug_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params)
+{
+ snd_pcm_plug_t *plug = pcm->private_data;
+ snd_pcm_t *slave = plug->gen.slave;
+ int err = snd_pcm_sw_params(slave, params);
+ if (err >= 0) {
+ pcm->fast_ops = slave->fast_ops;
+ pcm->fast_op_arg = slave->fast_op_arg;
+ }
+ return err;
+}
+
static void snd_pcm_plug_dump(snd_pcm_t *pcm, snd_output_t *out)
{
snd_pcm_plug_t *plug = pcm->private_data;
@@ -1078,7 +1119,7 @@ static const snd_pcm_ops_t snd_pcm_plug_ops = {
.hw_refine = snd_pcm_plug_hw_refine,
.hw_params = snd_pcm_plug_hw_params,
.hw_free = snd_pcm_plug_hw_free,
- .sw_params = snd_pcm_generic_sw_params,
+ .sw_params = snd_pcm_plug_sw_params,
.channel_info = snd_pcm_generic_channel_info,
.dump = snd_pcm_plug_dump,
.nonblock = snd_pcm_generic_nonblock,
diff --git a/src/pcm/pcm_plugin.c b/src/pcm/pcm_plugin.c
index 24c9941dcbb8452d1b8e54ee84c135a8eff63a98..9d7e233e84379b6b01a3dae85293714fbcdd2949 100644
--- a/src/pcm/pcm_plugin.c
+++ b/src/pcm/pcm_plugin.c
@@ -82,9 +82,9 @@ pcm.rate44100Hz {
*/
-#include
#include "pcm_local.h"
#include "pcm_plugin.h"
+#include