2002-01-22 14:27:25 +00:00
|
|
|
/**
|
|
|
|
|
* \file pcm/pcm_rate.c
|
|
|
|
|
* \ingroup PCM_Plugins
|
|
|
|
|
* \brief PCM Rate Plugin Interface
|
|
|
|
|
* \author Abramo Bagnara <abramo@alsa-project.org>
|
2004-02-04 09:21:11 +00:00
|
|
|
* \author Jaroslav Kysela <perex@suse.cz>
|
|
|
|
|
* \date 2000-2004
|
2002-01-22 14:27:25 +00:00
|
|
|
*/
|
2000-09-24 09:57:26 +00:00
|
|
|
/*
|
|
|
|
|
* PCM - Rate conversion
|
|
|
|
|
* Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
|
2004-02-04 09:21:11 +00:00
|
|
|
* 2004 by Jaroslav Kysela <perex@suse.cz>
|
2000-09-24 09:57:26 +00:00
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
* This library is free software; you can redistribute it and/or modify
|
2001-12-30 09:22:54 +00:00
|
|
|
* 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
|
2001-12-30 09:22:54 +00:00
|
|
|
* GNU Lesser General Public License for more details.
|
2000-09-24 09:57:26 +00:00
|
|
|
*
|
2001-12-30 09:22:54 +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
|
2001-12-30 09:22:54 +00:00
|
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
2000-09-24 09:57:26 +00:00
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <limits.h>
|
|
|
|
|
#include <byteswap.h>
|
|
|
|
|
#include "pcm_local.h"
|
|
|
|
|
#include "pcm_plugin.h"
|
2004-02-04 09:21:11 +00:00
|
|
|
#include "iatomic.h"
|
2000-09-24 09:57:26 +00:00
|
|
|
|
2004-02-07 15:01:31 +00:00
|
|
|
#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
|
|
|
|
|
|
2002-01-22 14:27:25 +00:00
|
|
|
#ifndef DOC_HIDDEN
|
|
|
|
|
|
2004-02-04 09:21:11 +00:00
|
|
|
#define LINEAR_DIV (1<<16)
|
2000-09-24 09:57:26 +00:00
|
|
|
|
2004-01-24 16:23:11 +00:00
|
|
|
enum rate_type {
|
|
|
|
|
RATE_TYPE_LINEAR, /* linear interpolation */
|
|
|
|
|
RATE_TYPE_BANDLIMIT, /* bandlimited interpolation */
|
|
|
|
|
RATE_TYPE_POLYPHASE, /* polyphase resampling */
|
|
|
|
|
};
|
|
|
|
|
|
2000-09-24 09:57:26 +00:00
|
|
|
typedef struct {
|
2004-01-24 16:23:11 +00:00
|
|
|
union {
|
|
|
|
|
struct {
|
|
|
|
|
int init;
|
|
|
|
|
int16_t old_sample, new_sample;
|
|
|
|
|
int sum;
|
|
|
|
|
} linear;
|
|
|
|
|
} u;
|
2001-01-26 09:56:30 +00:00
|
|
|
} snd_pcm_rate_state_t;
|
2000-09-24 09:57:26 +00:00
|
|
|
|
2004-02-04 09:21:11 +00:00
|
|
|
typedef void (*rate_f)(const snd_pcm_channel_area_t *dst_areas,
|
|
|
|
|
snd_pcm_uframes_t dst_offset,
|
|
|
|
|
snd_pcm_uframes_t dst_frames,
|
|
|
|
|
const snd_pcm_channel_area_t *src_areas,
|
|
|
|
|
snd_pcm_uframes_t src_offset,
|
|
|
|
|
snd_pcm_uframes_t src_frames,
|
|
|
|
|
unsigned int channels,
|
|
|
|
|
unsigned int getidx, unsigned int putidx,
|
|
|
|
|
unsigned int arg,
|
|
|
|
|
snd_pcm_rate_state_t *states);
|
2000-09-24 09:57:26 +00:00
|
|
|
|
|
|
|
|
typedef struct {
|
2004-02-04 09:21:11 +00:00
|
|
|
snd_pcm_t *slave;
|
|
|
|
|
int close_slave;
|
|
|
|
|
snd_atomic_write_t watom;
|
|
|
|
|
snd_pcm_uframes_t appl_ptr, hw_ptr;
|
2004-03-17 11:48:14 +00:00
|
|
|
snd_pcm_uframes_t orig_avail_min;
|
|
|
|
|
snd_pcm_sw_params_t sw_params;
|
2004-01-24 16:23:11 +00:00
|
|
|
enum rate_type type;
|
2001-03-29 17:50:28 +00:00
|
|
|
unsigned int get_idx;
|
|
|
|
|
unsigned int put_idx;
|
2000-09-24 09:57:26 +00:00
|
|
|
unsigned int pitch;
|
|
|
|
|
rate_f func;
|
2001-02-04 17:03:17 +00:00
|
|
|
snd_pcm_format_t sformat;
|
2001-03-29 17:50:28 +00:00
|
|
|
unsigned int srate;
|
2001-01-26 09:56:30 +00:00
|
|
|
snd_pcm_rate_state_t *states;
|
2004-02-04 09:21:11 +00:00
|
|
|
snd_pcm_channel_area_t *pareas; /* areas for splitted period (rate pcm) */
|
|
|
|
|
snd_pcm_channel_area_t *sareas; /* areas for splitted period (slave pcm) */
|
2000-09-24 09:57:26 +00:00
|
|
|
} snd_pcm_rate_t;
|
|
|
|
|
|
2004-02-04 09:21:11 +00:00
|
|
|
static void snd_pcm_rate_expand(const snd_pcm_channel_area_t *dst_areas,
|
|
|
|
|
snd_pcm_uframes_t dst_offset, snd_pcm_uframes_t dst_frames,
|
|
|
|
|
const snd_pcm_channel_area_t *src_areas,
|
|
|
|
|
snd_pcm_uframes_t src_offset, snd_pcm_uframes_t src_frames,
|
|
|
|
|
unsigned int channels,
|
|
|
|
|
unsigned int getidx, unsigned int putidx,
|
|
|
|
|
unsigned int get_threshold,
|
|
|
|
|
snd_pcm_rate_state_t *states)
|
2000-09-24 09:57:26 +00:00
|
|
|
{
|
2000-10-22 09:50:20 +00:00
|
|
|
#define GET16_LABELS
|
|
|
|
|
#define PUT16_LABELS
|
2000-09-24 09:57:26 +00:00
|
|
|
#include "plugin_ops.h"
|
2000-10-22 09:50:20 +00:00
|
|
|
#undef GET16_LABELS
|
|
|
|
|
#undef PUT16_LABELS
|
|
|
|
|
void *get = get16_labels[getidx];
|
|
|
|
|
void *put = put16_labels[putidx];
|
2000-09-24 09:57:26 +00:00
|
|
|
unsigned int channel;
|
2001-01-15 11:06:53 +00:00
|
|
|
snd_pcm_uframes_t src_frames1 = 0;
|
|
|
|
|
snd_pcm_uframes_t dst_frames1 = 0;
|
2000-09-24 09:57:26 +00:00
|
|
|
int16_t sample = 0;
|
|
|
|
|
|
|
|
|
|
for (channel = 0; channel < channels; ++channel) {
|
2004-02-04 18:29:41 +00:00
|
|
|
//int xpos = 0;
|
2000-11-30 14:15:52 +00:00
|
|
|
const snd_pcm_channel_area_t *src_area = &src_areas[channel];
|
|
|
|
|
const snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
|
2001-02-11 15:45:35 +00:00
|
|
|
const char *src;
|
|
|
|
|
char *dst;
|
2000-09-24 09:57:26 +00:00
|
|
|
int src_step, dst_step;
|
2004-01-24 16:23:11 +00:00
|
|
|
int16_t old_sample = states->u.linear.old_sample;
|
|
|
|
|
int16_t new_sample = states->u.linear.new_sample;
|
2004-02-04 09:21:11 +00:00
|
|
|
unsigned int pos = 0;
|
2000-09-24 09:57:26 +00:00
|
|
|
src = snd_pcm_channel_area_addr(src_area, src_offset);
|
|
|
|
|
dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
|
|
|
|
|
src_step = snd_pcm_channel_area_step(src_area);
|
|
|
|
|
dst_step = snd_pcm_channel_area_step(dst_area);
|
|
|
|
|
src_frames1 = 0;
|
|
|
|
|
dst_frames1 = 0;
|
2004-02-04 18:29:41 +00:00
|
|
|
if (!states->u.linear.init) {
|
|
|
|
|
goto __force_get;
|
|
|
|
|
} else {
|
|
|
|
|
states->u.linear.init = 2;
|
2002-03-11 14:59:11 +00:00
|
|
|
}
|
2003-09-10 11:02:57 +00:00
|
|
|
while (dst_frames1 < dst_frames) {
|
2004-01-24 16:23:11 +00:00
|
|
|
if (states->u.linear.init == 2) {
|
2002-03-11 18:56:34 +00:00
|
|
|
old_sample = new_sample;
|
2004-02-04 18:29:41 +00:00
|
|
|
__force_get:
|
2000-09-24 09:57:26 +00:00
|
|
|
goto *get;
|
2000-10-22 09:50:20 +00:00
|
|
|
#define GET16_END after_get
|
2000-09-24 09:57:26 +00:00
|
|
|
#include "plugin_ops.h"
|
2000-10-22 09:50:20 +00:00
|
|
|
#undef GET16_END
|
2000-09-24 09:57:26 +00:00
|
|
|
after_get:
|
2002-03-11 18:56:34 +00:00
|
|
|
new_sample = sample;
|
2004-02-04 18:29:41 +00:00
|
|
|
if (!states->u.linear.init)
|
|
|
|
|
old_sample = sample;
|
2004-01-24 16:23:11 +00:00
|
|
|
states->u.linear.init = 1;
|
2002-03-11 18:56:34 +00:00
|
|
|
}
|
2002-03-12 19:22:43 +00:00
|
|
|
sample = (((int64_t)old_sample * (int64_t)(get_threshold - pos)) + ((int64_t)new_sample * pos)) / get_threshold;
|
2004-02-04 18:29:41 +00:00
|
|
|
//printf("sample[%i] = %i (old_sample = %i, new_sample = %i)\n", xpos++, sample, old_sample, new_sample);
|
2000-09-24 09:57:26 +00:00
|
|
|
goto *put;
|
2000-10-22 09:50:20 +00:00
|
|
|
#define PUT16_END after_put
|
2000-09-24 09:57:26 +00:00
|
|
|
#include "plugin_ops.h"
|
2000-10-22 09:50:20 +00:00
|
|
|
#undef PUT16_END
|
2000-09-24 09:57:26 +00:00
|
|
|
after_put:
|
|
|
|
|
dst += dst_step;
|
|
|
|
|
dst_frames1++;
|
2004-02-04 09:21:11 +00:00
|
|
|
pos += LINEAR_DIV;
|
2003-09-10 11:02:57 +00:00
|
|
|
if (pos >= get_threshold) {
|
|
|
|
|
pos -= get_threshold;
|
|
|
|
|
src += src_step;
|
|
|
|
|
src_frames1++;
|
2004-01-24 16:23:11 +00:00
|
|
|
states->u.linear.init = 2; /* get a new sample */
|
2004-02-04 18:29:41 +00:00
|
|
|
//printf("new_src_pos = %i\n", (src - (char *)snd_pcm_channel_area_addr(src_area, src_offset)) / src_step);
|
2004-02-04 09:21:11 +00:00
|
|
|
assert(src_frames1 <= src_frames);
|
2003-09-10 11:02:57 +00:00
|
|
|
}
|
|
|
|
|
}
|
2004-01-24 16:23:11 +00:00
|
|
|
states->u.linear.old_sample = old_sample;
|
|
|
|
|
states->u.linear.new_sample = new_sample;
|
2000-09-24 09:57:26 +00:00
|
|
|
states++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2004-02-04 09:21:11 +00:00
|
|
|
static void snd_pcm_rate_shrink(const snd_pcm_channel_area_t *dst_areas,
|
|
|
|
|
snd_pcm_uframes_t dst_offset, snd_pcm_uframes_t dst_frames,
|
|
|
|
|
const snd_pcm_channel_area_t *src_areas,
|
|
|
|
|
snd_pcm_uframes_t src_offset, snd_pcm_uframes_t src_frames,
|
|
|
|
|
unsigned int channels,
|
|
|
|
|
unsigned int getidx, unsigned int putidx,
|
|
|
|
|
unsigned int get_increment,
|
|
|
|
|
snd_pcm_rate_state_t *states)
|
2000-09-24 09:57:26 +00:00
|
|
|
{
|
2000-10-22 09:50:20 +00:00
|
|
|
#define GET16_LABELS
|
|
|
|
|
#define PUT16_LABELS
|
2000-09-24 09:57:26 +00:00
|
|
|
#include "plugin_ops.h"
|
2000-10-22 09:50:20 +00:00
|
|
|
#undef GET16_LABELS
|
|
|
|
|
#undef PUT16_LABELS
|
|
|
|
|
void *get = get16_labels[getidx];
|
|
|
|
|
void *put = put16_labels[putidx];
|
2000-09-24 09:57:26 +00:00
|
|
|
unsigned int channel;
|
2001-01-15 11:06:53 +00:00
|
|
|
snd_pcm_uframes_t src_frames1 = 0;
|
|
|
|
|
snd_pcm_uframes_t dst_frames1 = 0;
|
2000-09-24 09:57:26 +00:00
|
|
|
int16_t sample = 0;
|
|
|
|
|
|
|
|
|
|
for (channel = 0; channel < channels; ++channel) {
|
2000-11-30 14:15:52 +00:00
|
|
|
const snd_pcm_channel_area_t *src_area = &src_areas[channel];
|
|
|
|
|
const snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
|
2000-09-24 09:57:26 +00:00
|
|
|
unsigned int pos;
|
|
|
|
|
int sum;
|
2001-02-11 15:45:35 +00:00
|
|
|
const char *src;
|
|
|
|
|
char *dst;
|
2000-09-24 09:57:26 +00:00
|
|
|
int src_step, dst_step;
|
2004-01-24 16:23:11 +00:00
|
|
|
sum = states->u.linear.sum;
|
2004-02-04 09:21:11 +00:00
|
|
|
pos = 0;
|
2004-01-24 16:23:11 +00:00
|
|
|
states->u.linear.init = 0;
|
2000-09-24 09:57:26 +00:00
|
|
|
src = snd_pcm_channel_area_addr(src_area, src_offset);
|
|
|
|
|
dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
|
|
|
|
|
src_step = snd_pcm_channel_area_step(src_area);
|
|
|
|
|
dst_step = snd_pcm_channel_area_step(dst_area);
|
|
|
|
|
src_frames1 = 0;
|
|
|
|
|
dst_frames1 = 0;
|
|
|
|
|
while (src_frames1 < src_frames) {
|
|
|
|
|
|
|
|
|
|
goto *get;
|
2000-10-22 09:50:20 +00:00
|
|
|
#define GET16_END after_get
|
2000-09-24 09:57:26 +00:00
|
|
|
#include "plugin_ops.h"
|
2000-10-22 09:50:20 +00:00
|
|
|
#undef GET16_END
|
2000-09-24 09:57:26 +00:00
|
|
|
after_get:
|
|
|
|
|
src += src_step;
|
|
|
|
|
src_frames1++;
|
|
|
|
|
pos += get_increment;
|
2004-02-04 09:21:11 +00:00
|
|
|
if (pos >= LINEAR_DIV) {
|
2000-09-24 09:57:26 +00:00
|
|
|
int s = sample;
|
2004-02-04 09:21:11 +00:00
|
|
|
pos -= LINEAR_DIV;
|
2000-09-24 09:57:26 +00:00
|
|
|
sum += s * (get_increment - pos);
|
2004-02-04 09:21:11 +00:00
|
|
|
sum /= LINEAR_DIV;
|
2000-09-24 09:57:26 +00:00
|
|
|
sample = sum;
|
|
|
|
|
goto *put;
|
2000-10-22 09:50:20 +00:00
|
|
|
#define PUT16_END after_put
|
2000-09-24 09:57:26 +00:00
|
|
|
#include "plugin_ops.h"
|
2000-10-22 09:50:20 +00:00
|
|
|
#undef PUT16_END
|
2000-09-24 09:57:26 +00:00
|
|
|
after_put:
|
|
|
|
|
dst += dst_step;
|
|
|
|
|
sum = s * pos;
|
|
|
|
|
dst_frames1++;
|
2004-02-04 09:21:11 +00:00
|
|
|
assert(dst_frames1 <= dst_frames);
|
2000-09-24 09:57:26 +00:00
|
|
|
} else
|
|
|
|
|
sum += sample * get_increment;
|
|
|
|
|
}
|
2004-01-24 16:23:11 +00:00
|
|
|
states->u.linear.sum = sum;
|
2000-09-24 09:57:26 +00:00
|
|
|
states++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2002-01-22 14:27:25 +00:00
|
|
|
#endif /* DOC_HIDDEN */
|
|
|
|
|
|
2004-02-04 09:21:11 +00:00
|
|
|
static snd_pcm_sframes_t snd_pcm_rate_client_frames(snd_pcm_t *pcm, snd_pcm_sframes_t frames)
|
|
|
|
|
{
|
|
|
|
|
snd_pcm_rate_t *rate = pcm->private_data;
|
|
|
|
|
if (frames == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
/* Round toward zero */
|
|
|
|
|
if (pcm->stream == SND_PCM_STREAM_PLAYBACK)
|
|
|
|
|
return muldiv_down(frames, LINEAR_DIV, rate->pitch);
|
|
|
|
|
else
|
|
|
|
|
return muldiv_down(frames, rate->pitch, LINEAR_DIV);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static snd_pcm_sframes_t snd_pcm_rate_slave_frames(snd_pcm_t *pcm, snd_pcm_sframes_t frames)
|
|
|
|
|
{
|
|
|
|
|
snd_pcm_rate_t *rate = pcm->private_data;
|
|
|
|
|
/* Round toward zero */
|
|
|
|
|
if (pcm->stream == SND_PCM_STREAM_PLAYBACK)
|
|
|
|
|
return muldiv_down(frames, rate->pitch, LINEAR_DIV);
|
|
|
|
|
else
|
|
|
|
|
return muldiv_down(frames, LINEAR_DIV, rate->pitch);
|
|
|
|
|
}
|
|
|
|
|
|
2001-01-18 18:20:31 +00:00
|
|
|
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
|
|
|
{
|
|
|
|
|
int err;
|
2001-03-21 16:31:31 +00:00
|
|
|
snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHM };
|
2001-02-04 18:57:05 +00:00
|
|
|
snd_pcm_format_mask_t format_mask = { SND_PCM_FMTBIT_LINEAR };
|
2001-02-04 17:03:17 +00:00
|
|
|
err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
|
2001-02-04 18:57:05 +00:00
|
|
|
&access_mask);
|
2000-12-21 20:44:10 +00:00
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
2001-02-04 17:03:17 +00:00
|
|
|
err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_FORMAT,
|
2001-02-04 18:57:05 +00:00
|
|
|
&format_mask);
|
2001-01-18 18:20:31 +00:00
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
2001-02-04 17:03:17 +00:00
|
|
|
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;
|
2001-02-04 17:03:17 +00:00
|
|
|
err = _snd_pcm_hw_param_set_min(params,
|
2002-01-12 10:52:42 +00:00
|
|
|
SND_PCM_HW_PARAM_RATE, SND_PCM_PLUGIN_RATE_MIN, 0);
|
2000-12-21 20:44:10 +00:00
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
2001-02-04 17:03:17 +00:00
|
|
|
err = _snd_pcm_hw_param_set_max(params,
|
2002-01-12 10:52:42 +00:00
|
|
|
SND_PCM_HW_PARAM_RATE, SND_PCM_PLUGIN_RATE_MAX, 0);
|
2000-12-21 20:44:10 +00:00
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
2001-01-18 18:20:31 +00:00
|
|
|
params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2000-12-07 15:58:03 +00:00
|
|
|
|
2001-01-18 18:20:31 +00:00
|
|
|
static int snd_pcm_rate_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)
|
|
|
|
|
{
|
2001-02-11 15:45:35 +00:00
|
|
|
snd_pcm_rate_t *rate = pcm->private_data;
|
2001-02-04 18:57:05 +00:00
|
|
|
snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP };
|
2001-01-18 18:20:31 +00:00
|
|
|
_snd_pcm_hw_params_any(sparams);
|
2001-02-04 17:03:17 +00:00
|
|
|
_snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
|
2001-02-04 18:57:05 +00:00
|
|
|
&saccess_mask);
|
2001-02-05 15:44:42 +00:00
|
|
|
if (rate->sformat != SND_PCM_FORMAT_UNKNOWN) {
|
2001-02-04 17:03:17 +00:00
|
|
|
_snd_pcm_hw_params_set_format(sparams, rate->sformat);
|
|
|
|
|
_snd_pcm_hw_params_set_subformat(sparams, SND_PCM_SUBFORMAT_STD);
|
2001-01-18 18:20:31 +00:00
|
|
|
}
|
2001-02-04 17:03:17 +00:00
|
|
|
_snd_pcm_hw_param_set_minmax(sparams, SND_PCM_HW_PARAM_RATE,
|
|
|
|
|
rate->srate, 0, rate->srate + 1, -1);
|
2001-01-18 18:20:31 +00:00
|
|
|
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)
|
|
|
|
|
{
|
2001-02-11 15:45:35 +00:00
|
|
|
snd_pcm_rate_t *rate = pcm->private_data;
|
2001-01-30 16:51:26 +00:00
|
|
|
snd_interval_t t, buffer_size;
|
|
|
|
|
const snd_interval_t *srate, *crate;
|
2001-01-18 18:20:31 +00:00
|
|
|
int err;
|
|
|
|
|
unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS |
|
|
|
|
|
SND_PCM_HW_PARBIT_PERIOD_TIME |
|
|
|
|
|
SND_PCM_HW_PARBIT_TICK_TIME);
|
2001-02-05 15:44:42 +00:00
|
|
|
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);
|
2001-02-04 17:03:17 +00:00
|
|
|
snd_interval_copy(&buffer_size, snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_BUFFER_SIZE));
|
2001-01-30 16:51:26 +00:00
|
|
|
snd_interval_unfloor(&buffer_size);
|
2001-02-04 17:03:17 +00:00
|
|
|
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);
|
2001-01-30 16:51:26 +00:00
|
|
|
snd_interval_muldiv(&buffer_size, srate, crate, &t);
|
2001-02-04 17:03:17 +00:00
|
|
|
err = _snd_pcm_hw_param_set_interval(sparams, SND_PCM_HW_PARAM_BUFFER_SIZE, &t);
|
2001-01-18 18:20:31 +00:00
|
|
|
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;
|
|
|
|
|
}
|
2001-01-18 18:20:31 +00:00
|
|
|
|
|
|
|
|
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
|
|
|
{
|
2001-02-11 15:45:35 +00:00
|
|
|
snd_pcm_rate_t *rate = pcm->private_data;
|
2001-01-30 16:51:26 +00:00
|
|
|
snd_interval_t t;
|
2004-02-07 15:01:31 +00:00
|
|
|
#ifdef DEBUG_REFINE
|
|
|
|
|
snd_output_t *out;
|
|
|
|
|
#endif
|
|
|
|
|
const snd_interval_t *sbuffer_size, *buffer_size;
|
2001-01-30 16:51:26 +00:00
|
|
|
const snd_interval_t *srate, *crate;
|
2000-12-21 20:44:10 +00:00
|
|
|
int err;
|
|
|
|
|
unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS |
|
2001-01-15 11:06:53 +00:00
|
|
|
SND_PCM_HW_PARBIT_PERIOD_TIME |
|
|
|
|
|
SND_PCM_HW_PARBIT_TICK_TIME);
|
2001-02-05 15:44:42 +00:00
|
|
|
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);
|
2001-02-04 17:03:17 +00:00
|
|
|
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);
|
2001-01-30 16:51:26 +00:00
|
|
|
snd_interval_muldiv(sbuffer_size, crate, srate, &t);
|
|
|
|
|
snd_interval_floor(&t);
|
2001-02-04 17:03:17 +00:00
|
|
|
err = _snd_pcm_hw_param_set_interval(params, SND_PCM_HW_PARAM_BUFFER_SIZE, &t);
|
2001-01-18 18:20:31 +00:00
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
2004-02-07 15:01:31 +00:00
|
|
|
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 ((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
|
2001-01-18 18:20:31 +00:00
|
|
|
err = _snd_pcm_hw_params_refine(params, links, sparams);
|
2004-02-07 15:01:31 +00:00
|
|
|
#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
|
2001-01-18 18:20:31 +00:00
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2001-01-15 11:06:53 +00:00
|
|
|
|
2004-02-04 09:21:11 +00:00
|
|
|
static int snd_pcm_rate_hw_refine_slave(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
|
|
|
|
|
{
|
|
|
|
|
snd_pcm_rate_t *rate = pcm->private_data;
|
|
|
|
|
return snd_pcm_hw_refine(rate->slave, params);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int snd_pcm_rate_hw_params_slave(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
|
|
|
|
|
{
|
|
|
|
|
snd_pcm_rate_t *rate = pcm->private_data;
|
|
|
|
|
return _snd_pcm_hw_params(rate->slave, params);
|
|
|
|
|
}
|
|
|
|
|
|
2001-01-18 18:20:31 +00:00
|
|
|
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,
|
2004-02-04 09:21:11 +00:00
|
|
|
snd_pcm_rate_hw_refine_slave);
|
2001-01-18 18:20:31 +00:00
|
|
|
}
|
2001-01-15 11:06:53 +00:00
|
|
|
|
2001-01-18 18:20:31 +00:00
|
|
|
static int snd_pcm_rate_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
|
|
|
|
|
{
|
2001-02-11 15:45:35 +00:00
|
|
|
snd_pcm_rate_t *rate = pcm->private_data;
|
2004-02-04 09:21:11 +00:00
|
|
|
snd_pcm_t *slave = rate->slave;
|
|
|
|
|
snd_pcm_format_t src_format, dst_format, pformat, sformat;
|
|
|
|
|
unsigned int src_rate, dst_rate, channels, pwidth, swidth, chn;
|
|
|
|
|
snd_pcm_uframes_t period_size, buffer_size;
|
2001-01-18 18:20:31 +00:00
|
|
|
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,
|
2004-02-04 09:21:11 +00:00
|
|
|
snd_pcm_rate_hw_params_slave);
|
2000-09-24 09:57:26 +00:00
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
2001-01-18 18:20:31 +00:00
|
|
|
|
2000-09-24 09:57:26 +00:00
|
|
|
if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
|
2002-09-17 12:33:05 +00:00
|
|
|
err = INTERNAL(snd_pcm_hw_params_get_format)(params, &src_format);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
2004-02-04 09:21:11 +00:00
|
|
|
pformat = src_format;
|
2000-11-20 20:10:46 +00:00
|
|
|
dst_format = slave->format;
|
2004-02-04 09:21:11 +00:00
|
|
|
sformat = dst_format;
|
2000-11-20 20:10:46 +00:00
|
|
|
dst_rate = slave->rate;
|
2004-02-04 09:21:11 +00:00
|
|
|
err = INTERNAL(snd_pcm_hw_params_get_rate)(params, &src_rate, 0);
|
2000-09-24 09:57:26 +00:00
|
|
|
} else {
|
2004-02-04 09:21:11 +00:00
|
|
|
sformat = src_format = slave->format;
|
2002-09-17 12:33:05 +00:00
|
|
|
err = INTERNAL(snd_pcm_hw_params_get_format)(params, &dst_format);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
2004-02-04 09:21:11 +00:00
|
|
|
pformat = dst_format;
|
2000-11-20 20:10:46 +00:00
|
|
|
src_rate = slave->rate;
|
2002-09-17 12:33:05 +00:00
|
|
|
err = INTERNAL(snd_pcm_hw_params_get_rate)(params, &dst_rate, 0);
|
2000-09-24 09:57:26 +00:00
|
|
|
}
|
2004-02-04 18:29:41 +00:00
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
err = INTERNAL(snd_pcm_hw_params_get_period_size)(params, &period_size, 0);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
err = INTERNAL(snd_pcm_hw_params_get_buffer_size)(params, &buffer_size);
|
2004-02-04 09:21:11 +00:00
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
err = INTERNAL(snd_pcm_hw_params_get_channels)(params, &channels);
|
2002-09-09 19:47:50 +00:00
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
2001-01-26 09:56:30 +00:00
|
|
|
rate->get_idx = snd_pcm_linear_get_index(src_format, SND_PCM_FORMAT_S16);
|
|
|
|
|
rate->put_idx = snd_pcm_linear_put_index(SND_PCM_FORMAT_S16, dst_format);
|
2000-09-24 09:57:26 +00:00
|
|
|
if (src_rate < dst_rate) {
|
2001-01-26 09:56:30 +00:00
|
|
|
rate->func = snd_pcm_rate_expand;
|
2000-09-24 09:57:26 +00:00
|
|
|
/* pitch is get_threshold */
|
|
|
|
|
} else {
|
2001-01-26 09:56:30 +00:00
|
|
|
rate->func = snd_pcm_rate_shrink;
|
2000-09-24 09:57:26 +00:00
|
|
|
/* pitch is get_increment */
|
|
|
|
|
}
|
2004-02-04 09:21:11 +00:00
|
|
|
rate->pitch = (((u_int64_t)dst_rate * LINEAR_DIV) + src_rate - 1) / src_rate;
|
2001-01-19 13:10:50 +00:00
|
|
|
assert(!rate->states);
|
2004-02-04 09:21:11 +00:00
|
|
|
assert(!rate->pareas);
|
|
|
|
|
rate->states = malloc(channels * sizeof(*rate->states));
|
|
|
|
|
if (rate->states == NULL)
|
|
|
|
|
return -ENOMEM;
|
2004-02-04 18:29:41 +00:00
|
|
|
if ((buffer_size / period_size) * period_size == buffer_size &&
|
|
|
|
|
(slave->buffer_size / slave->period_size) * slave->period_size == slave->buffer_size)
|
2004-02-04 09:21:11 +00:00
|
|
|
return 0;
|
|
|
|
|
rate->pareas = malloc(2 * channels * sizeof(*rate->pareas));
|
|
|
|
|
if (rate->pareas == NULL)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
pwidth = snd_pcm_format_physical_width(pformat);
|
|
|
|
|
swidth = snd_pcm_format_physical_width(sformat);
|
|
|
|
|
rate->pareas[0].addr = malloc(((pwidth * channels * period_size) / 8) +
|
|
|
|
|
((swidth * channels * slave->period_size) / 8));
|
|
|
|
|
if (rate->pareas[0].addr == NULL) {
|
|
|
|
|
free(rate->pareas);
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
}
|
|
|
|
|
rate->sareas = rate->pareas + channels;
|
|
|
|
|
rate->sareas[0].addr = (char *)rate->pareas[0].addr + ((pwidth * channels * period_size) / 8);
|
|
|
|
|
for (chn = 0; chn < channels; chn++) {
|
|
|
|
|
rate->pareas[chn].addr = rate->pareas[0].addr + (pwidth * chn * period_size) / 8;
|
|
|
|
|
rate->pareas[chn].first = 0;
|
|
|
|
|
rate->pareas[chn].step = pwidth;
|
|
|
|
|
rate->sareas[chn].addr = rate->sareas[0].addr + (swidth * chn * slave->period_size) / 8;
|
|
|
|
|
rate->sareas[chn].first = 0;
|
|
|
|
|
rate->sareas[chn].step = swidth;
|
|
|
|
|
}
|
2000-09-24 09:57:26 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2001-01-19 13:10:50 +00:00
|
|
|
static int snd_pcm_rate_hw_free(snd_pcm_t *pcm)
|
|
|
|
|
{
|
2001-02-11 15:45:35 +00:00
|
|
|
snd_pcm_rate_t *rate = pcm->private_data;
|
2004-02-04 09:21:11 +00:00
|
|
|
if (rate->pareas) {
|
|
|
|
|
free(rate->pareas[0].addr);
|
|
|
|
|
free(rate->pareas);
|
|
|
|
|
rate->pareas = NULL;
|
|
|
|
|
rate->sareas = NULL;
|
|
|
|
|
}
|
2001-01-19 13:10:50 +00:00
|
|
|
if (rate->states) {
|
|
|
|
|
free(rate->states);
|
2002-03-11 14:59:11 +00:00
|
|
|
rate->states = NULL;
|
2001-01-19 13:10:50 +00:00
|
|
|
}
|
2004-02-04 09:21:11 +00:00
|
|
|
return snd_pcm_hw_free(rate->slave);
|
2001-01-19 13:10:50 +00:00
|
|
|
}
|
|
|
|
|
|
2002-03-11 14:59:11 +00:00
|
|
|
static void recalc(snd_pcm_t *pcm, snd_pcm_uframes_t *val)
|
|
|
|
|
{
|
|
|
|
|
snd_pcm_rate_t *rate = pcm->private_data;
|
2004-02-04 09:21:11 +00:00
|
|
|
snd_pcm_t *slave = rate->slave;
|
2002-03-11 14:59:11 +00:00
|
|
|
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
|
2004-02-04 09:21:11 +00:00
|
|
|
*val = muldiv_near(*val, slave->period_size, pcm->period_size);
|
2002-03-11 14:59:11 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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)
|
|
|
|
|
{
|
2001-02-11 15:45:35 +00:00
|
|
|
snd_pcm_rate_t *rate = pcm->private_data;
|
2004-02-04 09:21:11 +00:00
|
|
|
snd_pcm_t *slave = rate->slave;
|
2004-03-17 11:48:14 +00:00
|
|
|
snd_pcm_sw_params_t *sparams;
|
2003-02-11 18:14:43 +00:00
|
|
|
snd_pcm_uframes_t boundary1, boundary2;
|
|
|
|
|
|
2004-03-17 11:48:14 +00:00
|
|
|
rate->sw_params = *params;
|
|
|
|
|
sparams = &rate->sw_params;
|
2004-02-04 09:21:11 +00:00
|
|
|
if ((rate->pitch >= LINEAR_DIV ? 1 : 0) ^ (pcm->stream == SND_PCM_STREAM_CAPTURE ? 1 : 0)) {
|
2003-02-11 18:14:43 +00:00
|
|
|
boundary1 = pcm->buffer_size;
|
|
|
|
|
boundary2 = slave->buffer_size;
|
|
|
|
|
while (boundary2 * 2 <= LONG_MAX - slave->buffer_size) {
|
|
|
|
|
boundary1 *= 2;
|
|
|
|
|
boundary2 *= 2;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
boundary1 = pcm->buffer_size;
|
|
|
|
|
boundary2 = slave->buffer_size;
|
|
|
|
|
while (boundary1 * 2 <= LONG_MAX - pcm->buffer_size) {
|
|
|
|
|
boundary1 *= 2;
|
|
|
|
|
boundary2 *= 2;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
params->boundary = boundary1;
|
2004-03-17 11:48:14 +00:00
|
|
|
sparams->boundary = boundary2;
|
2003-02-11 18:14:43 +00:00
|
|
|
if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
|
2004-02-04 09:21:11 +00:00
|
|
|
rate->pitch = (((u_int64_t)slave->period_size * LINEAR_DIV) + pcm->period_size - 1) / pcm->period_size;
|
|
|
|
|
do {
|
|
|
|
|
snd_pcm_uframes_t cframes;
|
|
|
|
|
|
|
|
|
|
cframes = snd_pcm_rate_client_frames(pcm, slave->period_size - 1);
|
|
|
|
|
if (cframes == pcm->period_size - 1)
|
|
|
|
|
break;
|
|
|
|
|
if (cframes > pcm->period_size - 1) {
|
|
|
|
|
rate->pitch++;
|
|
|
|
|
if ((snd_pcm_uframes_t)snd_pcm_rate_client_frames(pcm, slave->period_size - 1) < pcm->period_size - 1) {
|
|
|
|
|
SNDERR("Unable to satisfy pitch condition (%i/%i - %li/%li)\n", slave->rate, pcm->rate, slave->period_size - 1, pcm->period_size - 1);
|
|
|
|
|
return -EIO;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
rate->pitch--;
|
|
|
|
|
if ((snd_pcm_uframes_t)snd_pcm_rate_client_frames(pcm, slave->period_size - 1) > pcm->period_size - 1) {
|
|
|
|
|
SNDERR("Unable to satisfy pitch condition (%i/%i - %li/%li)\n", slave->rate, pcm->rate, slave->period_size - 1, pcm->period_size - 1);
|
|
|
|
|
return -EIO;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} while (1);
|
|
|
|
|
assert((snd_pcm_uframes_t)snd_pcm_rate_client_frames(pcm, slave->period_size - 1) == pcm->period_size - 1);
|
2003-02-11 18:14:43 +00:00
|
|
|
} else {
|
2004-02-04 09:21:11 +00:00
|
|
|
rate->pitch = (((u_int64_t)pcm->period_size * LINEAR_DIV) + slave->period_size - 1) / slave->period_size;
|
|
|
|
|
do {
|
|
|
|
|
snd_pcm_uframes_t cframes;
|
|
|
|
|
|
|
|
|
|
cframes = snd_pcm_rate_slave_frames(pcm, pcm->period_size - 1);
|
|
|
|
|
if (cframes == slave->period_size - 1)
|
|
|
|
|
break;
|
|
|
|
|
if (cframes > slave->period_size - 1) {
|
|
|
|
|
rate->pitch++;
|
|
|
|
|
if ((snd_pcm_uframes_t)snd_pcm_rate_slave_frames(pcm, pcm->period_size - 1) < slave->period_size - 1) {
|
|
|
|
|
SNDERR("Unable to satisfy pitch condition (%i/%i - %li/%li)\n", slave->rate, pcm->rate, slave->period_size - 1, pcm->period_size - 1);
|
|
|
|
|
return -EIO;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
rate->pitch--;
|
|
|
|
|
if ((snd_pcm_uframes_t)snd_pcm_rate_slave_frames(pcm, pcm->period_size - 1) > slave->period_size - 1) {
|
|
|
|
|
SNDERR("Unable to satisfy pitch condition (%i/%i - %li/%li)\n", slave->rate, pcm->rate, slave->period_size - 1, pcm->period_size - 1);
|
|
|
|
|
return -EIO;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} while (1);
|
|
|
|
|
assert((snd_pcm_uframes_t)snd_pcm_rate_slave_frames(pcm, pcm->period_size - 1) == slave->period_size - 1);
|
2003-02-11 18:14:43 +00:00
|
|
|
}
|
2004-03-17 11:48:14 +00:00
|
|
|
recalc(pcm, &sparams->avail_min);
|
|
|
|
|
rate->orig_avail_min = sparams->avail_min;
|
|
|
|
|
recalc(pcm, &sparams->xfer_align);
|
|
|
|
|
recalc(pcm, &sparams->start_threshold);
|
2004-04-03 08:27:58 +00:00
|
|
|
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->start_threshold > (slave->buffer_size / sparams->xfer_align) * sparams->xfer_align)
|
|
|
|
|
sparams->start_threshold = (slave->buffer_size / sparams->xfer_align) * sparams->xfer_align;
|
|
|
|
|
}
|
2004-03-17 11:48:14 +00:00
|
|
|
if (sparams->stop_threshold >= sparams->boundary) {
|
|
|
|
|
sparams->stop_threshold = sparams->boundary;
|
2003-02-11 18:14:43 +00:00
|
|
|
} else {
|
2004-03-17 11:48:14 +00:00
|
|
|
recalc(pcm, &sparams->stop_threshold);
|
2003-02-11 18:14:43 +00:00
|
|
|
}
|
2004-03-17 11:48:14 +00:00
|
|
|
recalc(pcm, &sparams->silence_threshold);
|
|
|
|
|
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)
|
|
|
|
|
{
|
2001-02-11 15:45:35 +00:00
|
|
|
snd_pcm_rate_t *rate = pcm->private_data;
|
2000-09-24 09:57:26 +00:00
|
|
|
unsigned int k;
|
2004-01-24 16:23:11 +00:00
|
|
|
switch (rate->type) {
|
|
|
|
|
case RATE_TYPE_LINEAR:
|
|
|
|
|
for (k = 0; k < pcm->channels; ++k) {
|
|
|
|
|
rate->states[k].u.linear.sum = 0;
|
|
|
|
|
rate->states[k].u.linear.old_sample = 0;
|
|
|
|
|
rate->states[k].u.linear.new_sample = 0;
|
|
|
|
|
rate->states[k].u.linear.init = 0;
|
|
|
|
|
}
|
2004-01-27 13:08:16 +00:00
|
|
|
break;
|
2004-01-24 16:23:11 +00:00
|
|
|
default:
|
|
|
|
|
assert(0);
|
2000-09-24 09:57:26 +00:00
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2004-02-04 09:21:11 +00:00
|
|
|
static inline int
|
|
|
|
|
snd_pcm_rate_write_areas1(snd_pcm_t *pcm,
|
2001-02-27 13:42:12 +00:00
|
|
|
const snd_pcm_channel_area_t *areas,
|
|
|
|
|
snd_pcm_uframes_t offset,
|
|
|
|
|
const snd_pcm_channel_area_t *slave_areas,
|
2004-02-04 09:21:11 +00:00
|
|
|
snd_pcm_uframes_t slave_offset)
|
2000-09-24 09:57:26 +00:00
|
|
|
{
|
2001-02-11 15:45:35 +00:00
|
|
|
snd_pcm_rate_t *rate = pcm->private_data;
|
2004-02-04 09:21:11 +00:00
|
|
|
rate->func(slave_areas, slave_offset, rate->slave->period_size,
|
|
|
|
|
areas, offset, pcm->period_size,
|
|
|
|
|
pcm->channels,
|
|
|
|
|
rate->get_idx, rate->put_idx,
|
|
|
|
|
rate->pitch, rate->states);
|
|
|
|
|
return 0;
|
2000-09-24 09:57:26 +00:00
|
|
|
}
|
|
|
|
|
|
2004-02-04 09:21:11 +00:00
|
|
|
static inline int
|
|
|
|
|
snd_pcm_rate_read_areas1(snd_pcm_t *pcm,
|
2001-02-27 13:42:12 +00:00
|
|
|
const snd_pcm_channel_area_t *areas,
|
|
|
|
|
snd_pcm_uframes_t offset,
|
|
|
|
|
const snd_pcm_channel_area_t *slave_areas,
|
2004-02-04 09:21:11 +00:00
|
|
|
snd_pcm_uframes_t slave_offset)
|
2000-09-24 09:57:26 +00:00
|
|
|
{
|
2001-02-11 15:45:35 +00:00
|
|
|
snd_pcm_rate_t *rate = pcm->private_data;
|
2004-02-04 09:21:11 +00:00
|
|
|
rate->func(areas, offset, pcm->period_size,
|
|
|
|
|
slave_areas, slave_offset, rate->slave->period_size,
|
|
|
|
|
pcm->channels,
|
|
|
|
|
rate->get_idx, rate->put_idx,
|
|
|
|
|
rate->pitch, rate->states);
|
|
|
|
|
return 0;
|
2000-09-24 09:57:26 +00:00
|
|
|
}
|
|
|
|
|
|
2004-02-04 09:21:11 +00:00
|
|
|
static inline snd_pcm_sframes_t snd_pcm_rate_move_applptr(snd_pcm_t *pcm, snd_pcm_sframes_t frames)
|
2000-09-24 09:57:26 +00:00
|
|
|
{
|
2001-02-11 15:45:35 +00:00
|
|
|
snd_pcm_rate_t *rate = pcm->private_data;
|
2004-02-04 09:21:11 +00:00
|
|
|
snd_pcm_uframes_t orig_appl_ptr, appl_ptr = rate->appl_ptr, slave_appl_ptr;
|
|
|
|
|
snd_pcm_sframes_t diff, ndiff;
|
|
|
|
|
snd_pcm_t *slave = rate->slave;
|
|
|
|
|
|
|
|
|
|
orig_appl_ptr = rate->appl_ptr;
|
|
|
|
|
if (frames > 0)
|
|
|
|
|
snd_pcm_mmap_appl_forward(pcm, frames);
|
2000-09-24 09:57:26 +00:00
|
|
|
else
|
2004-02-04 09:21:11 +00:00
|
|
|
snd_pcm_mmap_appl_backward(pcm, -frames);
|
|
|
|
|
slave_appl_ptr =
|
|
|
|
|
(appl_ptr / pcm->period_size) * rate->slave->period_size;
|
|
|
|
|
diff = slave_appl_ptr - *slave->appl.ptr;
|
|
|
|
|
if (diff < -(snd_pcm_sframes_t)(slave->boundary / 2)) {
|
|
|
|
|
diff = (slave->boundary - *slave->appl.ptr) + slave_appl_ptr;
|
|
|
|
|
} else if (diff > (snd_pcm_sframes_t)(slave->boundary / 2)) {
|
|
|
|
|
diff = -((slave->boundary - slave_appl_ptr) + *slave->appl.ptr);
|
|
|
|
|
}
|
|
|
|
|
if (diff == 0)
|
|
|
|
|
return frames;
|
|
|
|
|
if (diff > 0) {
|
|
|
|
|
ndiff = snd_pcm_forward(rate->slave, diff);
|
|
|
|
|
} else {
|
|
|
|
|
ndiff = snd_pcm_rewind(rate->slave, diff);
|
|
|
|
|
}
|
|
|
|
|
if (ndiff < 0)
|
|
|
|
|
return diff;
|
|
|
|
|
slave_appl_ptr = *slave->appl.ptr;
|
|
|
|
|
rate->appl_ptr =
|
|
|
|
|
(slave_appl_ptr / rate->slave->period_size) * pcm->period_size +
|
|
|
|
|
snd_pcm_rate_client_frames(pcm, slave_appl_ptr % rate->slave->period_size) +
|
|
|
|
|
orig_appl_ptr % pcm->period_size;
|
|
|
|
|
diff = orig_appl_ptr - rate->appl_ptr;
|
|
|
|
|
if (diff < -(snd_pcm_sframes_t)(slave->boundary / 2)) {
|
|
|
|
|
diff = (slave->boundary - rate->appl_ptr) + orig_appl_ptr;
|
|
|
|
|
} else if (diff > (snd_pcm_sframes_t)(slave->boundary / 2)) {
|
|
|
|
|
diff = -((slave->boundary - orig_appl_ptr) + rate->appl_ptr);
|
|
|
|
|
}
|
|
|
|
|
if (frames < 0)
|
|
|
|
|
return -diff;
|
|
|
|
|
return diff;
|
2000-09-24 09:57:26 +00:00
|
|
|
}
|
|
|
|
|
|
2004-02-04 09:21:11 +00:00
|
|
|
static inline void snd_pcm_rate_sync_hwptr(snd_pcm_t *pcm)
|
2000-12-29 15:00:53 +00:00
|
|
|
{
|
2001-02-11 15:45:35 +00:00
|
|
|
snd_pcm_rate_t *rate = pcm->private_data;
|
2004-02-04 09:21:11 +00:00
|
|
|
snd_pcm_uframes_t slave_hw_ptr = *rate->slave->hw.ptr;
|
|
|
|
|
|
|
|
|
|
if (pcm->stream != SND_PCM_STREAM_PLAYBACK)
|
|
|
|
|
return;
|
|
|
|
|
rate->hw_ptr =
|
|
|
|
|
(slave_hw_ptr / rate->slave->period_size) * pcm->period_size +
|
|
|
|
|
snd_pcm_rate_client_frames(pcm, slave_hw_ptr % rate->slave->period_size);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int snd_pcm_rate_close(snd_pcm_t *pcm)
|
|
|
|
|
{
|
|
|
|
|
snd_pcm_rate_t *rate = pcm->private_data;
|
|
|
|
|
int err = 0;
|
|
|
|
|
if (rate->close_slave)
|
|
|
|
|
err = snd_pcm_close(rate->slave);
|
|
|
|
|
free(rate);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int snd_pcm_rate_nonblock(snd_pcm_t *pcm, int nonblock)
|
|
|
|
|
{
|
|
|
|
|
snd_pcm_rate_t *rate = pcm->private_data;
|
|
|
|
|
return snd_pcm_nonblock(rate->slave, nonblock);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int snd_pcm_rate_async(snd_pcm_t *pcm, int sig, pid_t pid)
|
|
|
|
|
{
|
|
|
|
|
snd_pcm_rate_t *rate = pcm->private_data;
|
|
|
|
|
return snd_pcm_async(rate->slave, sig, pid);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
return snd_pcm_poll_descriptors_revents(rate->slave, pfds, nfds, revents);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int snd_pcm_rate_info(snd_pcm_t *pcm, snd_pcm_info_t * info)
|
|
|
|
|
{
|
|
|
|
|
snd_pcm_rate_t *rate = pcm->private_data;
|
|
|
|
|
return snd_pcm_info(rate->slave, info);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int snd_pcm_rate_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info)
|
|
|
|
|
{
|
|
|
|
|
return snd_pcm_channel_info_shm(pcm, info, -1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static snd_pcm_state_t snd_pcm_rate_state(snd_pcm_t *pcm)
|
|
|
|
|
{
|
|
|
|
|
snd_pcm_rate_t *rate = pcm->private_data;
|
|
|
|
|
return snd_pcm_state(rate->slave);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int snd_pcm_rate_hwsync(snd_pcm_t *pcm)
|
|
|
|
|
{
|
|
|
|
|
snd_pcm_rate_t *rate = pcm->private_data;
|
|
|
|
|
int err = snd_pcm_hwsync(rate->slave);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
snd_atomic_write_begin(&rate->watom);
|
|
|
|
|
snd_pcm_rate_sync_hwptr(pcm);
|
|
|
|
|
snd_atomic_write_end(&rate->watom);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int snd_pcm_rate_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)
|
|
|
|
|
{
|
|
|
|
|
snd_pcm_rate_hwsync(pcm);
|
2000-12-29 15:00:53 +00:00
|
|
|
if (pcm->stream == SND_PCM_STREAM_PLAYBACK)
|
2004-02-04 09:21:11 +00:00
|
|
|
*delayp = snd_pcm_mmap_playback_hw_avail(pcm);
|
2000-12-29 15:00:53 +00:00
|
|
|
else
|
2004-02-04 09:21:11 +00:00
|
|
|
*delayp = snd_pcm_mmap_capture_hw_avail(pcm);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int snd_pcm_rate_prepare(snd_pcm_t *pcm)
|
|
|
|
|
{
|
|
|
|
|
snd_pcm_rate_t *rate = pcm->private_data;
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
snd_atomic_write_begin(&rate->watom);
|
|
|
|
|
err = snd_pcm_prepare(rate->slave);
|
|
|
|
|
if (err < 0) {
|
|
|
|
|
snd_atomic_write_end(&rate->watom);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
*pcm->hw.ptr = 0;
|
|
|
|
|
*pcm->appl.ptr = 0;
|
|
|
|
|
snd_atomic_write_end(&rate->watom);
|
|
|
|
|
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;
|
|
|
|
|
snd_atomic_write_begin(&rate->watom);
|
|
|
|
|
err = snd_pcm_reset(rate->slave);
|
|
|
|
|
if (err < 0) {
|
|
|
|
|
snd_atomic_write_end(&rate->watom);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
*pcm->hw.ptr = 0;
|
|
|
|
|
*pcm->appl.ptr = 0;
|
|
|
|
|
snd_atomic_write_end(&rate->watom);
|
|
|
|
|
err = snd_pcm_rate_init(pcm);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int snd_pcm_rate_start(snd_pcm_t *pcm)
|
|
|
|
|
{
|
|
|
|
|
snd_pcm_rate_t *rate = pcm->private_data;
|
|
|
|
|
return snd_pcm_start(rate->slave);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int snd_pcm_rate_drop(snd_pcm_t *pcm)
|
|
|
|
|
{
|
|
|
|
|
snd_pcm_rate_t *rate = pcm->private_data;
|
|
|
|
|
return snd_pcm_drop(rate->slave);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int snd_pcm_rate_drain(snd_pcm_t *pcm)
|
|
|
|
|
{
|
|
|
|
|
snd_pcm_rate_t *rate = pcm->private_data;
|
|
|
|
|
return snd_pcm_drain(rate->slave);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int snd_pcm_rate_pause(snd_pcm_t *pcm, int enable)
|
|
|
|
|
{
|
|
|
|
|
snd_pcm_rate_t *rate = pcm->private_data;
|
|
|
|
|
return snd_pcm_pause(rate->slave, enable);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static snd_pcm_sframes_t snd_pcm_rate_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
|
|
|
|
|
{
|
|
|
|
|
snd_pcm_rate_t *rate = pcm->private_data;
|
|
|
|
|
snd_pcm_sframes_t n = snd_pcm_mmap_hw_avail(pcm);
|
|
|
|
|
|
|
|
|
|
if ((snd_pcm_uframes_t)n > frames)
|
|
|
|
|
frames = n;
|
|
|
|
|
if (frames == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
snd_atomic_write_begin(&rate->watom);
|
|
|
|
|
n = snd_pcm_rate_move_applptr(pcm, -frames);
|
|
|
|
|
snd_atomic_write_end(&rate->watom);
|
|
|
|
|
return n;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static snd_pcm_sframes_t snd_pcm_rate_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
|
|
|
|
|
{
|
|
|
|
|
snd_pcm_rate_t *rate = pcm->private_data;
|
|
|
|
|
snd_pcm_sframes_t n = snd_pcm_mmap_avail(pcm);
|
|
|
|
|
|
|
|
|
|
if ((snd_pcm_uframes_t)n > frames)
|
|
|
|
|
frames = n;
|
|
|
|
|
if (frames == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
snd_atomic_write_begin(&rate->watom);
|
|
|
|
|
n = snd_pcm_rate_move_applptr(pcm, frames);
|
|
|
|
|
snd_atomic_write_end(&rate->watom);
|
|
|
|
|
return n;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int snd_pcm_rate_resume(snd_pcm_t *pcm)
|
|
|
|
|
{
|
|
|
|
|
snd_pcm_rate_t *rate = pcm->private_data;
|
|
|
|
|
return snd_pcm_resume(rate->slave);
|
|
|
|
|
}
|
|
|
|
|
|
2004-03-17 11:48:14 +00:00
|
|
|
static int snd_pcm_rate_poll_ask(snd_pcm_t *pcm)
|
|
|
|
|
{
|
|
|
|
|
snd_pcm_rate_t *rate = pcm->private_data;
|
|
|
|
|
snd_pcm_uframes_t avail_min;
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
if (rate->slave->fast_ops->poll_ask) {
|
|
|
|
|
err = rate->slave->fast_ops->poll_ask(rate->slave->fast_op_arg);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
avail_min = rate->appl_ptr % pcm->period_size;
|
|
|
|
|
if (avail_min > 0) {
|
|
|
|
|
recalc(pcm, &avail_min);
|
|
|
|
|
if (avail_min < rate->slave->buffer_size &&
|
|
|
|
|
avail_min != rate->slave->period_size)
|
|
|
|
|
avail_min++; /* 1st small little rounding correction */
|
|
|
|
|
if (avail_min < rate->slave->buffer_size &&
|
|
|
|
|
avail_min != rate->slave->period_size)
|
|
|
|
|
avail_min++; /* 2nd small little rounding correction */
|
|
|
|
|
avail_min += rate->orig_avail_min;
|
|
|
|
|
} else {
|
|
|
|
|
avail_min = rate->orig_avail_min;
|
|
|
|
|
}
|
|
|
|
|
if (rate->sw_params.avail_min == avail_min)
|
|
|
|
|
return 0;
|
|
|
|
|
rate->sw_params.avail_min = avail_min;
|
|
|
|
|
return snd_pcm_sw_params(rate->slave, &rate->sw_params);
|
|
|
|
|
}
|
|
|
|
|
|
2004-02-04 09:21:11 +00:00
|
|
|
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;
|
|
|
|
|
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);
|
|
|
|
|
if (cont >= pcm->period_size) {
|
|
|
|
|
result = snd_pcm_mmap_begin(rate->slave, &slave_areas, &slave_offset, &slave_frames);
|
|
|
|
|
if (result < 0)
|
|
|
|
|
return result;
|
|
|
|
|
if (slave_frames < rate->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);
|
|
|
|
|
result = snd_pcm_mmap_commit(rate->slave, slave_offset, rate->slave->period_size);
|
|
|
|
|
if (result < (snd_pcm_sframes_t)rate->slave->period_size) {
|
|
|
|
|
if (result < 0)
|
|
|
|
|
return result;
|
|
|
|
|
result = snd_pcm_rewind(rate->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);
|
2004-02-08 14:18:06 +00:00
|
|
|
snd_pcm_areas_copy(rate->pareas, cont,
|
|
|
|
|
areas, 0,
|
|
|
|
|
pcm->channels, pcm->period_size - cont,
|
|
|
|
|
pcm->format);
|
2004-02-04 09:21:11 +00:00
|
|
|
|
|
|
|
|
snd_pcm_rate_write_areas1(pcm, rate->pareas, 0, rate->sareas, 0);
|
|
|
|
|
|
|
|
|
|
/* ok, commit first fragment */
|
|
|
|
|
result = snd_pcm_mmap_begin(rate->slave, &slave_areas, &slave_offset, &slave_frames);
|
|
|
|
|
if (result < 0)
|
|
|
|
|
return result;
|
|
|
|
|
__partial:
|
|
|
|
|
xfer = 0;
|
|
|
|
|
cont = rate->slave->buffer_size - slave_offset;
|
|
|
|
|
if (cont > rate->slave->period_size)
|
|
|
|
|
cont = rate->slave->period_size;
|
|
|
|
|
snd_pcm_areas_copy(slave_areas, slave_offset,
|
|
|
|
|
rate->sareas, 0,
|
|
|
|
|
pcm->channels, cont,
|
|
|
|
|
rate->slave->format);
|
|
|
|
|
result = snd_pcm_mmap_commit(rate->slave, slave_offset, cont);
|
|
|
|
|
if (result < (snd_pcm_sframes_t)cont) {
|
|
|
|
|
if (result < 0)
|
|
|
|
|
return result;
|
|
|
|
|
result = snd_pcm_rewind(rate->slave, result);
|
|
|
|
|
if (result < 0)
|
|
|
|
|
return result;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
xfer = cont;
|
|
|
|
|
|
|
|
|
|
if (xfer == rate->slave->period_size)
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
/* commit second fragment */
|
|
|
|
|
cont = rate->slave->period_size - cont;
|
|
|
|
|
slave_frames = cont;
|
|
|
|
|
result = snd_pcm_mmap_begin(rate->slave, &slave_areas, &slave_offset, &slave_frames);
|
|
|
|
|
if (result < 0)
|
|
|
|
|
return result;
|
|
|
|
|
assert(slave_offset == 0);
|
|
|
|
|
snd_pcm_areas_copy(slave_areas, slave_offset,
|
|
|
|
|
rate->sareas, xfer,
|
|
|
|
|
pcm->channels, cont,
|
|
|
|
|
rate->slave->format);
|
|
|
|
|
result = snd_pcm_mmap_commit(rate->slave, slave_offset, cont);
|
|
|
|
|
if (result < (snd_pcm_sframes_t)cont) {
|
|
|
|
|
if (result < 0)
|
|
|
|
|
return result;
|
|
|
|
|
result = snd_pcm_rewind(rate->slave, result + xfer);
|
|
|
|
|
if (result < 0)
|
|
|
|
|
return result;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
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->slave, &slave_areas, &slave_offset, &slave_frames);
|
|
|
|
|
if (result < 0)
|
|
|
|
|
return result;
|
|
|
|
|
if (slave_frames < rate->slave->period_size)
|
|
|
|
|
goto __partial;
|
|
|
|
|
snd_pcm_rate_read_areas1(pcm, areas, hw_offset,
|
|
|
|
|
slave_areas, slave_offset);
|
|
|
|
|
result = snd_pcm_mmap_commit(rate->slave, slave_offset, rate->slave->period_size);
|
|
|
|
|
if (result < (snd_pcm_sframes_t)rate->slave->period_size) {
|
|
|
|
|
if (result < 0)
|
|
|
|
|
return result;
|
|
|
|
|
result = snd_pcm_rewind(rate->slave, result);
|
|
|
|
|
if (result < 0)
|
|
|
|
|
return result;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
snd_pcm_rate_t *rate = pcm->private_data;
|
|
|
|
|
snd_pcm_uframes_t xfer;
|
|
|
|
|
|
|
|
|
|
/* ok, grab first fragment */
|
|
|
|
|
result = snd_pcm_mmap_begin(rate->slave, &slave_areas, &slave_offset, &slave_frames);
|
|
|
|
|
if (result < 0)
|
|
|
|
|
return result;
|
|
|
|
|
__partial:
|
|
|
|
|
xfer = 0;
|
|
|
|
|
cont = rate->slave->buffer_size - slave_offset;
|
2004-03-23 14:37:34 +00:00
|
|
|
if (cont > rate->slave->period_size)
|
|
|
|
|
cont = rate->slave->period_size;
|
2004-02-04 09:21:11 +00:00
|
|
|
snd_pcm_areas_copy(rate->sareas, 0,
|
|
|
|
|
slave_areas, slave_offset,
|
|
|
|
|
pcm->channels, cont,
|
|
|
|
|
rate->slave->format);
|
|
|
|
|
result = snd_pcm_mmap_commit(rate->slave, slave_offset, cont);
|
|
|
|
|
if (result < (snd_pcm_sframes_t)cont) {
|
|
|
|
|
if (result < 0)
|
|
|
|
|
return result;
|
|
|
|
|
result = snd_pcm_rewind(rate->slave, result);
|
|
|
|
|
if (result < 0)
|
|
|
|
|
return result;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
xfer = cont;
|
|
|
|
|
|
2004-02-08 14:18:06 +00:00
|
|
|
if (xfer == rate->slave->period_size)
|
|
|
|
|
goto __transfer;
|
|
|
|
|
|
2004-02-04 09:21:11 +00:00
|
|
|
/* grab second fragment */
|
|
|
|
|
cont = rate->slave->period_size - cont;
|
|
|
|
|
slave_frames = cont;
|
|
|
|
|
result = snd_pcm_mmap_begin(rate->slave, &slave_areas, &slave_offset, &slave_frames);
|
|
|
|
|
if (result < 0)
|
|
|
|
|
return result;
|
|
|
|
|
assert(slave_offset == 0);
|
|
|
|
|
snd_pcm_areas_copy(rate->sareas, xfer,
|
|
|
|
|
slave_areas, slave_offset,
|
|
|
|
|
pcm->channels, cont,
|
|
|
|
|
rate->slave->format);
|
|
|
|
|
result = snd_pcm_mmap_commit(rate->slave, slave_offset, cont);
|
|
|
|
|
if (result < (snd_pcm_sframes_t)cont) {
|
|
|
|
|
if (result < 0)
|
|
|
|
|
return result;
|
|
|
|
|
result = snd_pcm_rewind(rate->slave, result + xfer);
|
|
|
|
|
if (result < 0)
|
|
|
|
|
return result;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2004-02-08 14:18:06 +00:00
|
|
|
__transfer:
|
2004-02-04 09:21:11 +00:00
|
|
|
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 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;
|
|
|
|
|
snd_pcm_t *slave = rate->slave;
|
|
|
|
|
snd_pcm_uframes_t appl_offset, xfer;
|
|
|
|
|
snd_pcm_sframes_t slave_size;
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
if (size == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
if (pcm->stream == SND_PCM_STREAM_CAPTURE) {
|
|
|
|
|
snd_atomic_write_begin(&rate->watom);
|
|
|
|
|
snd_pcm_mmap_appl_forward(pcm, size);
|
|
|
|
|
snd_atomic_write_end(&rate->watom);
|
|
|
|
|
return size;
|
|
|
|
|
}
|
|
|
|
|
slave_size = snd_pcm_avail_update(slave);
|
|
|
|
|
if (slave_size < 0)
|
|
|
|
|
return slave_size;
|
|
|
|
|
xfer = rate->appl_ptr % pcm->period_size;
|
|
|
|
|
appl_offset = (rate->appl_ptr - xfer) % pcm->buffer_size;
|
|
|
|
|
xfer = pcm->period_size - xfer;
|
|
|
|
|
if (xfer >= size) {
|
2004-04-03 08:27:58 +00:00
|
|
|
if (xfer == size && (snd_pcm_uframes_t)slave_size >= rate->slave->period_size) {
|
2004-02-04 09:21:11 +00:00
|
|
|
err = snd_pcm_rate_commit_next_period(pcm, appl_offset);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
if (err == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
snd_atomic_write_begin(&rate->watom);
|
|
|
|
|
snd_pcm_mmap_appl_forward(pcm, size);
|
|
|
|
|
snd_atomic_write_end(&rate->watom);
|
|
|
|
|
return size;
|
|
|
|
|
} else {
|
2004-04-03 08:27:58 +00:00
|
|
|
if ((snd_pcm_uframes_t)slave_size >= rate->slave->period_size) {
|
|
|
|
|
err = snd_pcm_rate_commit_next_period(pcm, appl_offset);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
if (err == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2004-02-04 09:21:11 +00:00
|
|
|
snd_atomic_write_begin(&rate->watom);
|
|
|
|
|
snd_pcm_mmap_appl_forward(pcm, xfer);
|
|
|
|
|
snd_atomic_write_end(&rate->watom);
|
2004-02-08 14:18:06 +00:00
|
|
|
appl_offset += pcm->period_size;
|
|
|
|
|
appl_offset %= pcm->buffer_size;
|
2004-04-03 08:27:58 +00:00
|
|
|
size -= xfer;
|
|
|
|
|
slave_size -= rate->slave->period_size;
|
2004-02-04 09:21:11 +00:00
|
|
|
}
|
|
|
|
|
while ((snd_pcm_uframes_t)size >= pcm->period_size &&
|
|
|
|
|
(snd_pcm_uframes_t)slave_size >= rate->slave->period_size) {
|
|
|
|
|
err = snd_pcm_rate_commit_next_period(pcm, appl_offset);
|
|
|
|
|
if (err == 0)
|
|
|
|
|
return xfer;
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;
|
|
|
|
|
xfer += pcm->period_size;
|
|
|
|
|
size -= pcm->period_size;
|
|
|
|
|
slave_size -= rate->slave->period_size;
|
2004-02-08 14:18:06 +00:00
|
|
|
appl_offset += pcm->period_size;
|
|
|
|
|
appl_offset %= pcm->buffer_size;
|
2004-02-04 09:21:11 +00:00
|
|
|
snd_atomic_write_begin(&rate->watom);
|
|
|
|
|
snd_pcm_mmap_appl_forward(pcm, pcm->period_size);
|
|
|
|
|
snd_atomic_write_end(&rate->watom);
|
|
|
|
|
}
|
2004-02-04 18:29:41 +00:00
|
|
|
size %= pcm->period_size;
|
2004-02-04 09:21:11 +00:00
|
|
|
if (size > 0) {
|
|
|
|
|
snd_atomic_write_begin(&rate->watom);
|
|
|
|
|
snd_pcm_mmap_appl_forward(pcm, size);
|
|
|
|
|
snd_atomic_write_end(&rate->watom);
|
|
|
|
|
xfer += size;
|
|
|
|
|
}
|
|
|
|
|
return 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_t *slave = rate->slave;
|
|
|
|
|
snd_pcm_uframes_t slave_size;
|
|
|
|
|
|
|
|
|
|
slave_size = snd_pcm_avail_update(slave);
|
|
|
|
|
if (pcm->stream == SND_PCM_STREAM_CAPTURE)
|
|
|
|
|
goto _capture;
|
|
|
|
|
snd_atomic_write_begin(&rate->watom);
|
|
|
|
|
snd_pcm_rate_sync_hwptr(pcm);
|
|
|
|
|
snd_atomic_write_end(&rate->watom);
|
|
|
|
|
return snd_pcm_mmap_avail(pcm);
|
|
|
|
|
_capture: {
|
|
|
|
|
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 &&
|
|
|
|
|
slave_size >= rate->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 -= rate->slave->period_size;
|
2004-02-08 14:18:06 +00:00
|
|
|
hw_offset += pcm->period_size;
|
|
|
|
|
hw_offset %= pcm->buffer_size;
|
2004-02-04 09:21:11 +00:00
|
|
|
snd_pcm_mmap_hw_forward(pcm, pcm->period_size);
|
|
|
|
|
}
|
|
|
|
|
return (snd_pcm_sframes_t)xfer;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int snd_pcm_rate_mmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
|
|
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int snd_pcm_rate_munmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
|
|
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
snd_atomic_read_t ratom;
|
|
|
|
|
snd_atomic_read_init(&ratom, &rate->watom);
|
|
|
|
|
_again:
|
|
|
|
|
snd_atomic_read_begin(&ratom);
|
|
|
|
|
err = snd_pcm_status(rate->slave, status);
|
|
|
|
|
if (err < 0) {
|
|
|
|
|
snd_atomic_read_ok(&ratom);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
snd_pcm_rate_sync_hwptr(pcm);
|
|
|
|
|
status->appl_ptr = *pcm->appl.ptr;
|
|
|
|
|
status->hw_ptr = *pcm->hw.ptr;
|
|
|
|
|
if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
|
|
|
|
|
status->delay = snd_pcm_mmap_playback_hw_avail(pcm);
|
|
|
|
|
status->avail = snd_pcm_mmap_playback_avail(pcm);
|
|
|
|
|
} else {
|
|
|
|
|
status->delay = snd_pcm_mmap_capture_hw_avail(pcm);
|
|
|
|
|
status->avail = snd_pcm_mmap_capture_avail(pcm);
|
|
|
|
|
}
|
|
|
|
|
if (!snd_atomic_read_ok(&ratom)) {
|
|
|
|
|
snd_atomic_read_wait(&ratom);
|
|
|
|
|
goto _again;
|
|
|
|
|
}
|
|
|
|
|
status->avail_max = snd_pcm_rate_client_frames(pcm, (snd_pcm_sframes_t) status->avail_max);
|
|
|
|
|
return 0;
|
2000-12-29 15:00:53 +00:00
|
|
|
}
|
|
|
|
|
|
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
|
|
|
{
|
2001-02-11 15:45:35 +00:00
|
|
|
snd_pcm_rate_t *rate = pcm->private_data;
|
2001-02-05 15:44:42 +00:00
|
|
|
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 (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: ");
|
2004-02-04 09:21:11 +00:00
|
|
|
snd_pcm_dump(rate->slave, out);
|
2000-09-24 09:57:26 +00:00
|
|
|
}
|
|
|
|
|
|
2004-02-04 09:21:11 +00:00
|
|
|
static 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_rate_drop,
|
|
|
|
|
.drain = snd_pcm_rate_drain,
|
|
|
|
|
.pause = snd_pcm_rate_pause,
|
|
|
|
|
.rewind = snd_pcm_rate_rewind,
|
|
|
|
|
.forward = snd_pcm_rate_forward,
|
|
|
|
|
.resume = snd_pcm_rate_resume,
|
2004-03-17 11:48:14 +00:00
|
|
|
.poll_ask = snd_pcm_rate_poll_ask,
|
2004-02-04 09:21:11 +00:00
|
|
|
.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,
|
|
|
|
|
};
|
|
|
|
|
|
2002-01-22 14:27:25 +00:00
|
|
|
static snd_pcm_ops_t snd_pcm_rate_ops = {
|
2004-02-04 09:21:11 +00:00
|
|
|
.close = snd_pcm_rate_close,
|
|
|
|
|
.info = snd_pcm_rate_info,
|
2003-07-25 17:02:00 +00:00
|
|
|
.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,
|
2004-02-04 09:21:11 +00:00
|
|
|
.channel_info = snd_pcm_rate_channel_info,
|
2003-07-25 17:02:00 +00:00
|
|
|
.dump = snd_pcm_rate_dump,
|
2004-02-04 09:21:11 +00:00
|
|
|
.nonblock = snd_pcm_rate_nonblock,
|
|
|
|
|
.async = snd_pcm_rate_async,
|
|
|
|
|
.poll_revents = snd_pcm_rate_poll_revents,
|
|
|
|
|
.mmap = snd_pcm_rate_mmap,
|
|
|
|
|
.munmap = snd_pcm_rate_munmap,
|
2000-09-24 09:57:26 +00:00
|
|
|
};
|
|
|
|
|
|
2002-01-22 14:27:25 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \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 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.
|
|
|
|
|
*/
|
2001-03-29 17:50:28 +00:00
|
|
|
int snd_pcm_rate_open(snd_pcm_t **pcmp, const char *name, snd_pcm_format_t sformat, unsigned int srate, snd_pcm_t *slave, int close_slave)
|
2000-09-24 09:57:26 +00:00
|
|
|
{
|
2000-10-14 10:31:34 +00:00
|
|
|
snd_pcm_t *pcm;
|
2000-09-24 09:57:26 +00:00
|
|
|
snd_pcm_rate_t *rate;
|
2001-06-20 20:52:12 +00:00
|
|
|
int err;
|
2000-10-14 10:31:34 +00:00
|
|
|
assert(pcmp && slave);
|
2001-02-05 15:44:42 +00:00
|
|
|
if (sformat != SND_PCM_FORMAT_UNKNOWN &&
|
2001-02-04 17:03:17 +00:00
|
|
|
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;
|
|
|
|
|
}
|
2004-02-04 09:21:11 +00:00
|
|
|
rate->slave = slave;
|
|
|
|
|
rate->close_slave = close_slave;
|
2004-01-24 16:23:11 +00:00
|
|
|
rate->type = RATE_TYPE_LINEAR;
|
2000-11-20 20:10:46 +00:00
|
|
|
rate->srate = srate;
|
|
|
|
|
rate->sformat = sformat;
|
2004-02-04 09:21:11 +00:00
|
|
|
snd_atomic_write_init(&rate->watom);
|
2000-09-24 09:57:26 +00:00
|
|
|
|
2001-06-20 20:52:12 +00:00
|
|
|
err = snd_pcm_new(&pcm, SND_PCM_TYPE_RATE, name, slave->stream, slave->mode);
|
|
|
|
|
if (err < 0) {
|
2000-09-24 09:57:26 +00:00
|
|
|
free(rate);
|
2001-06-20 20:52:12 +00:00
|
|
|
return err;
|
2000-09-24 09:57:26 +00:00
|
|
|
}
|
2000-10-14 10:31:34 +00:00
|
|
|
pcm->ops = &snd_pcm_rate_ops;
|
2004-02-04 09:21:11 +00:00
|
|
|
pcm->fast_ops = &snd_pcm_rate_fast_ops;
|
2001-02-11 15:45:35 +00:00
|
|
|
pcm->private_data = rate;
|
2000-10-14 10:31:34 +00:00
|
|
|
pcm->poll_fd = slave->poll_fd;
|
2003-02-11 18:14:43 +00:00
|
|
|
pcm->poll_events = slave->poll_events;
|
2004-02-04 09:21:11 +00:00
|
|
|
pcm->mmap_rw = 1;
|
|
|
|
|
snd_pcm_set_hw_ptr(pcm, &rate->hw_ptr, -1, 0);
|
|
|
|
|
snd_pcm_set_appl_ptr(pcm, &rate->appl_ptr, -1, 0);
|
2000-10-14 10:31:34 +00:00
|
|
|
*pcmp = pcm;
|
2000-09-24 09:57:26 +00:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2002-01-22 14:27:25 +00:00
|
|
|
/*! \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
|
2002-01-22 14:27:25 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
\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.
|
|
|
|
|
*/
|
2001-02-11 15:45:35 +00:00
|
|
|
int _snd_pcm_rate_open(snd_pcm_t **pcmp, const char *name,
|
2001-06-11 13:35:48 +00:00
|
|
|
snd_config_t *root, snd_config_t *conf,
|
|
|
|
|
snd_pcm_stream_t stream, int mode)
|
2000-09-24 09:57:26 +00:00
|
|
|
{
|
2001-02-11 15:45:35 +00:00
|
|
|
snd_config_iterator_t i, next;
|
2000-09-24 09:57:26 +00:00
|
|
|
int err;
|
|
|
|
|
snd_pcm_t *spcm;
|
2001-05-14 15:44:37 +00:00
|
|
|
snd_config_t *slave = NULL, *sconf;
|
2001-02-05 15:44:42 +00:00
|
|
|
snd_pcm_format_t sformat = SND_PCM_FORMAT_UNKNOWN;
|
2001-03-17 16:34:43 +00:00
|
|
|
int srate = -1;
|
2001-02-11 15:45:35 +00:00
|
|
|
snd_config_for_each(i, next, conf) {
|
2001-02-07 11:34:33 +00:00
|
|
|
snd_config_t *n = snd_config_iterator_entry(i);
|
2001-11-19 08:14:21 +00:00
|
|
|
const char *id;
|
|
|
|
|
if (snd_config_get_id(n, &id) < 0)
|
|
|
|
|
continue;
|
2001-06-04 18:04:18 +00:00
|
|
|
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;
|
|
|
|
|
}
|
2001-03-04 20:39:02 +00:00
|
|
|
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
|
|
|
}
|
2001-06-15 08:47:59 +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,
|
2001-07-07 15:53:20 +00:00
|
|
|
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) {
|
2001-06-16 22:03:23 +00:00
|
|
|
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;
|
|
|
|
|
}
|
2001-06-15 08:47:59 +00:00
|
|
|
err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode);
|
2001-06-16 22:03:23 +00:00
|
|
|
snd_config_delete(sconf);
|
2000-09-24 09:57:26 +00:00
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
2001-03-29 17:50:28 +00:00
|
|
|
err = snd_pcm_rate_open(pcmp, name,
|
|
|
|
|
sformat, (unsigned int) srate, spcm, 1);
|
2000-09-24 09:57:26 +00:00
|
|
|
if (err < 0)
|
|
|
|
|
snd_pcm_close(spcm);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
2002-01-22 14:27:25 +00:00
|
|
|
#ifndef DOC_HIDDEN
|
2001-10-24 14:14:11 +00:00
|
|
|
SND_DLSYM_BUILD_VERSION(_snd_pcm_rate_open, SND_PCM_DLSYM_VERSION);
|
2002-01-22 14:27:25 +00:00
|
|
|
#endif
|