summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2006-04-06 18:37:55 +0200
committerTakashi Iwai <tiwai@suse.de>2006-04-06 18:37:55 +0200
commit33d69ef33be28a4d205a852462c25c67d2f51707 (patch)
tree18b8d96738df6476cfb24bdbafcbd57dda96b6d5
parent8f605df24aa972d606ab5fbd895c76b525f262bb (diff)
downloadalsa-lib-33d69ef33be28a4d205a852462c25c67d2f51707.tar.gz
Create rate converter plugin SDK
Created a new rate converter plugin SDK. A rate converter can be replaced as an extra plugin now. The default rate converter is a built-in linear converter. You can find a sample external converter in alsa-plugins package.
-rw-r--r--include/Makefile.am2
-rw-r--r--include/pcm_plugin.h2
-rw-r--r--include/pcm_rate.h117
-rw-r--r--src/pcm/Makefile.am2
-rw-r--r--src/pcm/pcm_local.h2
-rw-r--r--src/pcm/pcm_plug.c28
-rw-r--r--src/pcm/pcm_rate.c752
-rw-r--r--src/pcm/pcm_rate_linear.c435
8 files changed, 879 insertions, 461 deletions
diff --git a/include/Makefile.am b/include/Makefile.am
index 307e7c54..7782f79c 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -10,7 +10,7 @@ alsainclude_HEADERS = asoundlib.h asoundef.h \
seq_event.h seq.h seqmid.h seq_midi_event.h \
conv.h instr.h iatomic.h \
alisp.h pcm_external.h pcm_ioplug.h pcm_extplug.h \
- control_external.h
+ pcm_rate.h control_external.h
noinst_HEADERS = alsa sys.h search.h list.h aserver.h local.h alsa-symbols.h
diff --git a/include/pcm_plugin.h b/include/pcm_plugin.h
index 374ba710..7d48527e 100644
--- a/include/pcm_plugin.h
+++ b/include/pcm_plugin.h
@@ -156,7 +156,7 @@ int _snd_pcm_route_open(snd_pcm_t **pcmp, const char *name,
* Rate plugin for linear formats
*/
int snd_pcm_rate_open(snd_pcm_t **pcmp, const char *name,
- snd_pcm_format_t sformat, unsigned int srate,
+ snd_pcm_format_t sformat, unsigned int srate, const char *converter,
snd_pcm_t *slave, int close_slave);
int _snd_pcm_rate_open(snd_pcm_t **pcmp, const char *name,
snd_config_t *root, snd_config_t *conf,
diff --git a/include/pcm_rate.h b/include/pcm_rate.h
new file mode 100644
index 00000000..d211e097
--- /dev/null
+++ b/include/pcm_rate.h
@@ -0,0 +1,117 @@
+/**
+ * \file include/pcm_rate.h
+ * \brief External Rate-Converter-Plugin SDK
+ * \author Takashi Iwai <tiwai@suse.de>
+ * \date 2006
+ *
+ * External Rate-Converter-Plugin SDK
+ */
+
+/*
+ * ALSA external PCM rate-converter plugin SDK (draft version)
+ *
+ * Copyright (c) 2006 Takashi Iwai <tiwai@suse.de>
+ *
+ * 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
+ * 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef __ALSA_PCM_RATE_H
+#define __ALSA_PCM_RATE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Protocol version
+ */
+#define SND_PCM_RATE_PLUGIN_VERSION 0x010001
+
+/** hw_params information for a single side */
+typedef struct snd_pcm_rate_side_info {
+ snd_pcm_format_t format;
+ unsigned int rate;
+ snd_pcm_uframes_t buffer_size;
+ snd_pcm_uframes_t period_size;
+} snd_pcm_rate_side_info_t;
+
+/** hw_params information */
+typedef struct snd_pcm_rate_info {
+ struct snd_pcm_rate_side_info in;
+ struct snd_pcm_rate_side_info out;
+ unsigned int channels;
+} snd_pcm_rate_info_t;
+
+/** Callback table of rate-converter */
+typedef struct snd_pcm_rate_ops {
+ /**
+ * close the converter; optional
+ */
+ void (*close)(void *obj);
+ /**
+ * initialize the converter, called at hw_params
+ */
+ int (*init)(void *obj, snd_pcm_rate_info_t *info);
+ /**
+ * free the converter; optional
+ */
+ void (*free)(void *obj);
+ /**
+ * reset the converter, called at prepare; optional
+ */
+ void (*reset)(void *obj);
+ /**
+ * adjust the pitch, called at sw_params; optional
+ */
+ int (*adjust_pitch)(void *obj, snd_pcm_rate_info_t *info);
+ /**
+ * convert the data
+ */
+ void (*convert)(void *obj,
+ 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);
+ /**
+ * convert an s16 interleaved-data array; exclusive with convert
+ */
+ void (*convert_s16)(void *obj, int16_t *dst, unsigned int dst_frames,
+ const int16_t *src, unsigned int src_frames);
+ /**
+ * compute the frame size for input
+ */
+ snd_pcm_uframes_t (*input_frames)(void *obj, snd_pcm_uframes_t frames);
+ /**
+ * compute the frame size for output
+ */
+ snd_pcm_uframes_t (*output_frames)(void *obj, snd_pcm_uframes_t frames);
+} snd_pcm_rate_ops_t;
+
+/** open function type */
+typedef int (*snd_pcm_rate_open_func_t)(unsigned int version, void **objp,
+ snd_pcm_rate_ops_t *opsp);
+
+/**
+ * Define the object entry for external PCM rate-converter plugins
+ */
+#define SND_PCM_RATE_PLUGIN_ENTRY(name) _snd_pcm_rate_##name##_open
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ALSA_PCM_RATE_H */
diff --git a/src/pcm/Makefile.am b/src/pcm/Makefile.am
index 0e032b78..990ad330 100644
--- a/src/pcm/Makefile.am
+++ b/src/pcm/Makefile.am
@@ -29,7 +29,7 @@ if BUILD_PCM_PLUGIN_ADPCM
libpcm_la_SOURCES += pcm_adpcm.c
endif
if BUILD_PCM_PLUGIN_RATE
-libpcm_la_SOURCES += pcm_rate.c
+libpcm_la_SOURCES += pcm_rate.c pcm_rate_linear.c
endif
if BUILD_PCM_PLUGIN_PLUG
libpcm_la_SOURCES += pcm_plug.c
diff --git a/src/pcm/pcm_local.h b/src/pcm/pcm_local.h
index e94125ad..be17e7a2 100644
--- a/src/pcm/pcm_local.h
+++ b/src/pcm/pcm_local.h
@@ -755,6 +755,8 @@ int snd_pcm_hw_open_fd(snd_pcm_t **pcmp, const char *name, int fd, int mmap_emul
int snd_pcm_wait_nocheck(snd_pcm_t *pcm, int timeout);
+const char *snd_pcm_rate_get_default_converter(snd_config_t *root);
+
#define SND_PCM_HW_PARBIT_ACCESS (1U << SND_PCM_HW_PARAM_ACCESS)
#define SND_PCM_HW_PARBIT_FORMAT (1U << SND_PCM_HW_PARAM_FORMAT)
#define SND_PCM_HW_PARBIT_SUBFORMAT (1U << SND_PCM_HW_PARAM_SUBFORMAT)
diff --git a/src/pcm/pcm_plug.c b/src/pcm/pcm_plug.c
index 98ee6835..58a165a9 100644
--- a/src/pcm/pcm_plug.c
+++ b/src/pcm/pcm_plug.c
@@ -50,6 +50,7 @@ typedef struct {
snd_pcm_format_t sformat;
int schannels;
int srate;
+ const char *rate_converter;
enum snd_pcm_plug_route_policy route_policy;
snd_pcm_route_ttable_entry_t *ttable;
int ttable_ok, ttable_last;
@@ -359,7 +360,8 @@ static int snd_pcm_plug_change_rate(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_plu
if (clt->rate == slv->rate)
return 0;
assert(snd_pcm_format_linear(slv->format));
- err = snd_pcm_rate_open(new, NULL, slv->format, slv->rate, plug->gen.slave, plug->gen.slave != plug->req_slave);
+ err = snd_pcm_rate_open(new, NULL, slv->format, slv->rate, plug->rate_converter,
+ plug->gen.slave, plug->gen.slave != plug->req_slave);
if (err < 0)
return err;
slv->access = clt->access;
@@ -1013,6 +1015,7 @@ static snd_pcm_ops_t snd_pcm_plug_ops = {
int snd_pcm_plug_open(snd_pcm_t **pcmp,
const char *name,
snd_pcm_format_t sformat, int schannels, int srate,
+ const char *rate_converter,
enum snd_pcm_plug_route_policy route_policy,
snd_pcm_route_ttable_entry_t *ttable,
unsigned int tt_ssize,
@@ -1030,6 +1033,7 @@ int snd_pcm_plug_open(snd_pcm_t **pcmp,
plug->sformat = sformat;
plug->schannels = schannels;
plug->srate = srate;
+ plug->rate_converter = rate_converter;
plug->gen.slave = plug->req_slave = slave;
plug->gen.close_slave = close_slave;
plug->route_policy = route_policy;
@@ -1087,6 +1091,8 @@ pcm.name {
SCHANNEL REAL # route value (0.0 - 1.0)
}
}
+ rate_converter STR # type of rate converter
+ # default value is taken from defaults.pcm.rate_converter
}
\endcode
@@ -1127,6 +1133,8 @@ int _snd_pcm_plug_open(snd_pcm_t **pcmp, const char *name,
unsigned int cused, sused;
snd_pcm_format_t sformat = SND_PCM_FORMAT_UNKNOWN;
int schannels = -1, srate = -1;
+ const char *rate_converter = NULL;
+
snd_config_for_each(i, next, conf) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id;
@@ -1167,6 +1175,17 @@ int _snd_pcm_plug_open(snd_pcm_t **pcmp, const char *name,
continue;
}
#endif
+#ifdef BUILD_PCM_PLUGIN_RATE
+ if (strcmp(id, "rate_converter") == 0) {
+ const char *str;
+ if ((err = snd_config_get_string(n, &str)) < 0) {
+ SNDERR("Invalid type for %s", id);
+ return -EINVAL;
+ }
+ rate_converter = str;
+ continue;
+ }
+#endif
SNDERR("Unknown field %s", id);
return -EINVAL;
}
@@ -1200,11 +1219,16 @@ int _snd_pcm_plug_open(snd_pcm_t **pcmp, const char *name,
}
#endif
+#ifdef BUILD_PCM_PLUGIN_RATE
+ if (! rate_converter)
+ rate_converter = snd_pcm_rate_get_default_converter(root);
+#endif
+
err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
snd_config_delete(sconf);
if (err < 0)
return err;
- err = snd_pcm_plug_open(pcmp, name, sformat, schannels, srate,
+ err = snd_pcm_plug_open(pcmp, name, sformat, schannels, srate, rate_converter,
route_policy, ttable, ssize, cused, sused, spcm, 1);
if (err < 0)
snd_pcm_close(spcm);
diff --git a/src/pcm/pcm_rate.c b/src/pcm/pcm_rate.c
index 423ef24c..3c65e2ca 100644
--- a/src/pcm/pcm_rate.c
+++ b/src/pcm/pcm_rate.c
@@ -29,8 +29,10 @@
*/
#include <inttypes.h>
#include <byteswap.h>
+#include <dlfcn.h>
#include "pcm_local.h"
#include "pcm_plugin.h"
+#include "pcm_rate.h"
#include "iatomic.h"
#include "plugin_ops.h"
@@ -46,27 +48,8 @@ const char *_snd_module_pcm_rate = "";
#ifndef DOC_HIDDEN
-/* LINEAR_DIV needs to be large enough to handle resampling from 192000 -> 8000 */
-#define LINEAR_DIV_SHIFT 19
-#define LINEAR_DIV (1<<LINEAR_DIV_SHIFT)
-
-enum rate_type {
- RATE_TYPE_LINEAR, /* linear interpolation */
- RATE_TYPE_BANDLIMIT, /* bandlimited interpolation */
- RATE_TYPE_POLYPHASE, /* polyphase resampling */
-};
-
typedef struct _snd_pcm_rate snd_pcm_rate_t;
-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,
- snd_pcm_rate_t *rate);
-
struct _snd_pcm_rate {
snd_pcm_generic_t gen;
snd_atomic_write_t watom;
@@ -74,290 +57,21 @@ struct _snd_pcm_rate {
snd_pcm_uframes_t last_commit_ptr;
snd_pcm_uframes_t orig_avail_min;
snd_pcm_sw_params_t sw_params;
- enum rate_type type;
- unsigned int get_idx;
- unsigned int put_idx;
- unsigned int pitch;
- unsigned int pitch_shift; /* for expand interpolation */
- rate_f func;
snd_pcm_format_t sformat;
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) */
- int16_t *old_sample;
+ snd_pcm_rate_info_t info;
+ void *obj;
+ snd_pcm_rate_ops_t ops;
+ unsigned int get_idx;
+ unsigned int put_idx;
+ int16_t *src_buf;
+ int16_t *dst_buf;
};
-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,
- snd_pcm_rate_t *rate)
-{
-#define GET16_LABELS
-#define PUT16_LABELS
-#include "plugin_ops.h"
-#undef GET16_LABELS
-#undef PUT16_LABELS
- void *get = get16_labels[rate->get_idx];
- void *put = put16_labels[rate->put_idx];
- unsigned int get_threshold = rate->pitch;
- unsigned int channel;
- snd_pcm_uframes_t src_frames1;
- snd_pcm_uframes_t dst_frames1;
- int16_t sample = 0;
- unsigned int pos;
-
- for (channel = 0; channel < channels; ++channel) {
- const snd_pcm_channel_area_t *src_area = &src_areas[channel];
- const snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
- const char *src;
- char *dst;
- int src_step, dst_step;
- int16_t old_sample = 0;
- int16_t new_sample;
- int old_weight, new_weight;
- 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;
- new_sample = rate->old_sample[channel];
- pos = get_threshold;
- while (dst_frames1 < dst_frames) {
- if (pos >= get_threshold) {
- pos -= get_threshold;
- old_sample = new_sample;
- if (src_frames1 < src_frames) {
- goto *get;
-#define GET16_END after_get
-#include "plugin_ops.h"
-#undef GET16_END
- after_get:
- new_sample = sample;
- }
- }
- new_weight = (pos << (16 - rate->pitch_shift)) / (get_threshold >> rate->pitch_shift);
- old_weight = 0x10000 - new_weight;
- sample = (old_sample * old_weight + new_sample * new_weight) >> 16;
- goto *put;
-#define PUT16_END after_put
-#include "plugin_ops.h"
-#undef PUT16_END
- after_put:
- dst += dst_step;
- dst_frames1++;
- pos += LINEAR_DIV;
- if (pos >= get_threshold) {
- src += src_step;
- src_frames1++;
- }
- }
- rate->old_sample[channel] = new_sample;
- }
-}
-
-/* optimized version for S16 format */
-static void snd_pcm_rate_expand_s16(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,
- snd_pcm_rate_t *rate)
-{
- unsigned int channel;
- snd_pcm_uframes_t src_frames1;
- snd_pcm_uframes_t dst_frames1;
- unsigned int get_threshold = rate->pitch;
- unsigned int pos;
-
- for (channel = 0; channel < channels; ++channel) {
- const snd_pcm_channel_area_t *src_area = &src_areas[channel];
- const snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
- const int16_t *src;
- int16_t *dst;
- int src_step, dst_step;
- int16_t old_sample = 0;
- int16_t new_sample;
- int old_weight, new_weight;
- 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) >> 1;
- dst_step = snd_pcm_channel_area_step(dst_area) >> 1;
- src_frames1 = 0;
- dst_frames1 = 0;
- new_sample = rate->old_sample[channel];
- pos = get_threshold;
- while (dst_frames1 < dst_frames) {
- if (pos >= get_threshold) {
- pos -= get_threshold;
- old_sample = new_sample;
- if (src_frames1 < src_frames)
- new_sample = *src;
- }
- new_weight = (pos << (16 - rate->pitch_shift)) / (get_threshold >> rate->pitch_shift);
- old_weight = 0x10000 - new_weight;
- *dst = (old_sample * old_weight + new_sample * new_weight) >> 16;
- dst += dst_step;
- dst_frames1++;
- pos += LINEAR_DIV;
- if (pos >= get_threshold) {
- src += src_step;
- src_frames1++;
- }
- }
- rate->old_sample[channel] = new_sample;
- }
-}
-
-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,
- snd_pcm_rate_t *rate)
-{
-#define GET16_LABELS
-#define PUT16_LABELS
-#include "plugin_ops.h"
-#undef GET16_LABELS
-#undef PUT16_LABELS
- void *get = get16_labels[rate->get_idx];
- void *put = put16_labels[rate->put_idx];
- unsigned int get_increment = rate->pitch;
- unsigned int channel;
- snd_pcm_uframes_t src_frames1;
- snd_pcm_uframes_t dst_frames1;
- int16_t sample = 0;
- unsigned int pos;
-
- for (channel = 0; channel < channels; ++channel) {
- const snd_pcm_channel_area_t *src_area = &src_areas[channel];
- const snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
- const char *src;
- char *dst;
- int src_step, dst_step;
- int16_t old_sample = 0;
- int16_t new_sample = 0;
- int old_weight, new_weight;
- pos = LINEAR_DIV - get_increment; /* Force first sample to be copied */
- 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;
-#define GET16_END after_get
-#include "plugin_ops.h"
-#undef GET16_END
- after_get:
- new_sample = sample;
- src += src_step;
- src_frames1++;
- pos += get_increment;
- if (pos >= LINEAR_DIV) {
- pos -= LINEAR_DIV;
- old_weight = (pos << (32 - LINEAR_DIV_SHIFT)) / (get_increment >> (LINEAR_DIV_SHIFT - 16));
- new_weight = 0x10000 - old_weight;
- sample = (old_sample * old_weight + new_sample * new_weight) >> 16;
- goto *put;
-#define PUT16_END after_put
-#include "plugin_ops.h"
-#undef PUT16_END
- after_put:
- dst += dst_step;
- dst_frames1++;
- if (CHECK_SANITY(dst_frames1 > dst_frames)) {
- SNDERR("dst_frames overflow");
- break;
- }
- }
- old_sample = new_sample;
- }
- }
-}
-
-/* optimized version for S16 format */
-static void snd_pcm_rate_shrink_s16(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,
- snd_pcm_rate_t *rate)
-{
- unsigned int get_increment = rate->pitch;
- unsigned int channel;
- snd_pcm_uframes_t src_frames1;
- snd_pcm_uframes_t dst_frames1;
- unsigned int pos = 0;
-
- for (channel = 0; channel < channels; ++channel) {
- const snd_pcm_channel_area_t *src_area = &src_areas[channel];
- const snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
- const int16_t *src;
- int16_t *dst;
- int src_step, dst_step;
- int16_t old_sample = 0;
- int16_t new_sample = 0;
- int old_weight, new_weight;
- pos = LINEAR_DIV - get_increment; /* Force first sample to be copied */
- 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) >> 1;
- dst_step = snd_pcm_channel_area_step(dst_area) >> 1 ;
- src_frames1 = 0;
- dst_frames1 = 0;
- while (src_frames1 < src_frames) {
-
- new_sample = *src;
- src += src_step;
- src_frames1++;
- pos += get_increment;
- if (pos >= LINEAR_DIV) {
- pos -= LINEAR_DIV;
- old_weight = (pos << (32 - LINEAR_DIV_SHIFT)) / (get_increment >> (LINEAR_DIV_SHIFT - 16));
- new_weight = 0x10000 - old_weight;
- *dst = (old_sample * old_weight + new_sample * new_weight) >> 16;
- dst += dst_step;
- dst_frames1++;
- if (CHECK_SANITY(dst_frames1 > dst_frames)) {
- SNDERR("dst_frames overflow");
- break;
- }
- }
- old_sample = new_sample;
- }
- }
-}
-
#endif /* DOC_HIDDEN */
-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_near(frames, LINEAR_DIV, rate->pitch);
- else
- return muldiv_near(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_near(frames, rate->pitch, LINEAR_DIV);
- else
- return muldiv_near(frames, LINEAR_DIV, rate->pitch);
-}
-
static int snd_pcm_rate_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params)
{
int err;
@@ -516,9 +230,8 @@ 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_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;
+ snd_pcm_rate_side_info_t *sinfo, *cinfo;
+ unsigned int channels, cwidth, swidth, chn;
int err = snd_pcm_hw_params_slave(pcm, params,
snd_pcm_rate_hw_refine_cchange,
snd_pcm_rate_hw_refine_sprepare,
@@ -528,85 +241,86 @@ static int snd_pcm_rate_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
return err;
if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
- err = INTERNAL(snd_pcm_hw_params_get_format)(params, &src_format);
- if (err < 0)
- return err;
- pformat = src_format;
- dst_format = slave->format;
- sformat = dst_format;
- dst_rate = slave->rate;
- err = INTERNAL(snd_pcm_hw_params_get_rate)(params, &src_rate, 0);
+ cinfo = &rate->info.in;
+ sinfo = &rate->info.out;
} else {
- sformat = src_format = slave->format;
- err = INTERNAL(snd_pcm_hw_params_get_format)(params, &dst_format);
- if (err < 0)
- return err;
- pformat = dst_format;
- src_rate = slave->rate;
- err = INTERNAL(snd_pcm_hw_params_get_rate)(params, &dst_rate, 0);
+ sinfo = &rate->info.in;
+ cinfo = &rate->info.out;
}
+ 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);
if (err < 0)
return err;
- err = INTERNAL(snd_pcm_hw_params_get_period_size)(params, &period_size, 0);
+ err = INTERNAL(snd_pcm_hw_params_get_period_size)(params, &cinfo->period_size, 0);
if (err < 0)
return err;
- err = INTERNAL(snd_pcm_hw_params_get_buffer_size)(params, &buffer_size);
+ 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);
if (err < 0)
return err;
- 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);
- if (src_rate < dst_rate) {
- if (src_format == dst_format && src_format == SND_PCM_FORMAT_S16)
- rate->func = snd_pcm_rate_expand_s16;
- else
- rate->func = snd_pcm_rate_expand;
- /* pitch is get_threshold */
- } else {
- if (src_format == dst_format && src_format == SND_PCM_FORMAT_S16)
- rate->func = snd_pcm_rate_shrink_s16;
- else
- rate->func = snd_pcm_rate_shrink;
- /* pitch is get_increment */
- }
- rate->pitch = (((u_int64_t)dst_rate * LINEAR_DIV) + (src_rate / 2)) / src_rate;
+
+ 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;
}
-#if 0
- if ((buffer_size / period_size) * period_size == buffer_size &&
- (slave->buffer_size / slave->period_size) * slave->period_size == slave->buffer_size)
- return 0;
-#endif
+ err = rate->ops.init(rate->obj, &rate->info);
+ if (err < 0)
+ return err;
+
rate->pareas = malloc(2 * channels * sizeof(*rate->pareas));
if (rate->pareas == NULL)
- return -ENOMEM;
- free(rate->old_sample);
- rate->old_sample = malloc(sizeof(*rate->old_sample) * channels);
- if (rate->old_sample == 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;
- }
+ goto error;
+
+ cwidth = snd_pcm_format_physical_width(cinfo->format);
+ swidth = snd_pcm_format_physical_width(sinfo->format);
+ rate->pareas[0].addr = malloc(((cwidth * channels * cinfo->period_size) / 8) +
+ ((swidth * channels * sinfo->period_size) / 8));
+ if (rate->pareas[0].addr == NULL)
+ goto error;
+
rate->sareas = rate->pareas + channels;
- rate->sareas[0].addr = (char *)rate->pareas[0].addr + ((pwidth * channels * period_size) / 8);
+ rate->sareas[0].addr = (char *)rate->pareas[0].addr + ((cwidth * channels * cinfo->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].addr = rate->pareas[0].addr + (cwidth * chn * cinfo->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->pareas[chn].step = cwidth;
+ rate->sareas[chn].addr = rate->sareas[0].addr + (swidth * chn * sinfo->period_size) / 8;
rate->sareas[chn].first = 0;
rate->sareas[chn].step = swidth;
}
+
+ if (rate->ops.convert_s16) {
+ rate->get_idx = snd_pcm_linear_get_index(rate->info.in.format, SND_PCM_FORMAT_S16);
+ rate->put_idx = snd_pcm_linear_put_index(SND_PCM_FORMAT_S16, rate->info.out.format);
+ free(rate->src_buf);
+ rate->src_buf = malloc(channels * rate->info.in.period_size * 2);
+ free(rate->dst_buf);
+ rate->dst_buf = malloc(channels * rate->info.out.period_size * 2);
+ if (! rate->src_buf || ! rate->dst_buf)
+ goto error;
+ }
+
return 0;
+
+ error:
+ if (rate->pareas) {
+ free(rate->pareas[0].addr);
+ free(rate->pareas);
+ rate->pareas = NULL;
+ }
+ if (rate->ops.free)
+ rate->ops.free(rate->obj);
+ return -ENOMEM;
}
static int snd_pcm_rate_hw_free(snd_pcm_t *pcm)
@@ -618,8 +332,11 @@ static int snd_pcm_rate_hw_free(snd_pcm_t *pcm)
rate->pareas = NULL;
rate->sareas = NULL;
}
- free(rate->old_sample);
- rate->old_sample = NULL;
+ if (rate->ops.free)
+ rate->ops.free(rate->obj);
+ free(rate->src_buf);
+ free(rate->dst_buf);
+ rate->src_buf = rate->dst_buf = NULL;
return snd_pcm_hw_free(rate->gen.slave);
}
@@ -649,86 +366,19 @@ static int snd_pcm_rate_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params)
rate->sw_params = *params;
sparams = &rate->sw_params;
- if ((rate->pitch >= LINEAR_DIV ? 1 : 0) ^ (pcm->stream == SND_PCM_STREAM_CAPTURE ? 1 : 0)) {
- 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;
- }
+ 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 = boundary2;
- if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
- rate->pitch = (((u_int64_t)slave->period_size * LINEAR_DIV) + (pcm->period_size/2) ) / pcm->period_size;
- do {
- snd_pcm_uframes_t cframes,cframes_test;
-
- cframes = snd_pcm_rate_client_frames(pcm, slave->period_size );
- if (cframes == pcm->period_size )
- break;
- if (cframes > pcm->period_size ) {
- rate->pitch++;
- cframes_test = snd_pcm_rate_client_frames(pcm, slave->period_size );
- if (cframes_test < pcm->period_size ) {
- SNDERR("Unable to satisfy pitch condition (%i/%i - %li/%li)\n", slave->rate, pcm->rate, slave->period_size, pcm->period_size);
- return -EIO;
- }
- } else {
- rate->pitch--;
- cframes_test = snd_pcm_rate_client_frames(pcm, slave->period_size );
- if (cframes_test > pcm->period_size) {
- SNDERR("Unable to satisfy pitch condition (%i/%i - %li/%li)\n", slave->rate, pcm->rate, slave->period_size, pcm->period_size);
- return -EIO;
- }
- }
- } while (1);
- if ((snd_pcm_uframes_t)snd_pcm_rate_client_frames(pcm, slave->period_size ) != pcm->period_size) {
- SNDERR("invalid slave period_size %ld for pcm period_size %ld",
- slave->period_size, pcm->period_size);
- return -EIO;
- }
- } else {
- rate->pitch = (((u_int64_t)pcm->period_size * LINEAR_DIV) + (slave->period_size/2) ) / slave->period_size;
- do {
- snd_pcm_uframes_t cframes;
-
- cframes = snd_pcm_rate_slave_frames(pcm, pcm->period_size );
- if (cframes == slave->period_size )
- break;
- if (cframes > slave->period_size ) {
- rate->pitch++;
- if ((snd_pcm_uframes_t)snd_pcm_rate_slave_frames(pcm, pcm->period_size ) < slave->period_size ) {
- SNDERR("Unable to satisfy pitch condition (%i/%i - %li/%li)\n", slave->rate, pcm->rate, slave->period_size, pcm->period_size);
- return -EIO;
- }
- } else {
- rate->pitch--;
- if ((snd_pcm_uframes_t)snd_pcm_rate_slave_frames(pcm, pcm->period_size) > slave->period_size ) {
- SNDERR("Unable to satisfy pitch condition (%i/%i - %li/%li)\n", slave->rate, pcm->rate, slave->period_size , pcm->period_size );
- return -EIO;
- }
- }
- } while (1);
- if ((snd_pcm_uframes_t)snd_pcm_rate_slave_frames(pcm, pcm->period_size ) != slave->period_size) {
- SNDERR("invalid pcm period_size %ld for slave period_size",
- pcm->period_size, slave->period_size);
- return -EIO;
- }
- }
- if (rate->pitch >= LINEAR_DIV) {
- /* shift for expand linear interpolation */
- rate->pitch_shift = 0;
- while ((rate->pitch >> rate->pitch_shift) >= (1 << 16))
- rate->pitch_shift++;
- }
+
+ 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->xfer_align);
@@ -754,20 +404,113 @@ static int snd_pcm_rate_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params)
static int snd_pcm_rate_init(snd_pcm_t *pcm)
{
snd_pcm_rate_t *rate = pcm->private_data;
- switch (rate->type) {
- case RATE_TYPE_LINEAR:
- /* for expand */
- if (rate->old_sample)
- memset(rate->old_sample, 0, sizeof(*rate->old_sample) * pcm->channels);
- break;
- default:
- assert(0);
- }
+
+ if (rate->ops.reset)
+ rate->ops.reset(rate->obj);
rate->last_commit_ptr = 0;
return 0;
}
-static inline int
+static void convert_to_s16(snd_pcm_rate_t *rate, int16_t *buf,
+ const snd_pcm_channel_area_t *areas,
+ snd_pcm_uframes_t offset, unsigned int frames,
+ unsigned int channels)
+{
+#define GET16_LABELS
+#include "plugin_ops.h"
+#undef GET16_LABELS
+ void *get = get16_labels[rate->get_idx];
+ const char *src;
+ int16_t sample;
+ const char *srcs[channels];
+ int src_step[channels];
+ unsigned int c;
+
+ for (c = 0; c < channels; c++) {
+ srcs[c] = snd_pcm_channel_area_addr(areas + c, offset);
+ src_step[c] = snd_pcm_channel_area_step(areas + c);
+ }
+
+ while (frames--) {
+ for (c = 0; c < channels; c++) {
+ src = srcs[c];
+ goto *get;
+#define GET16_END after_get
+#include "plugin_ops.h"
+#undef GET16_END
+ after_get:
+ *buf++ = sample;
+ srcs[c] += src_step[c];
+ }
+ }
+}
+
+static void convert_from_s16(snd_pcm_rate_t *rate, const int16_t *buf,
+ const snd_pcm_channel_area_t *areas,
+ snd_pcm_uframes_t offset, unsigned int frames,
+ unsigned int channels)
+{
+#define PUT16_LABELS
+#include "plugin_ops.h"
+#undef PUT16_LABELS
+ void *put = put16_labels[rate->put_idx];
+ char *dst;
+ int16_t sample;
+ char *dsts[channels];
+ int dst_step[channels];
+ unsigned int c;
+
+ for (c = 0; c < channels; c++) {
+ dsts[c] = snd_pcm_channel_area_addr(areas + c, offset);
+ dst_step[c] = snd_pcm_channel_area_step(areas + c);
+ }
+
+ while (frames--) {
+ for (c = 0; c < channels; c++) {
+ dst = dsts[c];
+ sample = *buf++;
+ goto *put;
+#define PUT16_END after_put
+#include "plugin_ops.h"
+#undef PUT16_END
+ after_put:
+ dsts[c] += dst_step[c];
+ }
+ }
+}
+
+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)
+{
+ if (rate->ops.convert_s16) {
+ const int16_t *src;
+ int16_t *dst;
+ if (! rate->src_buf)
+ src = src_areas->addr + src_offset * 2 * channels;
+ else {
+ convert_to_s16(rate, rate->src_buf, src_areas, src_offset,
+ src_frames, channels);
+ src = rate->src_buf;
+ }
+ if (! rate->dst_buf)
+ dst = dst_areas->addr + dst_offset * 2 * channels;
+ else
+ dst = rate->dst_buf;
+ rate->ops.convert_s16(rate->obj, dst, dst_frames, src, src_frames);
+ if (dst == rate->dst_buf)
+ convert_from_s16(rate, rate->dst_buf, dst_areas, dst_offset,
+ dst_frames, channels);
+ } else {
+ rate->ops.convert(rate->obj, dst_areas, dst_offset, dst_frames,
+ src_areas, src_offset, src_frames);
+ }
+}
+
+static inline void
snd_pcm_rate_write_areas1(snd_pcm_t *pcm,
const snd_pcm_channel_area_t *areas,
snd_pcm_uframes_t offset,
@@ -775,13 +518,12 @@ snd_pcm_rate_write_areas1(snd_pcm_t *pcm,
snd_pcm_uframes_t slave_offset)
{
snd_pcm_rate_t *rate = pcm->private_data;
- rate->func(slave_areas, slave_offset, rate->gen.slave->period_size,
+ do_convert(slave_areas, slave_offset, rate->gen.slave->period_size,
areas, offset, pcm->period_size,
pcm->channels, rate);
- return 0;
}
-static inline int
+static inline void
snd_pcm_rate_read_areas1(snd_pcm_t *pcm,
const snd_pcm_channel_area_t *areas,
snd_pcm_uframes_t offset,
@@ -789,10 +531,9 @@ snd_pcm_rate_read_areas1(snd_pcm_t *pcm,
snd_pcm_uframes_t slave_offset)
{
snd_pcm_rate_t *rate = pcm->private_data;
- rate->func(areas, offset, pcm->period_size,
+ do_convert(areas, offset, pcm->period_size,
slave_areas, slave_offset, rate->gen.slave->period_size,
pcm->channels, rate);
- return 0;
}
static inline snd_pcm_sframes_t snd_pcm_rate_move_applptr(snd_pcm_t *pcm, snd_pcm_sframes_t frames)
@@ -827,8 +568,12 @@ static inline snd_pcm_sframes_t snd_pcm_rate_move_applptr(snd_pcm_t *pcm, snd_pc
slave_appl_ptr = *slave->appl.ptr;
rate->appl_ptr =
(slave_appl_ptr / rate->gen.slave->period_size) * pcm->period_size +
- snd_pcm_rate_client_frames(pcm, slave_appl_ptr % rate->gen.slave->period_size) +
orig_appl_ptr % pcm->period_size;
+ if (pcm->stream == SND_PCM_STREAM_PLAYBACK)
+ rate->appl_ptr += rate->ops.input_frames(rate->obj, slave_appl_ptr % rate->gen.slave->period_size);
+ else
+ rate->appl_ptr += rate->ops.output_frames(rate->obj, slave_appl_ptr % rate->gen.slave->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;
@@ -855,7 +600,7 @@ static inline void snd_pcm_rate_sync_hwptr(snd_pcm_t *pcm)
*/
rate->hw_ptr =
(slave_hw_ptr / rate->gen.slave->period_size) * pcm->period_size +
- snd_pcm_rate_client_frames(pcm, slave_hw_ptr % rate->gen.slave->period_size);
+ rate->ops.output_frames(rate->obj, slave_hw_ptr % rate->gen.slave->period_size);
}
static int snd_pcm_rate_hwsync(snd_pcm_t *pcm)
@@ -1323,7 +1068,7 @@ static int snd_pcm_rate_drain(snd_pcm_t *pcm)
spsize = rate->gen.slave->period_size;
} else {
psize = size;
- spsize = snd_pcm_rate_slave_frames(pcm, size);
+ spsize = rate->ops.output_frames(rate->obj, size);
if (! spsize)
break;
}
@@ -1357,15 +1102,16 @@ static int snd_pcm_rate_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
status->delay = snd_pcm_mmap_playback_hw_avail(pcm);
status->avail = snd_pcm_mmap_playback_avail(pcm);
+ status->avail_max = rate->ops.input_frames(rate->obj, status->avail_max);
} else {
status->delay = snd_pcm_mmap_capture_hw_avail(pcm);
status->avail = snd_pcm_mmap_capture_avail(pcm);
+ status->avail_max = rate->ops.output_frames(rate->obj, status->avail_max);
}
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;
}
@@ -1387,6 +1133,15 @@ static void snd_pcm_rate_dump(snd_pcm_t *pcm, snd_output_t *out)
snd_pcm_dump(rate->gen.slave, out);
}
+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);
+ return snd_pcm_generic_close(pcm);
+}
+
static snd_pcm_fast_ops_t snd_pcm_rate_fast_ops = {
.status = snd_pcm_rate_status,
.state = snd_pcm_generic_state,
@@ -1413,7 +1168,7 @@ static snd_pcm_fast_ops_t snd_pcm_rate_fast_ops = {
};
static snd_pcm_ops_t snd_pcm_rate_ops = {
- .close = snd_pcm_generic_close,
+ .close = snd_pcm_rate_close,
.info = snd_pcm_generic_info,
.hw_refine = snd_pcm_rate_hw_refine,
.hw_params = snd_pcm_rate_hw_params,
@@ -1427,6 +1182,22 @@ static snd_pcm_ops_t snd_pcm_rate_ops = {
.munmap = snd_pcm_generic_munmap,
};
+/**
+ * \brief Get a default converter string
+ * \param root Root configuration node
+ * \retval A const string if found, or NULL
+ */
+const char *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) {
+ const char *str;
+ if (snd_config_get_string(n, &str) >= 0)
+ return str;
+ }
+ return NULL;
+}
/**
* \brief Creates a new rate PCM
@@ -1434,6 +1205,7 @@ static snd_pcm_ops_t snd_pcm_rate_ops = {
* \param name Name of PCM
* \param sformat Slave format
* \param srate Slave rate
+ * \param type SRC type string
* \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
@@ -1441,11 +1213,18 @@ static snd_pcm_ops_t snd_pcm_rate_ops = {
* 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, snd_pcm_t *slave, int close_slave)
+int snd_pcm_rate_open(snd_pcm_t **pcmp, const char *name, snd_pcm_format_t sformat,
+ unsigned int srate, const char *type, snd_pcm_t *slave, int close_slave)
{
snd_pcm_t *pcm;
snd_pcm_rate_t *rate;
+ snd_pcm_rate_open_func_t open_func;
+ char open_name[64];
int err;
+#ifndef PIC
+ 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)
@@ -1456,16 +1235,60 @@ int snd_pcm_rate_open(snd_pcm_t **pcmp, const char *name, snd_pcm_format_t sform
}
rate->gen.slave = slave;
rate->gen.close_slave = close_slave;
- rate->type = RATE_TYPE_LINEAR;
rate->srate = srate;
rate->sformat = sformat;
snd_atomic_write_init(&rate->watom);
+ if (! type || ! *type)
+ type = "linear";
+
+#ifdef PIC
+ snprintf(open_name, sizeof(open_name), "_snd_pcm_rate_%s_open", type);
+ open_func = snd_dlobj_cache_lookup(open_name);
+ if (! open_func) {
+ void *h;
+ char lib_name[128], *lib = NULL;
+ if (strcmp(type, "linear")) {
+ snprintf(lib_name, sizeof(lib_name),
+ "%s/libasound_module_rate_%s.so", PKGLIBDIR, type);
+ lib = lib_name;
+ }
+ h = snd_dlopen(lib, RTLD_NOW);
+ if (! h) {
+ SNDERR("Cannot open library for type %s", type);
+ free(rate);
+ return -ENOENT;
+ }
+ open_func = dlsym(h, open_name);
+ if (! open_func) {
+ SNDERR("Cannot find function %s", open_name);
+ snd_dlclose(h);
+ free(rate);
+ return -ENOENT;
+ }
+ snd_dlobj_cache_add(open_name, h, open_func);
+ }
+#else
+ open_func = SND_PCM_RATE_PLUGIN_ENTRY(linear);
+#endif
+
err = snd_pcm_new(&pcm, SND_PCM_TYPE_RATE, name, slave->stream, slave->mode);
if (err < 0) {
free(rate);
return err;
}
+ err = open_func(SND_PCM_RATE_PLUGIN_VERSION, &rate->obj, &rate->ops);
+ if (err < 0) {
+ snd_pcm_close(pcm);
+ return err;
+ }
+ 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_close(pcm);
+ return err;
+ }
+
pcm->ops = &snd_pcm_rate_ops;
pcm->fast_ops = &snd_pcm_rate_fast_ops;
pcm->private_data = rate;
@@ -1497,6 +1320,8 @@ pcm.name {
rate INT # Slave rate
[format STR] # Slave format
}
+ [converter STR] # Converter type, default is taken from
+ # defaults.pcm.rate_converter
}
\endcode
@@ -1532,6 +1357,8 @@ int _snd_pcm_rate_open(snd_pcm_t **pcmp, const char *name,
snd_config_t *slave = NULL, *sconf;
snd_pcm_format_t sformat = SND_PCM_FORMAT_UNKNOWN;
int srate = -1;
+ const char *type = NULL;
+
snd_config_for_each(i, next, conf) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id;
@@ -1543,6 +1370,15 @@ int _snd_pcm_rate_open(snd_pcm_t **pcmp, const char *name,
slave = n;
continue;
}
+ if (strcmp(id, "converter") == 0) {
+ const char *str;
+ if ((err = snd_config_get_string(n, &str)) < 0) {
+ SNDERR("invalid converter string");
+ return -EINVAL;
+ }
+ type = str;
+ continue;
+ }
SNDERR("Unknown field %s", id);
return -EINVAL;
}
@@ -1550,6 +1386,10 @@ int _snd_pcm_rate_open(snd_pcm_t **pcmp, const char *name,
SNDERR("slave is not defined");
return -EINVAL;
}
+
+ if (! type) {
+ }
+
err = snd_pcm_slave_conf(root, slave, &sconf, 2,
SND_PCM_HW_PARAM_FORMAT, 0, &sformat,
SND_PCM_HW_PARAM_RATE, SCONF_MANDATORY, &srate);
@@ -1565,8 +1405,8 @@ int _snd_pcm_rate_open(snd_pcm_t **pcmp, const char *name,
snd_config_delete(sconf);
if (err < 0)
return err;
- err = snd_pcm_rate_open(pcmp, name,
- sformat, (unsigned int) srate, spcm, 1);
+ err = snd_pcm_rate_open(pcmp, name, sformat, (unsigned int) srate,
+ type, spcm, 1);
if (err < 0)
snd_pcm_close(spcm);
return err;
diff --git a/src/pcm/pcm_rate_linear.c b/src/pcm/pcm_rate_linear.c
new file mode 100644
index 00000000..dade0c6d
--- /dev/null
+++ b/src/pcm/pcm_rate_linear.c
@@ -0,0 +1,435 @@
+/*
+ * Linear rate converter plugin
+ *
+ * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
+ * 2004 by Jaroslav Kysela <perex@suse.cz>
+ * 2006 by Takashi Iwai <tiwai@suse.de>
+ *
+ * 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
+ * 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <inttypes.h>
+#include <byteswap.h>
+#include "pcm_local.h"
+#include "pcm_plugin.h"
+#include "pcm_rate.h"
+
+#include "plugin_ops.h"
+
+
+/* LINEAR_DIV needs to be large enough to handle resampling from 192000 -> 8000 */
+#define LINEAR_DIV_SHIFT 19
+#define LINEAR_DIV (1<<LINEAR_DIV_SHIFT)
+
+struct rate_linear {
+ unsigned int get_idx;
+ unsigned int put_idx;
+ unsigned int pitch;
+ unsigned int pitch_shift; /* for expand interpolation */
+ unsigned int channels;
+ int16_t *old_sample;
+ void (*func)(struct rate_linear *rate,
+ 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);
+};
+
+static snd_pcm_uframes_t input_frames(void *obj, snd_pcm_uframes_t frames)
+{
+ struct rate_linear *rate = obj;
+ if (frames == 0)
+ return 0;
+ /* Round toward zero */
+ return muldiv_near(frames, LINEAR_DIV, rate->pitch);
+}
+
+static snd_pcm_uframes_t output_frames(void *obj, snd_pcm_uframes_t frames)
+{
+ struct rate_linear *rate = obj;
+ if (frames == 0)
+ return 0;
+ /* Round toward zero */
+ return muldiv_near(frames, rate->pitch, LINEAR_DIV);
+}
+
+static void linear_expand(struct rate_linear *rate,
+ 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)
+{
+#define GET16_LABELS
+#define PUT16_LABELS
+#include "plugin_ops.h"
+#undef GET16_LABELS
+#undef PUT16_LABELS
+ void *get = get16_labels[rate->get_idx];
+ void *put = put16_labels[rate->put_idx];
+ unsigned int get_threshold = rate->pitch;
+ unsigned int channel;
+ unsigned int src_frames1;
+ unsigned int dst_frames1;
+ int16_t sample = 0;
+ unsigned int pos;
+
+ for (channel = 0; channel < rate->channels; ++channel) {
+ const snd_pcm_channel_area_t *src_area = &src_areas[channel];
+ const snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
+ const char *src;
+ char *dst;
+ int src_step, dst_step;
+ int16_t old_sample = 0;
+ int16_t new_sample;
+ int old_weight, new_weight;
+ 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;
+ new_sample = rate->old_sample[channel];
+ pos = get_threshold;
+ while (dst_frames1 < dst_frames) {
+ if (pos >= get_threshold) {
+ pos -= get_threshold;
+ old_sample = new_sample;
+ if (src_frames1 < src_frames) {
+ goto *get;
+#define GET16_END after_get
+#include "plugin_ops.h"
+#undef GET16_END
+ after_get:
+ new_sample = sample;
+ }
+ }
+ new_weight = (pos << (16 - rate->pitch_shift)) / (get_threshold >> rate->pitch_shift);
+ old_weight = 0x10000 - new_weight;
+ sample = (old_sample * old_weight + new_sample * new_weight) >> 16;
+ goto *put;
+#define PUT16_END after_put
+#include "plugin_ops.h"
+#undef PUT16_END
+ after_put:
+ dst += dst_step;
+ dst_frames1++;
+ pos += LINEAR_DIV;
+ if (pos >= get_threshold) {
+ src += src_step;
+ src_frames1++;
+ }
+ }
+ rate->old_sample[channel] = new_sample;
+ }
+}
+
+/* optimized version for S16 format */
+static void linear_expand_s16(struct rate_linear *rate,
+ 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 channel;
+ unsigned int src_frames1;
+ unsigned int dst_frames1;
+ unsigned int get_threshold = rate->pitch;
+ unsigned int pos;
+
+ for (channel = 0; channel < rate->channels; ++channel) {
+ const snd_pcm_channel_area_t *src_area = &src_areas[channel];
+ const snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
+ const int16_t *src;
+ int16_t *dst;
+ int src_step, dst_step;
+ int16_t old_sample = 0;
+ int16_t new_sample;
+ int old_weight, new_weight;
+ 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) >> 1;
+ dst_step = snd_pcm_channel_area_step(dst_area) >> 1;
+ src_frames1 = 0;
+ dst_frames1 = 0;
+ new_sample = rate->old_sample[channel];
+ pos = get_threshold;
+ while (dst_frames1 < dst_frames) {
+ if (pos >= get_threshold) {
+ pos -= get_threshold;
+ old_sample = new_sample;
+ if (src_frames1 < src_frames)
+ new_sample = *src;
+ }
+ new_weight = (pos << (16 - rate->pitch_shift)) / (get_threshold >> rate->pitch_shift);
+ old_weight = 0x10000 - new_weight;
+ *dst = (old_sample * old_weight + new_sample * new_weight) >> 16;
+ dst += dst_step;
+ dst_frames1++;
+ pos += LINEAR_DIV;
+ if (pos >= get_threshold) {
+ src += src_step;
+ src_frames1++;
+ }
+ }
+ rate->old_sample[channel] = new_sample;
+ }
+}
+
+static void linear_shrink(struct rate_linear *rate,
+ 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)
+{
+#define GET16_LABELS
+#define PUT16_LABELS
+#include "plugin_ops.h"
+#undef GET16_LABELS
+#undef PUT16_LABELS
+ void *get = get16_labels[rate->get_idx];
+ void *put = put16_labels[rate->put_idx];
+ unsigned int get_increment = rate->pitch;
+ unsigned int channel;
+ unsigned int src_frames1;
+ unsigned int dst_frames1;
+ int16_t sample = 0;
+ unsigned int pos;
+
+ for (channel = 0; channel < rate->channels; ++channel) {
+ const snd_pcm_channel_area_t *src_area = &src_areas[channel];
+ const snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
+ const char *src;
+ char *dst;
+ int src_step, dst_step;
+ int16_t old_sample = 0;
+ int16_t new_sample = 0;
+ int old_weight, new_weight;
+ pos = LINEAR_DIV - get_increment; /* Force first sample to be copied */
+ 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;
+#define GET16_END after_get
+#include "plugin_ops.h"
+#undef GET16_END
+ after_get:
+ new_sample = sample;
+ src += src_step;
+ src_frames1++;
+ pos += get_increment;
+ if (pos >= LINEAR_DIV) {
+ pos -= LINEAR_DIV;
+ old_weight = (pos << (32 - LINEAR_DIV_SHIFT)) / (get_increment >> (LINEAR_DIV_SHIFT - 16));
+ new_weight = 0x10000 - old_weight;
+ sample = (old_sample * old_weight + new_sample * new_weight) >> 16;
+ goto *put;
+#define PUT16_END after_put
+#include "plugin_ops.h"
+#undef PUT16_END
+ after_put:
+ dst += dst_step;
+ dst_frames1++;
+ if (CHECK_SANITY(dst_frames1 > dst_frames)) {
+ SNDERR("dst_frames overflow");
+ break;
+ }
+ }
+ old_sample = new_sample;
+ }
+ }
+}
+
+/* optimized version for S16 format */
+static void linear_shrink_s16(struct rate_linear *rate,
+ 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 get_increment = rate->pitch;
+ unsigned int channel;
+ unsigned int src_frames1;
+ unsigned int dst_frames1;
+ unsigned int pos = 0;
+
+ for (channel = 0; channel < rate->channels; ++channel) {
+ const snd_pcm_channel_area_t *src_area = &src_areas[channel];
+ const snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
+ const int16_t *src;
+ int16_t *dst;
+ int src_step, dst_step;
+ int16_t old_sample = 0;
+ int16_t new_sample = 0;
+ int old_weight, new_weight;
+ pos = LINEAR_DIV - get_increment; /* Force first sample to be copied */
+ 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) >> 1;
+ dst_step = snd_pcm_channel_area_step(dst_area) >> 1 ;
+ src_frames1 = 0;
+ dst_frames1 = 0;
+ while (src_frames1 < src_frames) {
+
+ new_sample = *src;
+ src += src_step;
+ src_frames1++;
+ pos += get_increment;
+ if (pos >= LINEAR_DIV) {
+ pos -= LINEAR_DIV;
+ old_weight = (pos << (32 - LINEAR_DIV_SHIFT)) / (get_increment >> (LINEAR_DIV_SHIFT - 16));
+ new_weight = 0x10000 - old_weight;
+ *dst = (old_sample * old_weight + new_sample * new_weight) >> 16;
+ dst += dst_step;
+ dst_frames1++;
+ if (CHECK_SANITY(dst_frames1 > dst_frames)) {
+ SNDERR("dst_frames overflow");
+ break;
+ }
+ }
+ old_sample = new_sample;
+ }
+ }
+}
+
+static void linear_convert(void *obj,
+ 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)
+{
+ struct rate_linear *rate = obj;
+ rate->func(rate, dst_areas, dst_offset, dst_frames,
+ src_areas, src_offset, src_frames);
+}
+
+static void linear_free(void *obj)
+{
+ struct rate_linear *rate = obj;
+
+ free(rate->old_sample);
+ rate->old_sample = NULL;
+}
+
+static int linear_init(void *obj, snd_pcm_rate_info_t *info)
+{
+ struct rate_linear *rate = obj;
+
+ rate->get_idx = snd_pcm_linear_get_index(info->in.format, SND_PCM_FORMAT_S16);
+ rate->put_idx = snd_pcm_linear_put_index(SND_PCM_FORMAT_S16, info->out.format);
+ if (info->in.rate < info->out.rate) {
+ if (info->in.format == info->out.format && info->in.format == SND_PCM_FORMAT_S16)
+ rate->func = linear_expand_s16;
+ else
+ rate->func = linear_expand;
+ /* pitch is get_threshold */
+ } else {
+ if (info->in.format == info->out.format && info->in.format == SND_PCM_FORMAT_S16)
+ rate->func = linear_shrink_s16;
+ else
+ rate->func = linear_shrink;
+ /* pitch is get_increment */
+ }
+ rate->pitch = (((u_int64_t)info->out.rate * LINEAR_DIV) +
+ (info->in.rate / 2)) / info->in.rate;
+ rate->channels = info->channels;
+
+ free(rate->old_sample);
+ rate->old_sample = malloc(sizeof(*rate->old_sample) * rate->channels);
+ if (! rate->old_sample)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int linear_adjust_pitch(void *obj, snd_pcm_rate_info_t *info)
+{
+ struct rate_linear *rate = obj;
+ snd_pcm_uframes_t cframes;
+
+ rate->pitch = (((u_int64_t)info->out.period_size * LINEAR_DIV) +
+ (info->in.period_size/2) ) / info->in.period_size;
+
+ cframes = input_frames(rate, info->out.period_size);
+ while (cframes != info->in.period_size) {
+ snd_pcm_uframes_t cframes_new;
+ if (cframes > info->in.period_size)
+ rate->pitch++;
+ else
+ rate->pitch--;
+ cframes_new = input_frames(rate, info->out.period_size);
+ if ((cframes > info->in.period_size && cframes_new < info->in.period_size) ||
+ (cframes < info->in.period_size && cframes_new > info->in.period_size)) {
+ SNDERR("invalid pcm period_size %ld -> %ld",
+ info->in.period_size, info->out.period_size);
+ return -EIO;
+ }
+ cframes = cframes_new;
+ }
+ if (rate->pitch >= LINEAR_DIV) {
+ /* shift for expand linear interpolation */
+ rate->pitch_shift = 0;
+ while ((rate->pitch >> rate->pitch_shift) >= (1 << 16))
+ rate->pitch_shift++;
+ }
+ return 0;
+}
+
+static void linear_reset(void *obj)
+{
+ struct rate_linear *rate = obj;
+
+ /* for expand */
+ if (rate->old_sample)
+ memset(rate->old_sample, 0, sizeof(*rate->old_sample) * rate->channels);
+}
+
+static void linear_close(void *obj)
+{
+ free(obj);
+}
+
+static snd_pcm_rate_ops_t linear_ops = {
+ .close = linear_close,
+ .init = linear_init,
+ .free = linear_free,
+ .reset = linear_reset,
+ .adjust_pitch = linear_adjust_pitch,
+ .convert = linear_convert,
+ .input_frames = input_frames,
+ .output_frames = output_frames,
+};
+
+int SND_PCM_RATE_PLUGIN_ENTRY(linear) (unsigned int version, void **objp, snd_pcm_rate_ops_t *ops)
+{
+ struct rate_linear *rate;
+
+ if (version != SND_PCM_RATE_PLUGIN_VERSION) {
+ SNDERR("Invalid plugin version %x\n", version);
+ return -EINVAL;
+ }
+
+ rate = calloc(1, sizeof(*rate));
+ if (! rate)
+ return -ENOMEM;
+
+ *objp = rate;
+ *ops = linear_ops;
+ return 0;
+}