alsa-lib/src/pcm/pcm_rate.c

1734 lines
49 KiB
C
Raw Normal View History

/**
* \file pcm/pcm_rate.c
* \ingroup PCM_Plugins
* \brief PCM Rate Plugin Interface
* \author Abramo Bagnara <abramo@alsa-project.org>
* \author Jaroslav Kysela <perex@perex.cz>
* \date 2000-2004
*/
2000-09-24 09:57:26 +00:00
/*
* PCM - Rate conversion
* Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
* 2004 by Jaroslav Kysela <perex@perex.cz>
2000-09-24 09:57:26 +00:00
*
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
2000-09-24 09:57:26 +00:00
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
2000-09-24 09:57:26 +00:00
*
* You should have received a copy of the GNU Lesser General Public
2000-09-24 09:57:26 +00:00
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
2000-09-24 09:57:26 +00:00
*
*/
#include <inttypes.h>
#include "bswap.h"
2000-09-24 09:57:26 +00:00
#include "pcm_local.h"
#include "pcm_plugin.h"
#include "pcm_rate.h"
2000-09-24 09:57:26 +00:00
#include "plugin_ops.h"
#if 0
#define DEBUG_REFINE
#endif
2001-10-24 14:14:11 +00:00
#ifndef PIC
/* entry for static linking */
const char *_snd_module_pcm_rate = "";
#endif
#ifndef DOC_HIDDEN
typedef struct _snd_pcm_rate snd_pcm_rate_t;
struct _snd_pcm_rate {
snd_pcm_generic_t gen;
snd_pcm_uframes_t appl_ptr, hw_ptr, last_slave_hw_ptr;
snd_pcm_uframes_t last_commit_ptr;
snd_pcm_uframes_t orig_avail_min;
snd_pcm_sw_params_t sw_params;
snd_pcm_format_t sformat;
2001-03-29 17:50:28 +00:00
unsigned int srate;
snd_pcm_channel_area_t *pareas; /* areas for splitted period (rate pcm) */
snd_pcm_channel_area_t *sareas; /* areas for splitted period (slave pcm) */
snd_pcm_rate_info_t info;
void *open_func;
void *obj;
snd_pcm_rate_ops_t ops;
pcm: rate: Improve the support multiple formats This patch extends the PCM rate plugin for allowing its converter plugin to deal with multiple formats. Currently, the converter plugin is allowed to take different formats only when convert callback is defined. And for this way (so far only the standard linear rate plugin does), all linear formats have to be handled, and it's cumbersome. OTOH, most other rate plugins are implemented with convert_s16 callback, which accepts only S16 format. This is often not ideal because many converter engines can handle 32bit formats. Also, the target format is often 32bit format, hence this would require additional conversion even if the converter engine can output 32bit natively. In this patch, for addressing the problems above, the rate plugin API is extended in the following way: - The new get_supported_formats callback is added; this stores the bit masks of the supported input and output formats, as well as the behavior flags. Currently only linear formats are allowed. - When the plugin accepts only the interleaved stream, set SND_PCM_RATE_FLAG_INTERLEAVED flag bit. Otherwise the code has to handle snd_pcm_channel_area at each call. - When both input and output formats have to be identical, pass SND_PCM_RATE_FLAG_SYNC_FORMATS flag bit. - When the converter wants to process different formats, use convert callback instead of convert_s16. You can put both in the ops for compatibility, too. The input and output formats are found in the info argument of init callback. - Now the PCM rate plugin core will skip the temporary buffer allocation and conversions for pre- and post-process if not needed (i.e. matching with the requested input or output format). The rate plugin API version is bumped to 0x010003. Signed-off-by: Takashi Iwai <tiwai@suse.de>
2021-06-17 10:03:17 +02:00
unsigned int src_conv_idx;
unsigned int dst_conv_idx;
snd_pcm_channel_area_t *src_buf;
snd_pcm_channel_area_t *dst_buf;
int start_pending; /* start is triggered but not commited to slave */
snd_htimestamp_t trigger_tstamp;
unsigned int plugin_version;
unsigned int rate_min, rate_max;
pcm: rate: Improve the support multiple formats This patch extends the PCM rate plugin for allowing its converter plugin to deal with multiple formats. Currently, the converter plugin is allowed to take different formats only when convert callback is defined. And for this way (so far only the standard linear rate plugin does), all linear formats have to be handled, and it's cumbersome. OTOH, most other rate plugins are implemented with convert_s16 callback, which accepts only S16 format. This is often not ideal because many converter engines can handle 32bit formats. Also, the target format is often 32bit format, hence this would require additional conversion even if the converter engine can output 32bit natively. In this patch, for addressing the problems above, the rate plugin API is extended in the following way: - The new get_supported_formats callback is added; this stores the bit masks of the supported input and output formats, as well as the behavior flags. Currently only linear formats are allowed. - When the plugin accepts only the interleaved stream, set SND_PCM_RATE_FLAG_INTERLEAVED flag bit. Otherwise the code has to handle snd_pcm_channel_area at each call. - When both input and output formats have to be identical, pass SND_PCM_RATE_FLAG_SYNC_FORMATS flag bit. - When the converter wants to process different formats, use convert callback instead of convert_s16. You can put both in the ops for compatibility, too. The input and output formats are found in the info argument of init callback. - Now the PCM rate plugin core will skip the temporary buffer allocation and conversions for pre- and post-process if not needed (i.e. matching with the requested input or output format). The rate plugin API version is bumped to 0x010003. Signed-off-by: Takashi Iwai <tiwai@suse.de>
2021-06-17 10:03:17 +02:00
snd_pcm_format_t orig_in_format;
snd_pcm_format_t orig_out_format;
uint64_t in_formats;
uint64_t out_formats;
unsigned int format_flags;
};
2000-09-24 09:57:26 +00:00
#define SND_PCM_RATE_PLUGIN_VERSION_OLD 0x010001 /* old rate plugin */
#endif /* DOC_HIDDEN */
/* allocate a channel area and a temporary buffer for the given size */
static snd_pcm_channel_area_t *
rate_alloc_tmp_buf(snd_pcm_format_t format,
unsigned int channels, unsigned int frames)
{
snd_pcm_channel_area_t *ap;
int width = snd_pcm_format_physical_width(format);
unsigned int i;
ap = malloc(sizeof(*ap) * channels);
if (!ap)
return NULL;
ap->addr = malloc(frames * channels * width / 8);
if (!ap->addr) {
free(ap);
return NULL;
}
/* set up in interleaved format */
for (i = 0; i < channels; i++) {
ap[i].addr = ap[0].addr + (i * width) / 8;
ap[i].first = 0;
ap[i].step = width * channels;
}
return ap;
}
static void rate_free_tmp_buf(snd_pcm_channel_area_t **ptr)
{
snd_pcm_channel_area_t *c = *ptr;
if (c) {
free(c->addr);
free(c);
*ptr = NULL;
}
}
static int snd_pcm_rate_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params)
2000-09-24 09:57:26 +00:00
{
snd_pcm_rate_t *rate = pcm->private_data;
2000-09-24 09:57:26 +00:00
int err;
2001-03-21 16:31:31 +00:00
snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHM };
snd_pcm_format_mask_t format_mask = { SND_PCM_FMTBIT_LINEAR };
err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
&access_mask);
2000-12-21 20:44:10 +00:00
if (err < 0)
return err;
err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_FORMAT,
&format_mask);
if (err < 0)
return err;
err = _snd_pcm_hw_params_set_subformat(params, SND_PCM_SUBFORMAT_STD);
2000-12-21 20:44:10 +00:00
if (err < 0)
return err;
if (rate->rate_min) {
err = _snd_pcm_hw_param_set_min(params, SND_PCM_HW_PARAM_RATE,
rate->rate_min, 0);
if (err < 0)
return err;
}
if (rate->rate_max) {
err = _snd_pcm_hw_param_set_max(params, SND_PCM_HW_PARAM_RATE,
rate->rate_max, 0);
if (err < 0)
return err;
}
params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
return 0;
}
static int snd_pcm_rate_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)
{
snd_pcm_rate_t *rate = pcm->private_data;
snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP };
_snd_pcm_hw_params_any(sparams);
_snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
&saccess_mask);
if (rate->sformat != SND_PCM_FORMAT_UNKNOWN) {
_snd_pcm_hw_params_set_format(sparams, rate->sformat);
_snd_pcm_hw_params_set_subformat(sparams, SND_PCM_SUBFORMAT_STD);
}
_snd_pcm_hw_param_set_minmax(sparams, SND_PCM_HW_PARAM_RATE,
rate->srate, 0, rate->srate + 1, -1);
return 0;
}
static int snd_pcm_rate_hw_refine_schange(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
snd_pcm_hw_params_t *sparams)
{
snd_pcm_rate_t *rate = pcm->private_data;
snd_interval_t t, buffer_size;
const snd_interval_t *srate, *crate;
int err;
unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS |
SND_PCM_HW_PARBIT_PERIOD_TIME |
SND_PCM_HW_PARBIT_TICK_TIME);
if (rate->sformat == SND_PCM_FORMAT_UNKNOWN)
2000-12-21 20:44:10 +00:00
links |= (SND_PCM_HW_PARBIT_FORMAT |
SND_PCM_HW_PARBIT_SUBFORMAT |
SND_PCM_HW_PARBIT_SAMPLE_BITS |
SND_PCM_HW_PARBIT_FRAME_BITS);
snd_interval_copy(&buffer_size, snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_BUFFER_SIZE));
snd_interval_unfloor(&buffer_size);
crate = snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_RATE);
srate = snd_pcm_hw_param_get_interval(sparams, SND_PCM_HW_PARAM_RATE);
snd_interval_muldiv(&buffer_size, srate, crate, &t);
err = _snd_pcm_hw_param_set_interval(sparams, SND_PCM_HW_PARAM_BUFFER_SIZE, &t);
if (err < 0)
return err;
err = _snd_pcm_hw_params_refine(sparams, links, params);
2000-09-24 09:57:26 +00:00
if (err < 0)
return err;
return 0;
}
static int snd_pcm_rate_hw_refine_cchange(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
snd_pcm_hw_params_t *sparams)
2000-09-24 09:57:26 +00:00
{
snd_pcm_rate_t *rate = pcm->private_data;
snd_interval_t t;
#ifdef DEBUG_REFINE
snd_output_t *out;
#endif
const snd_interval_t *sbuffer_size, *buffer_size;
const snd_interval_t *srate, *crate;
2000-12-21 20:44:10 +00:00
int err;
unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS |
SND_PCM_HW_PARBIT_PERIOD_TIME |
SND_PCM_HW_PARBIT_TICK_TIME);
if (rate->sformat == SND_PCM_FORMAT_UNKNOWN)
2000-12-21 20:44:10 +00:00
links |= (SND_PCM_HW_PARBIT_FORMAT |
SND_PCM_HW_PARBIT_SUBFORMAT |
SND_PCM_HW_PARBIT_SAMPLE_BITS |
SND_PCM_HW_PARBIT_FRAME_BITS);
sbuffer_size = snd_pcm_hw_param_get_interval(sparams, SND_PCM_HW_PARAM_BUFFER_SIZE);
crate = snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_RATE);
srate = snd_pcm_hw_param_get_interval(sparams, SND_PCM_HW_PARAM_RATE);
snd_interval_muldiv(sbuffer_size, crate, srate, &t);
snd_interval_floor(&t);
err = _snd_pcm_hw_param_set_interval(params, SND_PCM_HW_PARAM_BUFFER_SIZE, &t);
if (err < 0)
return err;
buffer_size = snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_BUFFER_SIZE);
/*
* this condition probably needs more work:
* in case when the buffer_size is known and we are looking
* for best period_size, we should prefer situation when
* (buffer_size / period_size) * period_size == buffer_size
*/
if (snd_interval_single(buffer_size) && buffer_size->integer) {
snd_interval_t *period_size;
period_size = (snd_interval_t *)snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_PERIOD_SIZE);
if (!snd_interval_checkempty(period_size) &&
period_size->openmin && period_size->openmax &&
period_size->min + 1 == period_size->max) {
if (period_size->min > 0 && (buffer_size->min / period_size->min) * period_size->min == buffer_size->min) {
snd_interval_set_value(period_size, period_size->min);
} else if ((buffer_size->max / period_size->max) * period_size->max == buffer_size->max) {
snd_interval_set_value(period_size, period_size->max);
}
}
}
#ifdef DEBUG_REFINE
snd_output_stdio_attach(&out, stderr, 0);
snd_output_printf(out, "REFINE (params):\n");
snd_pcm_hw_params_dump(params, out);
snd_output_printf(out, "REFINE (slave params):\n");
snd_pcm_hw_params_dump(sparams, out);
snd_output_close(out);
#endif
err = _snd_pcm_hw_params_refine(params, links, sparams);
#ifdef DEBUG_REFINE
snd_output_stdio_attach(&out, stderr, 0);
snd_output_printf(out, "********************\n");
snd_output_printf(out, "REFINE (params) (%i):\n", err);
snd_pcm_hw_params_dump(params, out);
snd_output_printf(out, "REFINE (slave params):\n");
snd_pcm_hw_params_dump(sparams, out);
snd_output_close(out);
#endif
if (err < 0)
return err;
return 0;
}
static int snd_pcm_rate_hw_refine(snd_pcm_t *pcm,
snd_pcm_hw_params_t *params)
{
return snd_pcm_hw_refine_slave(pcm, params,
snd_pcm_rate_hw_refine_cprepare,
snd_pcm_rate_hw_refine_cchange,
snd_pcm_rate_hw_refine_sprepare,
snd_pcm_rate_hw_refine_schange,
snd_pcm_generic_hw_refine);
}
pcm: rate: Improve the support multiple formats This patch extends the PCM rate plugin for allowing its converter plugin to deal with multiple formats. Currently, the converter plugin is allowed to take different formats only when convert callback is defined. And for this way (so far only the standard linear rate plugin does), all linear formats have to be handled, and it's cumbersome. OTOH, most other rate plugins are implemented with convert_s16 callback, which accepts only S16 format. This is often not ideal because many converter engines can handle 32bit formats. Also, the target format is often 32bit format, hence this would require additional conversion even if the converter engine can output 32bit natively. In this patch, for addressing the problems above, the rate plugin API is extended in the following way: - The new get_supported_formats callback is added; this stores the bit masks of the supported input and output formats, as well as the behavior flags. Currently only linear formats are allowed. - When the plugin accepts only the interleaved stream, set SND_PCM_RATE_FLAG_INTERLEAVED flag bit. Otherwise the code has to handle snd_pcm_channel_area at each call. - When both input and output formats have to be identical, pass SND_PCM_RATE_FLAG_SYNC_FORMATS flag bit. - When the converter wants to process different formats, use convert callback instead of convert_s16. You can put both in the ops for compatibility, too. The input and output formats are found in the info argument of init callback. - Now the PCM rate plugin core will skip the temporary buffer allocation and conversions for pre- and post-process if not needed (i.e. matching with the requested input or output format). The rate plugin API version is bumped to 0x010003. Signed-off-by: Takashi Iwai <tiwai@suse.de>
2021-06-17 10:03:17 +02:00
/* evaluate the best matching available format to the given format */
static int get_best_format(uint64_t mask, snd_pcm_format_t orig)
{
int pwidth = snd_pcm_format_physical_width(orig);
int width = snd_pcm_format_width(orig);
int signd = snd_pcm_format_signed(orig);
int best_score = -1;
int match = -1;
int f, score;
for (f = 0; f <= SND_PCM_FORMAT_LAST; f++) {
if (!(mask & (1ULL << f)))
continue;
score = 0;
if (snd_pcm_format_linear(f)) {
if (snd_pcm_format_physical_width(f) == pwidth)
score++;
if (snd_pcm_format_physical_width(f) >= pwidth)
score++;
if (snd_pcm_format_width(f) == width)
score++;
if (snd_pcm_format_signed(f) == signd)
score++;
}
if (score > best_score) {
match = f;
best_score = score;
}
}
return match;
}
/* set up the input and output formats from the available lists */
static int choose_preferred_format(snd_pcm_rate_t *rate)
{
uint64_t in_mask = rate->in_formats;
uint64_t out_mask = rate->out_formats;
int in, out;
if (!in_mask || !out_mask)
return 0;
if (rate->orig_in_format == rate->orig_out_format)
if (in_mask & out_mask & (1ULL << rate->orig_in_format))
return 0; /* nothing changed */
repeat:
in = get_best_format(in_mask, rate->orig_in_format);
out = get_best_format(out_mask, rate->orig_out_format);
if (in < 0 || out < 0)
return -ENOENT;
if ((rate->format_flags & SND_PCM_RATE_FLAG_SYNC_FORMATS) &&
in != out) {
if (out_mask & (1ULL << in))
out = in;
else if (in_mask & (1ULL << out))
in = out;
else {
in_mask &= ~(1ULL << in);
out_mask &= ~(1ULL << out);
goto repeat;
}
}
rate->info.in.format = in;
rate->info.out.format = out;
return 0;
}
static int snd_pcm_rate_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
{
snd_pcm_rate_t *rate = pcm->private_data;
snd_pcm_t *slave = rate->gen.slave;
snd_pcm_rate_side_info_t *sinfo, *cinfo;
unsigned int channels, acc;
pcm: rate: Improve the support multiple formats This patch extends the PCM rate plugin for allowing its converter plugin to deal with multiple formats. Currently, the converter plugin is allowed to take different formats only when convert callback is defined. And for this way (so far only the standard linear rate plugin does), all linear formats have to be handled, and it's cumbersome. OTOH, most other rate plugins are implemented with convert_s16 callback, which accepts only S16 format. This is often not ideal because many converter engines can handle 32bit formats. Also, the target format is often 32bit format, hence this would require additional conversion even if the converter engine can output 32bit natively. In this patch, for addressing the problems above, the rate plugin API is extended in the following way: - The new get_supported_formats callback is added; this stores the bit masks of the supported input and output formats, as well as the behavior flags. Currently only linear formats are allowed. - When the plugin accepts only the interleaved stream, set SND_PCM_RATE_FLAG_INTERLEAVED flag bit. Otherwise the code has to handle snd_pcm_channel_area at each call. - When both input and output formats have to be identical, pass SND_PCM_RATE_FLAG_SYNC_FORMATS flag bit. - When the converter wants to process different formats, use convert callback instead of convert_s16. You can put both in the ops for compatibility, too. The input and output formats are found in the info argument of init callback. - Now the PCM rate plugin core will skip the temporary buffer allocation and conversions for pre- and post-process if not needed (i.e. matching with the requested input or output format). The rate plugin API version is bumped to 0x010003. Signed-off-by: Takashi Iwai <tiwai@suse.de>
2021-06-17 10:03:17 +02:00
int need_src_buf, need_dst_buf;
int err = snd_pcm_hw_params_slave(pcm, params,
snd_pcm_rate_hw_refine_cchange,
snd_pcm_rate_hw_refine_sprepare,
snd_pcm_rate_hw_refine_schange,
snd_pcm_generic_hw_params);
2000-09-24 09:57:26 +00:00
if (err < 0)
return err;
2000-09-24 09:57:26 +00:00
if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
cinfo = &rate->info.in;
sinfo = &rate->info.out;
2000-09-24 09:57:26 +00:00
} else {
sinfo = &rate->info.in;
cinfo = &rate->info.out;
2000-09-24 09:57:26 +00:00
}
err = INTERNAL(snd_pcm_hw_params_get_format)(params, &cinfo->format);
if (err < 0)
return err;
err = INTERNAL(snd_pcm_hw_params_get_rate)(params, &cinfo->rate, 0);
2004-02-04 18:29:41 +00:00
if (err < 0)
return err;
err = INTERNAL(snd_pcm_hw_params_get_period_size)(params, &cinfo->period_size, 0);
2004-02-04 18:29:41 +00:00
if (err < 0)
return err;
err = INTERNAL(snd_pcm_hw_params_get_buffer_size)(params, &cinfo->buffer_size);
if (err < 0)
return err;
err = INTERNAL(snd_pcm_hw_params_get_channels)(params, &channels);
pcm: rate: Improve the support multiple formats This patch extends the PCM rate plugin for allowing its converter plugin to deal with multiple formats. Currently, the converter plugin is allowed to take different formats only when convert callback is defined. And for this way (so far only the standard linear rate plugin does), all linear formats have to be handled, and it's cumbersome. OTOH, most other rate plugins are implemented with convert_s16 callback, which accepts only S16 format. This is often not ideal because many converter engines can handle 32bit formats. Also, the target format is often 32bit format, hence this would require additional conversion even if the converter engine can output 32bit natively. In this patch, for addressing the problems above, the rate plugin API is extended in the following way: - The new get_supported_formats callback is added; this stores the bit masks of the supported input and output formats, as well as the behavior flags. Currently only linear formats are allowed. - When the plugin accepts only the interleaved stream, set SND_PCM_RATE_FLAG_INTERLEAVED flag bit. Otherwise the code has to handle snd_pcm_channel_area at each call. - When both input and output formats have to be identical, pass SND_PCM_RATE_FLAG_SYNC_FORMATS flag bit. - When the converter wants to process different formats, use convert callback instead of convert_s16. You can put both in the ops for compatibility, too. The input and output formats are found in the info argument of init callback. - Now the PCM rate plugin core will skip the temporary buffer allocation and conversions for pre- and post-process if not needed (i.e. matching with the requested input or output format). The rate plugin API version is bumped to 0x010003. Signed-off-by: Takashi Iwai <tiwai@suse.de>
2021-06-17 10:03:17 +02:00
if (err < 0)
return err;
err = INTERNAL(snd_pcm_hw_params_get_access)(params, &acc);
if (err < 0)
return err;
rate->info.channels = channels;
sinfo->format = slave->format;
sinfo->rate = slave->rate;
sinfo->buffer_size = slave->buffer_size;
sinfo->period_size = slave->period_size;
if (CHECK_SANITY(rate->pareas)) {
SNDMSG("rate plugin already in use");
return -EBUSY;
}
rate->pareas = rate_alloc_tmp_buf(cinfo->format, channels,
cinfo->period_size);
rate->sareas = rate_alloc_tmp_buf(sinfo->format, channels,
sinfo->period_size);
pcm: rate: Improve the support multiple formats This patch extends the PCM rate plugin for allowing its converter plugin to deal with multiple formats. Currently, the converter plugin is allowed to take different formats only when convert callback is defined. And for this way (so far only the standard linear rate plugin does), all linear formats have to be handled, and it's cumbersome. OTOH, most other rate plugins are implemented with convert_s16 callback, which accepts only S16 format. This is often not ideal because many converter engines can handle 32bit formats. Also, the target format is often 32bit format, hence this would require additional conversion even if the converter engine can output 32bit natively. In this patch, for addressing the problems above, the rate plugin API is extended in the following way: - The new get_supported_formats callback is added; this stores the bit masks of the supported input and output formats, as well as the behavior flags. Currently only linear formats are allowed. - When the plugin accepts only the interleaved stream, set SND_PCM_RATE_FLAG_INTERLEAVED flag bit. Otherwise the code has to handle snd_pcm_channel_area at each call. - When both input and output formats have to be identical, pass SND_PCM_RATE_FLAG_SYNC_FORMATS flag bit. - When the converter wants to process different formats, use convert callback instead of convert_s16. You can put both in the ops for compatibility, too. The input and output formats are found in the info argument of init callback. - Now the PCM rate plugin core will skip the temporary buffer allocation and conversions for pre- and post-process if not needed (i.e. matching with the requested input or output format). The rate plugin API version is bumped to 0x010003. Signed-off-by: Takashi Iwai <tiwai@suse.de>
2021-06-17 10:03:17 +02:00
if (!rate->pareas || !rate->sareas) {
err = -ENOMEM;
goto error_pareas;
}
rate->orig_in_format = rate->info.in.format;
rate->orig_out_format = rate->info.out.format;
if (choose_preferred_format(rate) < 0) {
SNDERR("No matching format in rate plugin");
err = -EINVAL;
goto error_pareas;
}
err = rate->ops.init(rate->obj, &rate->info);
if (err < 0)
goto error_init;
rate_free_tmp_buf(&rate->src_buf);
rate_free_tmp_buf(&rate->dst_buf);
need_src_buf = need_dst_buf = 0;
if ((rate->format_flags & SND_PCM_RATE_FLAG_INTERLEAVED) &&
!(acc == SND_PCM_ACCESS_MMAP_INTERLEAVED ||
acc == SND_PCM_ACCESS_RW_INTERLEAVED)) {
need_src_buf = need_dst_buf = 1;
} else {
if (rate->orig_in_format != rate->info.in.format)
need_src_buf = 1;
if (rate->orig_out_format != rate->info.out.format)
need_dst_buf = 1;
}
if (need_src_buf) {
rate->src_conv_idx =
snd_pcm_linear_convert_index(rate->orig_in_format,
rate->info.in.format);
rate->src_buf = rate_alloc_tmp_buf(rate->info.in.format,
pcm: rate: Improve the support multiple formats This patch extends the PCM rate plugin for allowing its converter plugin to deal with multiple formats. Currently, the converter plugin is allowed to take different formats only when convert callback is defined. And for this way (so far only the standard linear rate plugin does), all linear formats have to be handled, and it's cumbersome. OTOH, most other rate plugins are implemented with convert_s16 callback, which accepts only S16 format. This is often not ideal because many converter engines can handle 32bit formats. Also, the target format is often 32bit format, hence this would require additional conversion even if the converter engine can output 32bit natively. In this patch, for addressing the problems above, the rate plugin API is extended in the following way: - The new get_supported_formats callback is added; this stores the bit masks of the supported input and output formats, as well as the behavior flags. Currently only linear formats are allowed. - When the plugin accepts only the interleaved stream, set SND_PCM_RATE_FLAG_INTERLEAVED flag bit. Otherwise the code has to handle snd_pcm_channel_area at each call. - When both input and output formats have to be identical, pass SND_PCM_RATE_FLAG_SYNC_FORMATS flag bit. - When the converter wants to process different formats, use convert callback instead of convert_s16. You can put both in the ops for compatibility, too. The input and output formats are found in the info argument of init callback. - Now the PCM rate plugin core will skip the temporary buffer allocation and conversions for pre- and post-process if not needed (i.e. matching with the requested input or output format). The rate plugin API version is bumped to 0x010003. Signed-off-by: Takashi Iwai <tiwai@suse.de>
2021-06-17 10:03:17 +02:00
channels, rate->info.in.period_size);
if (!rate->src_buf) {
err = -ENOMEM;
goto error;
}
}
if (need_dst_buf) {
rate->dst_conv_idx =
snd_pcm_linear_convert_index(rate->info.out.format,
rate->orig_out_format);
rate->dst_buf = rate_alloc_tmp_buf(rate->info.out.format,
pcm: rate: Improve the support multiple formats This patch extends the PCM rate plugin for allowing its converter plugin to deal with multiple formats. Currently, the converter plugin is allowed to take different formats only when convert callback is defined. And for this way (so far only the standard linear rate plugin does), all linear formats have to be handled, and it's cumbersome. OTOH, most other rate plugins are implemented with convert_s16 callback, which accepts only S16 format. This is often not ideal because many converter engines can handle 32bit formats. Also, the target format is often 32bit format, hence this would require additional conversion even if the converter engine can output 32bit natively. In this patch, for addressing the problems above, the rate plugin API is extended in the following way: - The new get_supported_formats callback is added; this stores the bit masks of the supported input and output formats, as well as the behavior flags. Currently only linear formats are allowed. - When the plugin accepts only the interleaved stream, set SND_PCM_RATE_FLAG_INTERLEAVED flag bit. Otherwise the code has to handle snd_pcm_channel_area at each call. - When both input and output formats have to be identical, pass SND_PCM_RATE_FLAG_SYNC_FORMATS flag bit. - When the converter wants to process different formats, use convert callback instead of convert_s16. You can put both in the ops for compatibility, too. The input and output formats are found in the info argument of init callback. - Now the PCM rate plugin core will skip the temporary buffer allocation and conversions for pre- and post-process if not needed (i.e. matching with the requested input or output format). The rate plugin API version is bumped to 0x010003. Signed-off-by: Takashi Iwai <tiwai@suse.de>
2021-06-17 10:03:17 +02:00
channels, rate->info.out.period_size);
if (!rate->dst_buf) {
err = -ENOMEM;
goto error;
pcm: rate: Improve the support multiple formats This patch extends the PCM rate plugin for allowing its converter plugin to deal with multiple formats. Currently, the converter plugin is allowed to take different formats only when convert callback is defined. And for this way (so far only the standard linear rate plugin does), all linear formats have to be handled, and it's cumbersome. OTOH, most other rate plugins are implemented with convert_s16 callback, which accepts only S16 format. This is often not ideal because many converter engines can handle 32bit formats. Also, the target format is often 32bit format, hence this would require additional conversion even if the converter engine can output 32bit natively. In this patch, for addressing the problems above, the rate plugin API is extended in the following way: - The new get_supported_formats callback is added; this stores the bit masks of the supported input and output formats, as well as the behavior flags. Currently only linear formats are allowed. - When the plugin accepts only the interleaved stream, set SND_PCM_RATE_FLAG_INTERLEAVED flag bit. Otherwise the code has to handle snd_pcm_channel_area at each call. - When both input and output formats have to be identical, pass SND_PCM_RATE_FLAG_SYNC_FORMATS flag bit. - When the converter wants to process different formats, use convert callback instead of convert_s16. You can put both in the ops for compatibility, too. The input and output formats are found in the info argument of init callback. - Now the PCM rate plugin core will skip the temporary buffer allocation and conversions for pre- and post-process if not needed (i.e. matching with the requested input or output format). The rate plugin API version is bumped to 0x010003. Signed-off-by: Takashi Iwai <tiwai@suse.de>
2021-06-17 10:03:17 +02:00
}
}
2000-09-24 09:57:26 +00:00
return 0;
error:
pcm: rate: Improve the support multiple formats This patch extends the PCM rate plugin for allowing its converter plugin to deal with multiple formats. Currently, the converter plugin is allowed to take different formats only when convert callback is defined. And for this way (so far only the standard linear rate plugin does), all linear formats have to be handled, and it's cumbersome. OTOH, most other rate plugins are implemented with convert_s16 callback, which accepts only S16 format. This is often not ideal because many converter engines can handle 32bit formats. Also, the target format is often 32bit format, hence this would require additional conversion even if the converter engine can output 32bit natively. In this patch, for addressing the problems above, the rate plugin API is extended in the following way: - The new get_supported_formats callback is added; this stores the bit masks of the supported input and output formats, as well as the behavior flags. Currently only linear formats are allowed. - When the plugin accepts only the interleaved stream, set SND_PCM_RATE_FLAG_INTERLEAVED flag bit. Otherwise the code has to handle snd_pcm_channel_area at each call. - When both input and output formats have to be identical, pass SND_PCM_RATE_FLAG_SYNC_FORMATS flag bit. - When the converter wants to process different formats, use convert callback instead of convert_s16. You can put both in the ops for compatibility, too. The input and output formats are found in the info argument of init callback. - Now the PCM rate plugin core will skip the temporary buffer allocation and conversions for pre- and post-process if not needed (i.e. matching with the requested input or output format). The rate plugin API version is bumped to 0x010003. Signed-off-by: Takashi Iwai <tiwai@suse.de>
2021-06-17 10:03:17 +02:00
rate_free_tmp_buf(&rate->src_buf);
rate_free_tmp_buf(&rate->dst_buf);
error_init:
if (rate->ops.free)
rate->ops.free(rate->obj);
pcm: rate: Improve the support multiple formats This patch extends the PCM rate plugin for allowing its converter plugin to deal with multiple formats. Currently, the converter plugin is allowed to take different formats only when convert callback is defined. And for this way (so far only the standard linear rate plugin does), all linear formats have to be handled, and it's cumbersome. OTOH, most other rate plugins are implemented with convert_s16 callback, which accepts only S16 format. This is often not ideal because many converter engines can handle 32bit formats. Also, the target format is often 32bit format, hence this would require additional conversion even if the converter engine can output 32bit natively. In this patch, for addressing the problems above, the rate plugin API is extended in the following way: - The new get_supported_formats callback is added; this stores the bit masks of the supported input and output formats, as well as the behavior flags. Currently only linear formats are allowed. - When the plugin accepts only the interleaved stream, set SND_PCM_RATE_FLAG_INTERLEAVED flag bit. Otherwise the code has to handle snd_pcm_channel_area at each call. - When both input and output formats have to be identical, pass SND_PCM_RATE_FLAG_SYNC_FORMATS flag bit. - When the converter wants to process different formats, use convert callback instead of convert_s16. You can put both in the ops for compatibility, too. The input and output formats are found in the info argument of init callback. - Now the PCM rate plugin core will skip the temporary buffer allocation and conversions for pre- and post-process if not needed (i.e. matching with the requested input or output format). The rate plugin API version is bumped to 0x010003. Signed-off-by: Takashi Iwai <tiwai@suse.de>
2021-06-17 10:03:17 +02:00
error_pareas:
rate_free_tmp_buf(&rate->pareas);
rate_free_tmp_buf(&rate->sareas);
return err;
2000-09-24 09:57:26 +00:00
}
2001-01-19 13:10:50 +00:00
static int snd_pcm_rate_hw_free(snd_pcm_t *pcm)
{
snd_pcm_rate_t *rate = pcm->private_data;
rate_free_tmp_buf(&rate->pareas);
rate_free_tmp_buf(&rate->sareas);
if (rate->ops.free)
rate->ops.free(rate->obj);
pcm: rate: Improve the support multiple formats This patch extends the PCM rate plugin for allowing its converter plugin to deal with multiple formats. Currently, the converter plugin is allowed to take different formats only when convert callback is defined. And for this way (so far only the standard linear rate plugin does), all linear formats have to be handled, and it's cumbersome. OTOH, most other rate plugins are implemented with convert_s16 callback, which accepts only S16 format. This is often not ideal because many converter engines can handle 32bit formats. Also, the target format is often 32bit format, hence this would require additional conversion even if the converter engine can output 32bit natively. In this patch, for addressing the problems above, the rate plugin API is extended in the following way: - The new get_supported_formats callback is added; this stores the bit masks of the supported input and output formats, as well as the behavior flags. Currently only linear formats are allowed. - When the plugin accepts only the interleaved stream, set SND_PCM_RATE_FLAG_INTERLEAVED flag bit. Otherwise the code has to handle snd_pcm_channel_area at each call. - When both input and output formats have to be identical, pass SND_PCM_RATE_FLAG_SYNC_FORMATS flag bit. - When the converter wants to process different formats, use convert callback instead of convert_s16. You can put both in the ops for compatibility, too. The input and output formats are found in the info argument of init callback. - Now the PCM rate plugin core will skip the temporary buffer allocation and conversions for pre- and post-process if not needed (i.e. matching with the requested input or output format). The rate plugin API version is bumped to 0x010003. Signed-off-by: Takashi Iwai <tiwai@suse.de>
2021-06-17 10:03:17 +02:00
rate_free_tmp_buf(&rate->src_buf);
rate_free_tmp_buf(&rate->dst_buf);
return snd_pcm_hw_free(rate->gen.slave);
2001-01-19 13:10:50 +00:00
}
static void recalc(snd_pcm_t *pcm, snd_pcm_uframes_t *val)
{
snd_pcm_rate_t *rate = pcm->private_data;
snd_pcm_t *slave = rate->gen.slave;
unsigned long div;
if (*val == pcm->buffer_size) {
*val = slave->buffer_size;
} else {
div = *val / pcm->period_size;
if (div * pcm->period_size == *val)
*val = div * slave->period_size;
else
*val = muldiv_near(*val, slave->period_size, pcm->period_size);
}
}
2000-11-20 20:10:46 +00:00
static int snd_pcm_rate_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params)
{
snd_pcm_rate_t *rate = pcm->private_data;
snd_pcm_t *slave = rate->gen.slave;
snd_pcm_sw_params_t *sparams;
snd_pcm_uframes_t boundary1, boundary2, sboundary;
int err;
sparams = &rate->sw_params;
err = snd_pcm_sw_params_current(slave, sparams);
if (err < 0)
return err;
sboundary = sparams->boundary;
*sparams = *params;
boundary1 = pcm->buffer_size;
boundary2 = slave->buffer_size;
while (boundary1 * 2 <= LONG_MAX - pcm->buffer_size &&
boundary2 * 2 <= LONG_MAX - slave->buffer_size) {
boundary1 *= 2;
boundary2 *= 2;
}
params->boundary = boundary1;
sparams->boundary = sboundary;
if (rate->ops.adjust_pitch)
rate->ops.adjust_pitch(rate->obj, &rate->info);
recalc(pcm, &sparams->avail_min);
rate->orig_avail_min = sparams->avail_min;
recalc(pcm, &sparams->start_threshold);
if (sparams->avail_min < 1) sparams->avail_min = 1;
if (sparams->start_threshold <= slave->buffer_size) {
if (sparams->start_threshold > (slave->buffer_size / sparams->avail_min) * sparams->avail_min)
sparams->start_threshold = (slave->buffer_size / sparams->avail_min) * sparams->avail_min;
}
if (sparams->stop_threshold >= params->boundary) {
sparams->stop_threshold = sparams->boundary;
} else {
recalc(pcm, &sparams->stop_threshold);
}
recalc(pcm, &sparams->silence_threshold);
if (sparams->silence_size >= params->boundary) {
sparams->silence_size = sparams->boundary;
} else {
recalc(pcm, &sparams->silence_size);
}
return snd_pcm_sw_params(slave, sparams);
2000-11-20 20:10:46 +00:00
}
2000-09-24 09:57:26 +00:00
static int snd_pcm_rate_init(snd_pcm_t *pcm)
{
snd_pcm_rate_t *rate = pcm->private_data;
if (rate->ops.reset)
rate->ops.reset(rate->obj);
rate->last_commit_ptr = 0;
rate->start_pending = 0;
2000-09-24 09:57:26 +00:00
return 0;
}
static void do_convert(const snd_pcm_channel_area_t *dst_areas,
snd_pcm_uframes_t dst_offset, unsigned int dst_frames,
const snd_pcm_channel_area_t *src_areas,
snd_pcm_uframes_t src_offset, unsigned int src_frames,
unsigned int channels,
snd_pcm_rate_t *rate)
{
pcm: rate: Improve the support multiple formats This patch extends the PCM rate plugin for allowing its converter plugin to deal with multiple formats. Currently, the converter plugin is allowed to take different formats only when convert callback is defined. And for this way (so far only the standard linear rate plugin does), all linear formats have to be handled, and it's cumbersome. OTOH, most other rate plugins are implemented with convert_s16 callback, which accepts only S16 format. This is often not ideal because many converter engines can handle 32bit formats. Also, the target format is often 32bit format, hence this would require additional conversion even if the converter engine can output 32bit natively. In this patch, for addressing the problems above, the rate plugin API is extended in the following way: - The new get_supported_formats callback is added; this stores the bit masks of the supported input and output formats, as well as the behavior flags. Currently only linear formats are allowed. - When the plugin accepts only the interleaved stream, set SND_PCM_RATE_FLAG_INTERLEAVED flag bit. Otherwise the code has to handle snd_pcm_channel_area at each call. - When both input and output formats have to be identical, pass SND_PCM_RATE_FLAG_SYNC_FORMATS flag bit. - When the converter wants to process different formats, use convert callback instead of convert_s16. You can put both in the ops for compatibility, too. The input and output formats are found in the info argument of init callback. - Now the PCM rate plugin core will skip the temporary buffer allocation and conversions for pre- and post-process if not needed (i.e. matching with the requested input or output format). The rate plugin API version is bumped to 0x010003. Signed-off-by: Takashi Iwai <tiwai@suse.de>
2021-06-17 10:03:17 +02:00
const snd_pcm_channel_area_t *out_areas;
snd_pcm_uframes_t out_offset;
if (rate->dst_buf) {
out_areas = rate->dst_buf;
out_offset = 0;
} else {
pcm: rate: Improve the support multiple formats This patch extends the PCM rate plugin for allowing its converter plugin to deal with multiple formats. Currently, the converter plugin is allowed to take different formats only when convert callback is defined. And for this way (so far only the standard linear rate plugin does), all linear formats have to be handled, and it's cumbersome. OTOH, most other rate plugins are implemented with convert_s16 callback, which accepts only S16 format. This is often not ideal because many converter engines can handle 32bit formats. Also, the target format is often 32bit format, hence this would require additional conversion even if the converter engine can output 32bit natively. In this patch, for addressing the problems above, the rate plugin API is extended in the following way: - The new get_supported_formats callback is added; this stores the bit masks of the supported input and output formats, as well as the behavior flags. Currently only linear formats are allowed. - When the plugin accepts only the interleaved stream, set SND_PCM_RATE_FLAG_INTERLEAVED flag bit. Otherwise the code has to handle snd_pcm_channel_area at each call. - When both input and output formats have to be identical, pass SND_PCM_RATE_FLAG_SYNC_FORMATS flag bit. - When the converter wants to process different formats, use convert callback instead of convert_s16. You can put both in the ops for compatibility, too. The input and output formats are found in the info argument of init callback. - Now the PCM rate plugin core will skip the temporary buffer allocation and conversions for pre- and post-process if not needed (i.e. matching with the requested input or output format). The rate plugin API version is bumped to 0x010003. Signed-off-by: Takashi Iwai <tiwai@suse.de>
2021-06-17 10:03:17 +02:00
out_areas = dst_areas;
out_offset = dst_offset;
}
if (rate->src_buf) {
snd_pcm_linear_convert(rate->src_buf, 0,
src_areas, src_offset,
channels, src_frames,
rate->src_conv_idx);
src_areas = rate->src_buf;
src_offset = 0;
}
pcm: rate: Improve the support multiple formats This patch extends the PCM rate plugin for allowing its converter plugin to deal with multiple formats. Currently, the converter plugin is allowed to take different formats only when convert callback is defined. And for this way (so far only the standard linear rate plugin does), all linear formats have to be handled, and it's cumbersome. OTOH, most other rate plugins are implemented with convert_s16 callback, which accepts only S16 format. This is often not ideal because many converter engines can handle 32bit formats. Also, the target format is often 32bit format, hence this would require additional conversion even if the converter engine can output 32bit natively. In this patch, for addressing the problems above, the rate plugin API is extended in the following way: - The new get_supported_formats callback is added; this stores the bit masks of the supported input and output formats, as well as the behavior flags. Currently only linear formats are allowed. - When the plugin accepts only the interleaved stream, set SND_PCM_RATE_FLAG_INTERLEAVED flag bit. Otherwise the code has to handle snd_pcm_channel_area at each call. - When both input and output formats have to be identical, pass SND_PCM_RATE_FLAG_SYNC_FORMATS flag bit. - When the converter wants to process different formats, use convert callback instead of convert_s16. You can put both in the ops for compatibility, too. The input and output formats are found in the info argument of init callback. - Now the PCM rate plugin core will skip the temporary buffer allocation and conversions for pre- and post-process if not needed (i.e. matching with the requested input or output format). The rate plugin API version is bumped to 0x010003. Signed-off-by: Takashi Iwai <tiwai@suse.de>
2021-06-17 10:03:17 +02:00
if (rate->ops.convert)
rate->ops.convert(rate->obj, out_areas, out_offset, dst_frames,
src_areas, src_offset, src_frames);
else
rate->ops.convert_s16(rate->obj,
snd_pcm_channel_area_addr(out_areas, out_offset),
dst_frames,
snd_pcm_channel_area_addr(src_areas, src_offset),
src_frames);
if (rate->dst_buf)
snd_pcm_linear_convert(dst_areas, dst_offset,
rate->dst_buf, 0,
channels, dst_frames,
rate->dst_conv_idx);
}
static inline void
snd_pcm_rate_write_areas1(snd_pcm_t *pcm,
const snd_pcm_channel_area_t *areas,
snd_pcm_uframes_t offset,
const snd_pcm_channel_area_t *slave_areas,
snd_pcm_uframes_t slave_offset)
2000-09-24 09:57:26 +00:00
{
snd_pcm_rate_t *rate = pcm->private_data;
do_convert(slave_areas, slave_offset, rate->gen.slave->period_size,
areas, offset, pcm->period_size,
pcm->channels, rate);
2000-09-24 09:57:26 +00:00
}
static inline void
snd_pcm_rate_read_areas1(snd_pcm_t *pcm,
const snd_pcm_channel_area_t *areas,
snd_pcm_uframes_t offset,
const snd_pcm_channel_area_t *slave_areas,
snd_pcm_uframes_t slave_offset)
2000-09-24 09:57:26 +00:00
{
snd_pcm_rate_t *rate = pcm->private_data;
do_convert(areas, offset, pcm->period_size,
slave_areas, slave_offset, rate->gen.slave->period_size,
pcm->channels, rate);
2000-09-24 09:57:26 +00:00
}
static inline void snd_pcm_rate_sync_hwptr0(snd_pcm_t *pcm, snd_pcm_uframes_t slave_hw_ptr)
{
snd_pcm_rate_t *rate;
snd_pcm_sframes_t slave_hw_ptr_diff;
snd_pcm_sframes_t last_slave_hw_ptr_frac;
if (pcm->stream != SND_PCM_STREAM_PLAYBACK)
return;
rate = pcm->private_data;
slave_hw_ptr_diff = pcm_frame_diff(slave_hw_ptr, rate->last_slave_hw_ptr, rate->gen.slave->boundary);
if (slave_hw_ptr_diff == 0)
return;
last_slave_hw_ptr_frac = rate->last_slave_hw_ptr % rate->gen.slave->period_size;
/* While handling fraction part fo slave period, rounded value will be
* introduced by input_frames().
* To eliminate rounding issue on rate->hw_ptr, subtract last rounded
* value from rate->hw_ptr and add new rounded value of present
* slave_hw_ptr fraction part to rate->hw_ptr. Hence,
* rate->hw_ptr += [ (no. of updated slave periods * pcm rate period size) -
* fractional part of last_slave_hw_ptr rounded value +
* fractional part of updated slave hw ptr's rounded value ]
*/
rate->hw_ptr += (
(((last_slave_hw_ptr_frac + slave_hw_ptr_diff) / rate->gen.slave->period_size) * pcm->period_size) -
rate->ops.input_frames(rate->obj, last_slave_hw_ptr_frac) +
rate->ops.input_frames(rate->obj, (last_slave_hw_ptr_frac + slave_hw_ptr_diff) % rate->gen.slave->period_size));
rate->last_slave_hw_ptr = slave_hw_ptr;
rate->hw_ptr %= pcm->boundary;
}
static inline void snd_pcm_rate_sync_hwptr(snd_pcm_t *pcm)
{
snd_pcm_rate_t *rate = pcm->private_data;
snd_pcm_rate_sync_hwptr0(pcm, *rate->gen.slave->hw.ptr);
}
static int snd_pcm_rate_hwsync(snd_pcm_t *pcm)
{
snd_pcm_rate_t *rate = pcm->private_data;
int err = snd_pcm_hwsync(rate->gen.slave);
if (err < 0)
return err;
snd_pcm_rate_sync_hwptr(pcm);
return 0;
}
static snd_pcm_uframes_t snd_pcm_rate_playback_internal_delay(snd_pcm_t *pcm)
{
snd_pcm_rate_t *rate = pcm->private_data;
return pcm_frame_diff(rate->appl_ptr, rate->last_commit_ptr, pcm->boundary);
}
static int snd_pcm_rate_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)
{
snd_pcm_rate_t *rate = pcm->private_data;
snd_pcm_sframes_t slave_delay;
int err;
snd_pcm_rate_hwsync(pcm);
err = snd_pcm_delay(rate->gen.slave, &slave_delay);
if (err < 0) {
return err;
}
if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
*delayp = rate->ops.input_frames(rate->obj, slave_delay)
+ snd_pcm_rate_playback_internal_delay(pcm);
} else {
*delayp = rate->ops.output_frames(rate->obj, slave_delay)
+ snd_pcm_mmap_capture_delay(pcm);
}
return 0;
}
static int snd_pcm_rate_prepare(snd_pcm_t *pcm)
{
snd_pcm_rate_t *rate = pcm->private_data;
int err;
err = snd_pcm_prepare(rate->gen.slave);
if (err < 0)
return err;
*pcm->hw.ptr = 0;
*pcm->appl.ptr = 0;
rate->last_slave_hw_ptr = 0;
err = snd_pcm_rate_init(pcm);
if (err < 0)
return err;
return 0;
}
static int snd_pcm_rate_reset(snd_pcm_t *pcm)
{
snd_pcm_rate_t *rate = pcm->private_data;
int err;
err = snd_pcm_reset(rate->gen.slave);
if (err < 0)
return err;
*pcm->hw.ptr = 0;
*pcm->appl.ptr = 0;
rate->last_slave_hw_ptr = 0;
err = snd_pcm_rate_init(pcm);
if (err < 0)
return err;
return 0;
}
static snd_pcm_sframes_t snd_pcm_rate_rewindable(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
{
return 0;
}
static snd_pcm_sframes_t snd_pcm_rate_forwardable(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
{
return 0;
}
static snd_pcm_sframes_t snd_pcm_rate_rewind(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
snd_pcm_uframes_t frames ATTRIBUTE_UNUSED)
{
return 0;
}
static snd_pcm_sframes_t snd_pcm_rate_forward(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
snd_pcm_uframes_t frames ATTRIBUTE_UNUSED)
{
return 0;
}
static int snd_pcm_rate_commit_area(snd_pcm_t *pcm, snd_pcm_rate_t *rate,
snd_pcm_uframes_t appl_offset,
snd_pcm_uframes_t size ATTRIBUTE_UNUSED,
snd_pcm_uframes_t slave_size)
{
snd_pcm_uframes_t cont = pcm->buffer_size - appl_offset;
const snd_pcm_channel_area_t *areas;
const snd_pcm_channel_area_t *slave_areas;
snd_pcm_uframes_t slave_offset, xfer;
snd_pcm_uframes_t slave_frames = ULONG_MAX;
snd_pcm_sframes_t result;
areas = snd_pcm_mmap_areas(pcm);
/*
* Because snd_pcm_rate_write_areas1() below will convert a full source period
* then there had better be a full period available in the current buffer.
*/
if (cont >= pcm->period_size) {
result = snd_pcm_mmap_begin(rate->gen.slave, &slave_areas, &slave_offset, &slave_frames);
if (result < 0)
return result;
/*
* Because snd_pcm_rate_write_areas1() below will convert to a full slave period
* then there had better be a full slave period available in the slave buffer.
*/
if (slave_frames < rate->gen.slave->period_size) {
snd_pcm_rate_write_areas1(pcm, areas, appl_offset, rate->sareas, 0);
goto __partial;
}
snd_pcm_rate_write_areas1(pcm, areas, appl_offset,
slave_areas, slave_offset);
/* Only commit the requested slave_size, even if more was actually converted */
result = snd_pcm_mmap_commit(rate->gen.slave, slave_offset, slave_size);
if (result < (snd_pcm_sframes_t)slave_size) {
if (result < 0)
return result;
result = snd_pcm_rewind(rate->gen.slave, result);
if (result < 0)
return result;
return 0;
}
} else {
snd_pcm_areas_copy(rate->pareas, 0,
areas, appl_offset,
pcm->channels, cont,
pcm->format);
snd_pcm_areas_copy(rate->pareas, cont,
areas, 0,
pcm->channels, pcm->period_size - cont,
pcm->format);
snd_pcm_rate_write_areas1(pcm, rate->pareas, 0, rate->sareas, 0);
/* ok, commit first fragment */
result = snd_pcm_mmap_begin(rate->gen.slave, &slave_areas, &slave_offset, &slave_frames);
if (result < 0)
return result;
__partial:
cont = slave_frames;
if (cont > slave_size)
cont = slave_size;
snd_pcm_areas_copy(slave_areas, slave_offset,
rate->sareas, 0,
pcm->channels, cont,
rate->gen.slave->format);
result = snd_pcm_mmap_commit(rate->gen.slave, slave_offset, cont);
if (result < (snd_pcm_sframes_t)cont) {
if (result < 0)
return result;
result = snd_pcm_rewind(rate->gen.slave, result);
if (result < 0)
return result;
return 0;
}
xfer = cont;
if (xfer == slave_size)
goto commit_done;
/* commit second fragment */
cont = slave_size - cont;
slave_frames = cont;
result = snd_pcm_mmap_begin(rate->gen.slave, &slave_areas, &slave_offset, &slave_frames);
if (result < 0)
return result;
#if 0
if (slave_offset) {
SNDERR("non-zero slave_offset %ld", slave_offset);
return -EIO;
}
#endif
snd_pcm_areas_copy(slave_areas, slave_offset,
rate->sareas, xfer,
pcm->channels, cont,
rate->gen.slave->format);
result = snd_pcm_mmap_commit(rate->gen.slave, slave_offset, cont);
if (result < (snd_pcm_sframes_t)cont) {
if (result < 0)
return result;
result = snd_pcm_rewind(rate->gen.slave, result + xfer);
if (result < 0)
return result;
return 0;
}
}
commit_done:
if (rate->start_pending) {
/* we have pending start-trigger. let's issue it now */
snd_pcm_start(rate->gen.slave);
rate->start_pending = 0;
}
return 1;
}
static int snd_pcm_rate_commit_next_period(snd_pcm_t *pcm, snd_pcm_uframes_t appl_offset)
{
snd_pcm_rate_t *rate = pcm->private_data;
return snd_pcm_rate_commit_area(pcm, rate, appl_offset, pcm->period_size,
rate->gen.slave->period_size);
}
static int snd_pcm_rate_grab_next_period(snd_pcm_t *pcm, snd_pcm_uframes_t hw_offset)
{
snd_pcm_rate_t *rate = pcm->private_data;
snd_pcm_uframes_t cont = pcm->buffer_size - hw_offset;
const snd_pcm_channel_area_t *areas;
const snd_pcm_channel_area_t *slave_areas;
snd_pcm_uframes_t slave_offset, xfer;
snd_pcm_uframes_t slave_frames = ULONG_MAX;
snd_pcm_sframes_t result;
areas = snd_pcm_mmap_areas(pcm);
if (cont >= pcm->period_size) {
result = snd_pcm_mmap_begin(rate->gen.slave, &slave_areas, &slave_offset, &slave_frames);
if (result < 0)
return result;
if (slave_frames < rate->gen.slave->period_size)
goto __partial;
snd_pcm_rate_read_areas1(pcm, areas, hw_offset,
slave_areas, slave_offset);
result = snd_pcm_mmap_commit(rate->gen.slave, slave_offset, rate->gen.slave->period_size);
if (result < (snd_pcm_sframes_t)rate->gen.slave->period_size) {
if (result < 0)
return result;
result = snd_pcm_rewind(rate->gen.slave, result);
if (result < 0)
return result;
return 0;
}
} else {
/* ok, grab first fragment */
result = snd_pcm_mmap_begin(rate->gen.slave, &slave_areas, &slave_offset, &slave_frames);
if (result < 0)
return result;
__partial:
cont = slave_frames;
if (cont > rate->gen.slave->period_size)
cont = rate->gen.slave->period_size;
snd_pcm_areas_copy(rate->sareas, 0,
slave_areas, slave_offset,
pcm->channels, cont,
rate->gen.slave->format);
result = snd_pcm_mmap_commit(rate->gen.slave, slave_offset, cont);
if (result < (snd_pcm_sframes_t)cont) {
if (result < 0)
return result;
result = snd_pcm_rewind(rate->gen.slave, result);
if (result < 0)
return result;
return 0;
}
xfer = cont;
if (xfer == rate->gen.slave->period_size)
goto __transfer;
/* grab second fragment */
cont = rate->gen.slave->period_size - cont;
slave_frames = cont;
result = snd_pcm_mmap_begin(rate->gen.slave, &slave_areas, &slave_offset, &slave_frames);
if (result < 0)
return result;
#if 0
if (slave_offset) {
SNDERR("non-zero slave_offset %ld", slave_offset);
return -EIO;
}
#endif
snd_pcm_areas_copy(rate->sareas, xfer,
slave_areas, slave_offset,
pcm->channels, cont,
rate->gen.slave->format);
result = snd_pcm_mmap_commit(rate->gen.slave, slave_offset, cont);
if (result < (snd_pcm_sframes_t)cont) {
if (result < 0)
return result;
result = snd_pcm_rewind(rate->gen.slave, result + xfer);
if (result < 0)
return result;
return 0;
}
__transfer:
cont = pcm->buffer_size - hw_offset;
if (cont >= pcm->period_size) {
snd_pcm_rate_read_areas1(pcm, areas, hw_offset,
rate->sareas, 0);
} else {
snd_pcm_rate_read_areas1(pcm,
rate->pareas, 0,
rate->sareas, 0);
snd_pcm_areas_copy(areas, hw_offset,
rate->pareas, 0,
pcm->channels, cont,
pcm->format);
snd_pcm_areas_copy(areas, 0,
rate->pareas, cont,
pcm->channels, pcm->period_size - cont,
pcm->format);
}
}
return 1;
}
static int snd_pcm_rate_sync_playback_area(snd_pcm_t *pcm, snd_pcm_uframes_t appl_ptr)
{
snd_pcm_rate_t *rate = pcm->private_data;
snd_pcm_t *slave = rate->gen.slave;
snd_pcm_uframes_t xfer;
snd_pcm_sframes_t slave_size;
int err;
slave_size = snd_pcm_avail_update(slave);
if (slave_size < 0)
return slave_size;
xfer = pcm_frame_diff(appl_ptr, rate->last_commit_ptr, pcm->boundary);
while (xfer >= pcm->period_size &&
(snd_pcm_uframes_t)slave_size >= rate->gen.slave->period_size) {
err = snd_pcm_rate_commit_next_period(pcm, rate->last_commit_ptr % pcm->buffer_size);
if (err == 0)
break;
if (err < 0)
return err;
xfer -= pcm->period_size;
slave_size -= rate->gen.slave->period_size;
rate->last_commit_ptr += pcm->period_size;
if (rate->last_commit_ptr >= pcm->boundary)
rate->last_commit_ptr = 0;
}
return 0;
}
static snd_pcm_sframes_t snd_pcm_rate_mmap_commit(snd_pcm_t *pcm,
snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,
snd_pcm_uframes_t size)
{
snd_pcm_rate_t *rate = pcm->private_data;
int err;
if (size == 0)
return 0;
if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
err = snd_pcm_rate_sync_playback_area(pcm, rate->appl_ptr + size);
if (err < 0)
return err;
}
snd_pcm_mmap_appl_forward(pcm, size);
return size;
}
static snd_pcm_sframes_t snd_pcm_rate_avail_update_capture(snd_pcm_t *pcm,
snd_pcm_sframes_t slave_size)
{
snd_pcm_rate_t *rate = pcm->private_data;
snd_pcm_t *slave = rate->gen.slave;
snd_pcm_uframes_t xfer, hw_offset, size;
xfer = snd_pcm_mmap_capture_avail(pcm);
size = pcm->buffer_size - xfer;
hw_offset = snd_pcm_mmap_hw_offset(pcm);
while (size >= pcm->period_size &&
(snd_pcm_uframes_t)slave_size >= slave->period_size) {
int err = snd_pcm_rate_grab_next_period(pcm, hw_offset);
if (err < 0)
return err;
if (err == 0)
return (snd_pcm_sframes_t)xfer;
xfer += pcm->period_size;
size -= pcm->period_size;
slave_size -= slave->period_size;
hw_offset += pcm->period_size;
hw_offset %= pcm->buffer_size;
snd_pcm_mmap_hw_forward(pcm, pcm->period_size);
}
return (snd_pcm_sframes_t)xfer;
}
static snd_pcm_sframes_t snd_pcm_rate_avail_update(snd_pcm_t *pcm)
{
snd_pcm_rate_t *rate = pcm->private_data;
snd_pcm_sframes_t slave_size;
slave_size = snd_pcm_avail_update(rate->gen.slave);
if (slave_size < 0)
return slave_size;
if (pcm->stream == SND_PCM_STREAM_CAPTURE)
return snd_pcm_rate_avail_update_capture(pcm, slave_size);
snd_pcm_rate_sync_hwptr(pcm);
snd_pcm_rate_sync_playback_area(pcm, rate->appl_ptr);
return snd_pcm_mmap_avail(pcm);
}
static int snd_pcm_rate_htimestamp(snd_pcm_t *pcm,
snd_pcm_uframes_t *avail,
snd_htimestamp_t *tstamp)
{
snd_pcm_rate_t *rate = pcm->private_data;
snd_pcm_sframes_t avail1;
snd_pcm_uframes_t tmp;
int ok = 0, err;
while (1) {
/* the position is from this plugin itself */
avail1 = snd_pcm_avail_update(pcm);
if (avail1 < 0)
return avail1;
if (ok && (snd_pcm_uframes_t)avail1 == *avail)
break;
*avail = avail1;
/* timestamp is taken from the slave PCM */
err = snd_pcm_htimestamp(rate->gen.slave, &tmp, tstamp);
if (err < 0)
return err;
ok = 1;
}
return 0;
}
static int snd_pcm_rate_poll_revents(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents)
{
snd_pcm_rate_t *rate = pcm->private_data;
if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
/* Try to sync as much as possible */
snd_pcm_rate_hwsync(pcm);
snd_pcm_rate_sync_playback_area(pcm, rate->appl_ptr);
}
return snd_pcm_poll_descriptors_revents(rate->gen.slave, pfds, nfds, revents);
}
pcm: Add thread-safety to PCM API Traditionally, many of ALSA library functions are supposed to be thread-unsafe, and applications are required to take care of thread safety by themselves. However, people never be careful enough, and almost all applications fail in this regard. This patch is an attempt to harden the thread safety in exported PCM functions in a simplistic way: just wrap some of exported functions with the pthread mutex of each PCM object. Not all API functions are wrapped by the mutex since it doesn't make sense. Instead, the patchset covers only the functions that may be likely called concurrently. The supposedly thread-safe API functions are marked in the document. For achieving the feature, two new fields are added snd_pcm_t when the option is enabled: thread_safe and lock. The former indicates that the plugin is thread-safe that doesn't need this workaround and the latter is the pthread mutex. Currently only hw plugin have thread_safe=1. So, the most of real-time sensitive apps won't be influenced by this patchset. Although the patch covers most of PCM ops, a few snd_pcm_fast_ops are left without the extra mutex locking: namely, the ones that may have blocking behavior, i.e. resume, drain, readi, writei, readn and writen. These are supposed to handle own locking in the callbacks. Also, if anyone wants to disable this new thread-safe API feature, it can be still turned off via --disable-thread-safety configure option. Signed-off-by: Takashi Iwai <tiwai@suse.de>
2016-06-30 15:32:40 +02:00
/* locking */
static int snd_pcm_rate_drain(snd_pcm_t *pcm)
{
snd_pcm_rate_t *rate = pcm->private_data;
if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
/* commit the remaining fraction (if any) */
snd_pcm_uframes_t size, ofs, saved_avail_min;
snd_pcm_sw_params_t sw_params;
int commit_err = 0;
pcm: Add thread-safety to PCM API Traditionally, many of ALSA library functions are supposed to be thread-unsafe, and applications are required to take care of thread safety by themselves. However, people never be careful enough, and almost all applications fail in this regard. This patch is an attempt to harden the thread safety in exported PCM functions in a simplistic way: just wrap some of exported functions with the pthread mutex of each PCM object. Not all API functions are wrapped by the mutex since it doesn't make sense. Instead, the patchset covers only the functions that may be likely called concurrently. The supposedly thread-safe API functions are marked in the document. For achieving the feature, two new fields are added snd_pcm_t when the option is enabled: thread_safe and lock. The former indicates that the plugin is thread-safe that doesn't need this workaround and the latter is the pthread mutex. Currently only hw plugin have thread_safe=1. So, the most of real-time sensitive apps won't be influenced by this patchset. Although the patch covers most of PCM ops, a few snd_pcm_fast_ops are left without the extra mutex locking: namely, the ones that may have blocking behavior, i.e. resume, drain, readi, writei, readn and writen. These are supposed to handle own locking in the callbacks. Also, if anyone wants to disable this new thread-safe API feature, it can be still turned off via --disable-thread-safety configure option. Signed-off-by: Takashi Iwai <tiwai@suse.de>
2016-06-30 15:32:40 +02:00
__snd_pcm_lock(pcm);
2007-09-21 10:40:55 +02:00
/* temporarily set avail_min to one */
sw_params = rate->sw_params;
saved_avail_min = sw_params.avail_min;
2007-09-21 10:40:55 +02:00
sw_params.avail_min = 1;
snd_pcm_sw_params(rate->gen.slave, &sw_params);
size = pcm_frame_diff(rate->appl_ptr, rate->last_commit_ptr, pcm->boundary);
ofs = rate->last_commit_ptr % pcm->buffer_size;
while (size > 0) {
snd_pcm_uframes_t psize, spsize;
pcm: Add thread-safety to PCM API Traditionally, many of ALSA library functions are supposed to be thread-unsafe, and applications are required to take care of thread safety by themselves. However, people never be careful enough, and almost all applications fail in this regard. This patch is an attempt to harden the thread safety in exported PCM functions in a simplistic way: just wrap some of exported functions with the pthread mutex of each PCM object. Not all API functions are wrapped by the mutex since it doesn't make sense. Instead, the patchset covers only the functions that may be likely called concurrently. The supposedly thread-safe API functions are marked in the document. For achieving the feature, two new fields are added snd_pcm_t when the option is enabled: thread_safe and lock. The former indicates that the plugin is thread-safe that doesn't need this workaround and the latter is the pthread mutex. Currently only hw plugin have thread_safe=1. So, the most of real-time sensitive apps won't be influenced by this patchset. Although the patch covers most of PCM ops, a few snd_pcm_fast_ops are left without the extra mutex locking: namely, the ones that may have blocking behavior, i.e. resume, drain, readi, writei, readn and writen. These are supposed to handle own locking in the callbacks. Also, if anyone wants to disable this new thread-safe API feature, it can be still turned off via --disable-thread-safety configure option. Signed-off-by: Takashi Iwai <tiwai@suse.de>
2016-06-30 15:32:40 +02:00
int err;
pcm: Add thread-safety to PCM API Traditionally, many of ALSA library functions are supposed to be thread-unsafe, and applications are required to take care of thread safety by themselves. However, people never be careful enough, and almost all applications fail in this regard. This patch is an attempt to harden the thread safety in exported PCM functions in a simplistic way: just wrap some of exported functions with the pthread mutex of each PCM object. Not all API functions are wrapped by the mutex since it doesn't make sense. Instead, the patchset covers only the functions that may be likely called concurrently. The supposedly thread-safe API functions are marked in the document. For achieving the feature, two new fields are added snd_pcm_t when the option is enabled: thread_safe and lock. The former indicates that the plugin is thread-safe that doesn't need this workaround and the latter is the pthread mutex. Currently only hw plugin have thread_safe=1. So, the most of real-time sensitive apps won't be influenced by this patchset. Although the patch covers most of PCM ops, a few snd_pcm_fast_ops are left without the extra mutex locking: namely, the ones that may have blocking behavior, i.e. resume, drain, readi, writei, readn and writen. These are supposed to handle own locking in the callbacks. Also, if anyone wants to disable this new thread-safe API feature, it can be still turned off via --disable-thread-safety configure option. Signed-off-by: Takashi Iwai <tiwai@suse.de>
2016-06-30 15:32:40 +02:00
err = __snd_pcm_wait_in_lock(rate->gen.slave, -1);
if (err < 0)
break;
if (size > pcm->period_size) {
psize = pcm->period_size;
spsize = rate->gen.slave->period_size;
} else {
psize = size;
spsize = rate->ops.output_frames(rate->obj, size);
if (! spsize)
break;
}
commit_err = snd_pcm_rate_commit_area(pcm, rate, ofs,
psize, spsize);
if (commit_err == 1) {
rate->last_commit_ptr += psize;
if (rate->last_commit_ptr >= pcm->boundary)
rate->last_commit_ptr = 0;
} else if (commit_err == 0) {
if (pcm->mode & SND_PCM_NONBLOCK) {
commit_err = -EAGAIN;
break;
}
continue;
} else
break;
ofs = (ofs + psize) % pcm->buffer_size;
size -= psize;
}
sw_params.avail_min = saved_avail_min;
snd_pcm_sw_params(rate->gen.slave, &sw_params);
pcm: Add thread-safety to PCM API Traditionally, many of ALSA library functions are supposed to be thread-unsafe, and applications are required to take care of thread safety by themselves. However, people never be careful enough, and almost all applications fail in this regard. This patch is an attempt to harden the thread safety in exported PCM functions in a simplistic way: just wrap some of exported functions with the pthread mutex of each PCM object. Not all API functions are wrapped by the mutex since it doesn't make sense. Instead, the patchset covers only the functions that may be likely called concurrently. The supposedly thread-safe API functions are marked in the document. For achieving the feature, two new fields are added snd_pcm_t when the option is enabled: thread_safe and lock. The former indicates that the plugin is thread-safe that doesn't need this workaround and the latter is the pthread mutex. Currently only hw plugin have thread_safe=1. So, the most of real-time sensitive apps won't be influenced by this patchset. Although the patch covers most of PCM ops, a few snd_pcm_fast_ops are left without the extra mutex locking: namely, the ones that may have blocking behavior, i.e. resume, drain, readi, writei, readn and writen. These are supposed to handle own locking in the callbacks. Also, if anyone wants to disable this new thread-safe API feature, it can be still turned off via --disable-thread-safety configure option. Signed-off-by: Takashi Iwai <tiwai@suse.de>
2016-06-30 15:32:40 +02:00
__snd_pcm_unlock(pcm);
if (commit_err < 0)
return commit_err;
}
return snd_pcm_drain(rate->gen.slave);
}
static snd_pcm_state_t snd_pcm_rate_state(snd_pcm_t *pcm)
{
snd_pcm_rate_t *rate = pcm->private_data;
if (rate->start_pending) /* pseudo-state */
return SND_PCM_STATE_RUNNING;
return snd_pcm_state(rate->gen.slave);
}
static int snd_pcm_rate_start(snd_pcm_t *pcm)
{
snd_pcm_rate_t *rate = pcm->private_data;
snd_pcm_sframes_t avail;
if (pcm->stream == SND_PCM_STREAM_CAPTURE)
return snd_pcm_start(rate->gen.slave);
if (snd_pcm_state(rate->gen.slave) != SND_PCM_STATE_PREPARED)
return -EBADFD;
gettimestamp(&rate->trigger_tstamp, pcm->tstamp_type);
avail = snd_pcm_mmap_playback_hw_avail(rate->gen.slave);
if (avail < 0) /* can't happen on healthy drivers */
return -EBADFD;
if (avail == 0) {
/* postpone the trigger since we have no data committed yet */
rate->start_pending = 1;
return 0;
}
rate->start_pending = 0;
return snd_pcm_start(rate->gen.slave);
}
static int snd_pcm_rate_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
{
snd_pcm_rate_t *rate = pcm->private_data;
snd_pcm_sframes_t err;
err = snd_pcm_status(rate->gen.slave, status);
if (err < 0)
return err;
if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
if (rate->start_pending)
status->state = SND_PCM_STATE_RUNNING;
status->trigger_tstamp = rate->trigger_tstamp;
}
snd_pcm_rate_sync_hwptr0(pcm, status->hw_ptr);
status->appl_ptr = *pcm->appl.ptr;
status->hw_ptr = *pcm->hw.ptr;
if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
status->delay = rate->ops.input_frames(rate->obj, status->delay)
+ snd_pcm_rate_playback_internal_delay(pcm);
status->avail = snd_pcm_mmap_playback_avail(pcm);
status->avail_max = rate->ops.input_frames(rate->obj, status->avail_max);
} else {
status->delay = rate->ops.output_frames(rate->obj, status->delay)
+ snd_pcm_mmap_capture_delay(pcm);
status->avail = snd_pcm_mmap_capture_avail(pcm);
status->avail_max = rate->ops.output_frames(rate->obj, status->avail_max);
}
return 0;
}
2001-01-17 11:00:32 +00:00
static void snd_pcm_rate_dump(snd_pcm_t *pcm, snd_output_t *out)
2000-09-24 09:57:26 +00:00
{
snd_pcm_rate_t *rate = pcm->private_data;
if (rate->sformat == SND_PCM_FORMAT_UNKNOWN)
2001-01-17 11:00:32 +00:00
snd_output_printf(out, "Rate conversion PCM (%d)\n",
2000-11-20 20:10:46 +00:00
rate->srate);
2000-09-24 09:57:26 +00:00
else
2001-01-17 11:00:32 +00:00
snd_output_printf(out, "Rate conversion PCM (%d, sformat=%s)\n",
2000-11-20 20:10:46 +00:00
rate->srate,
snd_pcm_format_name(rate->sformat));
if (rate->ops.dump)
rate->ops.dump(rate->obj, out);
snd_output_printf(out, "Protocol version: %x\n", rate->plugin_version);
2000-11-20 20:10:46 +00:00
if (pcm->setup) {
2001-01-17 11:00:32 +00:00
snd_output_printf(out, "Its setup is:\n");
snd_pcm_dump_setup(pcm, out);
2000-09-24 09:57:26 +00:00
}
2001-01-17 11:00:32 +00:00
snd_output_printf(out, "Slave: ");
snd_pcm_dump(rate->gen.slave, out);
2000-09-24 09:57:26 +00:00
}
static int snd_pcm_rate_close(snd_pcm_t *pcm)
{
snd_pcm_rate_t *rate = pcm->private_data;
if (rate->ops.close)
rate->ops.close(rate->obj);
if (rate->open_func)
snd_dlobj_cache_put(rate->open_func);
return snd_pcm_generic_close(pcm);
}
/**
* \brief Convert rate pcm frames to corresponding rate slave pcm frames
* \param pcm PCM handle
* \param frames Frames to be converted to slave frames
* \retval Corresponding slave frames
*/
static snd_pcm_uframes_t snd_pcm_rate_slave_frames(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
{
snd_pcm_uframes_t sframes;
snd_pcm_rate_t *rate = pcm->private_data;
if (pcm->stream == SND_PCM_STREAM_PLAYBACK)
sframes = rate->ops.output_frames(rate->obj, frames);
else
sframes = rate->ops.input_frames(rate->obj, frames);
return sframes;
}
static int snd_pcm_rate_may_wait_for_avail_min(snd_pcm_t *pcm,
snd_pcm_uframes_t avail)
{
return snd_pcm_plugin_may_wait_for_avail_min_conv(pcm, avail,
snd_pcm_rate_slave_frames);
}
static const snd_pcm_fast_ops_t snd_pcm_rate_fast_ops = {
.status = snd_pcm_rate_status,
.state = snd_pcm_rate_state,
.hwsync = snd_pcm_rate_hwsync,
.delay = snd_pcm_rate_delay,
.prepare = snd_pcm_rate_prepare,
.reset = snd_pcm_rate_reset,
.start = snd_pcm_rate_start,
.drop = snd_pcm_generic_drop,
.drain = snd_pcm_rate_drain,
.pause = snd_pcm_generic_pause,
.rewindable = snd_pcm_rate_rewindable,
.rewind = snd_pcm_rate_rewind,
.forwardable = snd_pcm_rate_forwardable,
.forward = snd_pcm_rate_forward,
.resume = snd_pcm_generic_resume,
.writei = snd_pcm_mmap_writei,
.writen = snd_pcm_mmap_writen,
.readi = snd_pcm_mmap_readi,
.readn = snd_pcm_mmap_readn,
.avail_update = snd_pcm_rate_avail_update,
.mmap_commit = snd_pcm_rate_mmap_commit,
.htimestamp = snd_pcm_rate_htimestamp,
.poll_descriptors_count = snd_pcm_generic_poll_descriptors_count,
.poll_descriptors = snd_pcm_generic_poll_descriptors,
.poll_revents = snd_pcm_rate_poll_revents,
.may_wait_for_avail_min = snd_pcm_rate_may_wait_for_avail_min,
};
static const snd_pcm_ops_t snd_pcm_rate_ops = {
.close = snd_pcm_rate_close,
.info = snd_pcm_generic_info,
.hw_refine = snd_pcm_rate_hw_refine,
.hw_params = snd_pcm_rate_hw_params,
.hw_free = snd_pcm_rate_hw_free,
.sw_params = snd_pcm_rate_sw_params,
.channel_info = snd_pcm_generic_channel_info,
.dump = snd_pcm_rate_dump,
.nonblock = snd_pcm_generic_nonblock,
.async = snd_pcm_generic_async,
.mmap = snd_pcm_generic_mmap,
.munmap = snd_pcm_generic_munmap,
.query_chmaps = snd_pcm_generic_query_chmaps,
.get_chmap = snd_pcm_generic_get_chmap,
.set_chmap = snd_pcm_generic_set_chmap,
2000-09-24 09:57:26 +00:00
};
/**
* \brief Get a default converter string
* \param root Root configuration node
* \retval A const config item if found, or NULL
*/
const snd_config_t *snd_pcm_rate_get_default_converter(snd_config_t *root)
{
snd_config_t *n;
/* look for default definition */
if (snd_config_search(root, "defaults.pcm.rate_converter", &n) >= 0)
return n;
return NULL;
}
pcm: rate: Improve the support multiple formats This patch extends the PCM rate plugin for allowing its converter plugin to deal with multiple formats. Currently, the converter plugin is allowed to take different formats only when convert callback is defined. And for this way (so far only the standard linear rate plugin does), all linear formats have to be handled, and it's cumbersome. OTOH, most other rate plugins are implemented with convert_s16 callback, which accepts only S16 format. This is often not ideal because many converter engines can handle 32bit formats. Also, the target format is often 32bit format, hence this would require additional conversion even if the converter engine can output 32bit natively. In this patch, for addressing the problems above, the rate plugin API is extended in the following way: - The new get_supported_formats callback is added; this stores the bit masks of the supported input and output formats, as well as the behavior flags. Currently only linear formats are allowed. - When the plugin accepts only the interleaved stream, set SND_PCM_RATE_FLAG_INTERLEAVED flag bit. Otherwise the code has to handle snd_pcm_channel_area at each call. - When both input and output formats have to be identical, pass SND_PCM_RATE_FLAG_SYNC_FORMATS flag bit. - When the converter wants to process different formats, use convert callback instead of convert_s16. You can put both in the ops for compatibility, too. The input and output formats are found in the info argument of init callback. - Now the PCM rate plugin core will skip the temporary buffer allocation and conversions for pre- and post-process if not needed (i.e. matching with the requested input or output format). The rate plugin API version is bumped to 0x010003. Signed-off-by: Takashi Iwai <tiwai@suse.de>
2021-06-17 10:03:17 +02:00
static void rate_initial_setup(snd_pcm_rate_t *rate)
{
if (rate->plugin_version == SND_PCM_RATE_PLUGIN_VERSION)
rate->plugin_version = rate->ops.version;
if (rate->plugin_version >= 0x010002 &&
rate->ops.get_supported_rates)
rate->ops.get_supported_rates(rate->obj,
&rate->rate_min,
&rate->rate_max);
if (rate->plugin_version >= 0x010003 &&
rate->ops.get_supported_formats) {
rate->ops.get_supported_formats(rate->obj,
&rate->in_formats,
&rate->out_formats,
&rate->format_flags);
} else if (!rate->ops.convert && rate->ops.convert_s16) {
rate->in_formats = rate->out_formats =
1ULL << SND_PCM_FORMAT_S16;
rate->format_flags = SND_PCM_RATE_FLAG_INTERLEAVED;
}
}
#ifdef PIC
static int is_builtin_plugin(const char *type)
{
return strcmp(type, "linear") == 0;
}
static const char *const default_rate_plugins[] = {
"speexrate", "linear", NULL
};
static int rate_open_func(snd_pcm_rate_t *rate, const char *type, const snd_config_t *converter_conf, int verbose)
{
char open_name[64], open_conf_name[64], lib_name[64], *lib = NULL;
snd_pcm_rate_open_func_t open_func;
snd_pcm_rate_open_conf_func_t open_conf_func;
int err;
snprintf(open_name, sizeof(open_name), "_snd_pcm_rate_%s_open", type);
snprintf(open_conf_name, sizeof(open_conf_name), "_snd_pcm_rate_%s_open_conf", type);
if (!is_builtin_plugin(type)) {
snprintf(lib_name, sizeof(lib_name),
"libasound_module_rate_%s.so", type);
lib = lib_name;
}
open_conf_func = snd_dlobj_cache_get(lib, open_conf_name, NULL, verbose && converter_conf != NULL);
if (open_conf_func) {
err = open_conf_func(SND_PCM_RATE_PLUGIN_VERSION,
&rate->obj, &rate->ops, converter_conf);
if (!err) {
rate->open_func = open_conf_func;
return 0;
} else {
snd_dlobj_cache_put(open_conf_func);
return err;
}
}
open_func = snd_dlobj_cache_get(lib, open_name, NULL, verbose);
if (!open_func)
return -ENOENT;
rate->open_func = open_func;
err = open_func(SND_PCM_RATE_PLUGIN_VERSION, &rate->obj, &rate->ops);
pcm: rate: Improve the support multiple formats This patch extends the PCM rate plugin for allowing its converter plugin to deal with multiple formats. Currently, the converter plugin is allowed to take different formats only when convert callback is defined. And for this way (so far only the standard linear rate plugin does), all linear formats have to be handled, and it's cumbersome. OTOH, most other rate plugins are implemented with convert_s16 callback, which accepts only S16 format. This is often not ideal because many converter engines can handle 32bit formats. Also, the target format is often 32bit format, hence this would require additional conversion even if the converter engine can output 32bit natively. In this patch, for addressing the problems above, the rate plugin API is extended in the following way: - The new get_supported_formats callback is added; this stores the bit masks of the supported input and output formats, as well as the behavior flags. Currently only linear formats are allowed. - When the plugin accepts only the interleaved stream, set SND_PCM_RATE_FLAG_INTERLEAVED flag bit. Otherwise the code has to handle snd_pcm_channel_area at each call. - When both input and output formats have to be identical, pass SND_PCM_RATE_FLAG_SYNC_FORMATS flag bit. - When the converter wants to process different formats, use convert callback instead of convert_s16. You can put both in the ops for compatibility, too. The input and output formats are found in the info argument of init callback. - Now the PCM rate plugin core will skip the temporary buffer allocation and conversions for pre- and post-process if not needed (i.e. matching with the requested input or output format). The rate plugin API version is bumped to 0x010003. Signed-off-by: Takashi Iwai <tiwai@suse.de>
2021-06-17 10:03:17 +02:00
if (!err)
return 0;
/* try to open with the old protocol version */
rate->plugin_version = SND_PCM_RATE_PLUGIN_VERSION_OLD;
err = open_func(SND_PCM_RATE_PLUGIN_VERSION_OLD,
&rate->obj, &rate->ops);
pcm: rate: Improve the support multiple formats This patch extends the PCM rate plugin for allowing its converter plugin to deal with multiple formats. Currently, the converter plugin is allowed to take different formats only when convert callback is defined. And for this way (so far only the standard linear rate plugin does), all linear formats have to be handled, and it's cumbersome. OTOH, most other rate plugins are implemented with convert_s16 callback, which accepts only S16 format. This is often not ideal because many converter engines can handle 32bit formats. Also, the target format is often 32bit format, hence this would require additional conversion even if the converter engine can output 32bit natively. In this patch, for addressing the problems above, the rate plugin API is extended in the following way: - The new get_supported_formats callback is added; this stores the bit masks of the supported input and output formats, as well as the behavior flags. Currently only linear formats are allowed. - When the plugin accepts only the interleaved stream, set SND_PCM_RATE_FLAG_INTERLEAVED flag bit. Otherwise the code has to handle snd_pcm_channel_area at each call. - When both input and output formats have to be identical, pass SND_PCM_RATE_FLAG_SYNC_FORMATS flag bit. - When the converter wants to process different formats, use convert callback instead of convert_s16. You can put both in the ops for compatibility, too. The input and output formats are found in the info argument of init callback. - Now the PCM rate plugin core will skip the temporary buffer allocation and conversions for pre- and post-process if not needed (i.e. matching with the requested input or output format). The rate plugin API version is bumped to 0x010003. Signed-off-by: Takashi Iwai <tiwai@suse.de>
2021-06-17 10:03:17 +02:00
if (!err)
return 0;
snd_dlobj_cache_put(open_func);
rate->open_func = NULL;
return err;
}
#endif
/*
* If the conf is an array of alternatives then the id of
* the first element will be "0" (or maybe NULL). Otherwise assume it is
* a structure.
*/
static int is_string_array(const snd_config_t *conf)
{
snd_config_iterator_t i;
if (snd_config_get_type(conf) != SND_CONFIG_TYPE_COMPOUND)
return 0;
i = snd_config_iterator_first(conf);
if (i && i != snd_config_iterator_end(conf)) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id;
if (snd_config_get_id(n, &id) < 0)
return 0;
if (id && strcmp(id, "0") != 0)
return 0;
}
return 1;
}
/**
* \brief Creates a new rate PCM
* \param pcmp Returns created PCM handle
* \param name Name of PCM
* \param sformat Slave format
* \param srate Slave rate
* \param converter SRC type string node
* \param slave Slave PCM handle
* \param close_slave When set, the slave PCM handle is closed with copy PCM
* \retval zero on success otherwise a negative error code
* \warning Using of this function might be dangerous in the sense
* of compatibility reasons. The prototype might be freely
* changed in future.
*/
int snd_pcm_rate_open(snd_pcm_t **pcmp, const char *name,
snd_pcm_format_t sformat, unsigned int srate,
const snd_config_t *converter,
snd_pcm_t *slave, int close_slave)
2000-09-24 09:57:26 +00:00
{
snd_pcm_t *pcm;
2000-09-24 09:57:26 +00:00
snd_pcm_rate_t *rate;
const char *type = NULL;
int err;
#ifndef PIC
snd_pcm_rate_open_func_t open_func;
extern int SND_PCM_RATE_PLUGIN_ENTRY(linear) (unsigned int version, void **objp, snd_pcm_rate_ops_t *ops);
#endif
assert(pcmp && slave);
if (sformat != SND_PCM_FORMAT_UNKNOWN &&
snd_pcm_format_linear(sformat) != 1)
2000-09-24 09:57:26 +00:00
return -EINVAL;
rate = calloc(1, sizeof(snd_pcm_rate_t));
if (!rate) {
return -ENOMEM;
}
rate->gen.slave = slave;
rate->gen.close_slave = close_slave;
2000-11-20 20:10:46 +00:00
rate->srate = srate;
rate->sformat = sformat;
2000-09-24 09:57:26 +00:00
pcm: rate: Improve the support multiple formats This patch extends the PCM rate plugin for allowing its converter plugin to deal with multiple formats. Currently, the converter plugin is allowed to take different formats only when convert callback is defined. And for this way (so far only the standard linear rate plugin does), all linear formats have to be handled, and it's cumbersome. OTOH, most other rate plugins are implemented with convert_s16 callback, which accepts only S16 format. This is often not ideal because many converter engines can handle 32bit formats. Also, the target format is often 32bit format, hence this would require additional conversion even if the converter engine can output 32bit natively. In this patch, for addressing the problems above, the rate plugin API is extended in the following way: - The new get_supported_formats callback is added; this stores the bit masks of the supported input and output formats, as well as the behavior flags. Currently only linear formats are allowed. - When the plugin accepts only the interleaved stream, set SND_PCM_RATE_FLAG_INTERLEAVED flag bit. Otherwise the code has to handle snd_pcm_channel_area at each call. - When both input and output formats have to be identical, pass SND_PCM_RATE_FLAG_SYNC_FORMATS flag bit. - When the converter wants to process different formats, use convert callback instead of convert_s16. You can put both in the ops for compatibility, too. The input and output formats are found in the info argument of init callback. - Now the PCM rate plugin core will skip the temporary buffer allocation and conversions for pre- and post-process if not needed (i.e. matching with the requested input or output format). The rate plugin API version is bumped to 0x010003. Signed-off-by: Takashi Iwai <tiwai@suse.de>
2021-06-17 10:03:17 +02:00
rate->rate_min = SND_PCM_PLUGIN_RATE_MIN;
rate->rate_max = SND_PCM_PLUGIN_RATE_MAX;
rate->plugin_version = SND_PCM_RATE_PLUGIN_VERSION;
err = snd_pcm_new(&pcm, SND_PCM_TYPE_RATE, name, slave->stream, slave->mode);
if (err < 0) {
free(rate);
return err;
}
#ifdef PIC
err = -ENOENT;
if (!converter) {
const char *const *types;
for (types = default_rate_plugins; *types; types++) {
err = rate_open_func(rate, *types, NULL, 0);
if (!err) {
type = *types;
break;
}
}
} else if (!snd_config_get_string(converter, &type))
err = rate_open_func(rate, type, NULL, 1);
else if (is_string_array(converter)) {
snd_config_iterator_t i, next;
snd_config_for_each(i, next, converter) {
snd_config_t *n = snd_config_iterator_entry(i);
if (snd_config_get_string(n, &type) < 0)
break;
err = rate_open_func(rate, type, NULL, 0);
if (!err)
break;
}
} else if (snd_config_get_type(converter) == SND_CONFIG_TYPE_COMPOUND) {
snd_config_iterator_t i, next;
snd_config_for_each(i, next, converter) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
if (strcmp(id, "name") != 0)
continue;
snd_config_get_string(n, &type);
break;
}
if (!type) {
SNDERR("No name given for rate converter");
snd_pcm_free(pcm);
free(rate);
return -EINVAL;
}
err = rate_open_func(rate, type, converter, 1);
} else {
SNDERR("Invalid type for rate converter");
snd_pcm_free(pcm);
free(rate);
return -EINVAL;
}
if (err < 0) {
SNDERR("Cannot find rate converter");
snd_pcm_free(pcm);
free(rate);
return -ENOENT;
2000-09-24 09:57:26 +00:00
}
#else
type = "linear";
open_func = SND_PCM_RATE_PLUGIN_ENTRY(linear);
err = open_func(SND_PCM_RATE_PLUGIN_VERSION, &rate->obj, &rate->ops);
if (err < 0) {
snd_pcm_free(pcm);
free(rate);
return err;
}
#endif
if (! rate->ops.init || ! (rate->ops.convert || rate->ops.convert_s16) ||
! rate->ops.input_frames || ! rate->ops.output_frames) {
SNDERR("Inproper rate plugin %s initialization", type);
snd_pcm_free(pcm);
free(rate);
return err;
}
pcm: rate: Improve the support multiple formats This patch extends the PCM rate plugin for allowing its converter plugin to deal with multiple formats. Currently, the converter plugin is allowed to take different formats only when convert callback is defined. And for this way (so far only the standard linear rate plugin does), all linear formats have to be handled, and it's cumbersome. OTOH, most other rate plugins are implemented with convert_s16 callback, which accepts only S16 format. This is often not ideal because many converter engines can handle 32bit formats. Also, the target format is often 32bit format, hence this would require additional conversion even if the converter engine can output 32bit natively. In this patch, for addressing the problems above, the rate plugin API is extended in the following way: - The new get_supported_formats callback is added; this stores the bit masks of the supported input and output formats, as well as the behavior flags. Currently only linear formats are allowed. - When the plugin accepts only the interleaved stream, set SND_PCM_RATE_FLAG_INTERLEAVED flag bit. Otherwise the code has to handle snd_pcm_channel_area at each call. - When both input and output formats have to be identical, pass SND_PCM_RATE_FLAG_SYNC_FORMATS flag bit. - When the converter wants to process different formats, use convert callback instead of convert_s16. You can put both in the ops for compatibility, too. The input and output formats are found in the info argument of init callback. - Now the PCM rate plugin core will skip the temporary buffer allocation and conversions for pre- and post-process if not needed (i.e. matching with the requested input or output format). The rate plugin API version is bumped to 0x010003. Signed-off-by: Takashi Iwai <tiwai@suse.de>
2021-06-17 10:03:17 +02:00
rate_initial_setup(rate);
pcm->ops = &snd_pcm_rate_ops;
pcm->fast_ops = &snd_pcm_rate_fast_ops;
pcm->private_data = rate;
pcm->poll_fd = slave->poll_fd;
pcm->poll_events = slave->poll_events;
pcm->mmap_rw = 1;
pcm->tstamp_type = slave->tstamp_type;
snd_pcm_set_hw_ptr(pcm, &rate->hw_ptr, -1, 0);
snd_pcm_set_appl_ptr(pcm, &rate->appl_ptr, -1, 0);
*pcmp = pcm;
2000-09-24 09:57:26 +00:00
return 0;
}
/*! \page pcm_plugins
\section pcm_plugins_rate Plugin: Rate
This plugin converts a stream rate. The input and output formats must be linear.
\code
pcm.name {
type rate # Rate PCM
slave STR # Slave name
# or
slave { # Slave definition
pcm STR # Slave PCM name
# or
pcm { } # Slave PCM definition
2004-04-26 07:40:12 +00:00
rate INT # Slave rate
[format STR] # Slave format
}
converter STR # optional
# or
converter [ STR1 STR2 ... ] # optional
# Converter type, default is taken from
# defaults.pcm.rate_converter
# or
converter { # optional
name STR # Convertor type
xxx yyy # optional convertor-specific configuration
}
}
\endcode
\subsection pcm_plugins_rate_funcref Function reference
<UL>
<LI>snd_pcm_rate_open()
<LI>_snd_pcm_rate_open()
</UL>
*/
/**
* \brief Creates a new rate PCM
* \param pcmp Returns created PCM handle
* \param name Name of PCM
* \param root Root configuration node
* \param conf Configuration node with rate PCM description
* \param stream Stream type
* \param mode Stream mode
* \retval zero on success otherwise a negative error code
* \warning Using of this function might be dangerous in the sense
* of compatibility reasons. The prototype might be freely
* changed in future.
*/
int _snd_pcm_rate_open(snd_pcm_t **pcmp, const char *name,
snd_config_t *root, snd_config_t *conf,
snd_pcm_stream_t stream, int mode)
2000-09-24 09:57:26 +00:00
{
snd_config_iterator_t i, next;
2000-09-24 09:57:26 +00:00
int err;
snd_pcm_t *spcm;
snd_config_t *slave = NULL, *sconf;
snd_pcm_format_t sformat = SND_PCM_FORMAT_UNKNOWN;
2001-03-17 16:34:43 +00:00
int srate = -1;
const snd_config_t *converter = NULL;
snd_config_for_each(i, next, conf) {
2001-02-07 11:34:33 +00:00
snd_config_t *n = snd_config_iterator_entry(i);
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
if (snd_pcm_conf_generic_id(id))
2000-09-24 09:57:26 +00:00
continue;
2001-03-17 16:34:43 +00:00
if (strcmp(id, "slave") == 0) {
slave = n;
2000-09-24 09:57:26 +00:00
continue;
}
if (strcmp(id, "converter") == 0) {
converter = n;
continue;
}
SNDERR("Unknown field %s", id);
2000-09-24 09:57:26 +00:00
return -EINVAL;
}
2001-03-17 16:34:43 +00:00
if (!slave) {
SNDERR("slave is not defined");
2000-09-24 09:57:26 +00:00
return -EINVAL;
2000-11-30 09:40:50 +00:00
}
err = snd_pcm_slave_conf(root, slave, &sconf, 2,
2001-03-17 16:34:43 +00:00
SND_PCM_HW_PARAM_FORMAT, 0, &sformat,
SND_PCM_HW_PARAM_RATE, SCONF_MANDATORY, &srate);
2001-03-17 16:34:43 +00:00
if (err < 0)
return err;
if (sformat != SND_PCM_FORMAT_UNKNOWN &&
snd_pcm_format_linear(sformat) != 1) {
snd_config_delete(sconf);
2001-03-17 16:34:43 +00:00
SNDERR("slave format is not linear");
2000-11-30 09:40:50 +00:00
return -EINVAL;
}
err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
snd_config_delete(sconf);
2000-09-24 09:57:26 +00:00
if (err < 0)
return err;
err = snd_pcm_rate_open(pcmp, name, sformat, (unsigned int) srate,
converter, spcm, 1);
2000-09-24 09:57:26 +00:00
if (err < 0)
snd_pcm_close(spcm);
return err;
}
#ifndef DOC_HIDDEN
2001-10-24 14:14:11 +00:00
SND_DLSYM_BUILD_VERSION(_snd_pcm_rate_open, SND_PCM_DLSYM_VERSION);
#endif