]> git.alsa-project.org Git - alsa-lib.git/commitdiff
Add filter-type external plugin SDK
authorTakashi Iwai <tiwai@suse.de>
Mon, 14 Feb 2005 13:33:08 +0000 (13:33 +0000)
committerTakashi Iwai <tiwai@suse.de>
Mon, 14 Feb 2005 13:33:08 +0000 (13:33 +0000)
Added the SDK for filter-type PCM plugins.
Share some codes with ioplug.

include/Makefile.am
include/pcm.h
include/pcm_external.h
include/pcm_extplug.h [new file with mode: 0644]
src/Versions
src/pcm/Makefile.am
src/pcm/pcm_ext_parm.h [new file with mode: 0644]
src/pcm/pcm_extplug.c [new file with mode: 0644]
src/pcm/pcm_ioplug.c

index eeb21fee70c7ba018a3c7aa1fa1ba2828d1aeeae..201256fcb702a1960d8ac63cb4830958ee07af49 100644 (file)
@@ -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 \
                      pcm_ordinary.h mixer_ordinary.h \
-                     alisp.h pcm_external.h pcm_ioplug.h
+                     alisp.h pcm_external.h pcm_ioplug.h pcm_extplug.h
 
 noinst_HEADERS = sys.h search.h list.h aserver.h local.h alsa-symbols.h
 
index 1357359a343a5b57e70bee3700e27c05938e3d01..5224bb3cb22b58db7622c19812a00072dfc24321 100644 (file)
@@ -360,7 +360,9 @@ enum _snd_pcm_type {
        SND_PCM_TYPE_SOFTVOL,
        /** External I/O plugin */
        SND_PCM_TYPE_IOPLUG,
-       SND_PCM_TYPE_LAST = SND_PCM_TYPE_IOPLUG
+       /** External filter plugin */
+       SND_PCM_TYPE_EXTPLUG,
+       SND_PCM_TYPE_LAST = SND_PCM_TYPE_EXTPLUG
 };
 
 /** PCM type */
index 92d7c5206494dfbbc76d1996a11d6854ea22bfce..be1696749540e542662b85a69290858898d2c346 100644 (file)
@@ -12,5 +12,6 @@ int SND_PCM_PLUGIN_ENTRY(plugin) (snd_pcm_t **pcmp, const char *name,\
                                  snd_pcm_stream_t stream, int mode)
 
 #include "pcm_ioplug.h"
+#include "pcm_extplug.h"
 
 #endif /* __ALSA_PCM_EXTERNAL_H */
diff --git a/include/pcm_extplug.h b/include/pcm_extplug.h
new file mode 100644 (file)
index 0000000..9ea667f
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * ALSA external PCM plugin SDK (draft version)
+ *
+ * Copyright (c) 2005 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_EXTPLUG_H
+#define __ALSA_PCM_EXTPLUG_H
+
+/* hw constraints */
+enum {
+       SND_PCM_EXTPLUG_HW_FORMAT,
+       SND_PCM_EXTPLUG_HW_CHANNELS,
+       SND_PCM_EXTPLUG_HW_PARAMS
+};
+       
+typedef struct snd_pcm_extplug snd_pcm_extplug_t;
+typedef struct snd_pcm_extplug_callback snd_pcm_extplug_callback_t;
+
+/* exported pcm data */
+struct snd_pcm_extplug {
+       /* must be filled before calling snd_pcm_extplug_create() */
+       const char *name;
+       const snd_pcm_extplug_callback_t *callback;
+       void *private_data;
+       /* filled by snd_pcm_extplug_open() */
+       snd_pcm_t *pcm;
+       /* read-only status */
+       snd_pcm_stream_t stream;
+       /* filled in hw_params */
+       snd_pcm_format_t format;
+       snd_pcm_subformat_t subformat;
+       unsigned int channels;
+       unsigned int rate;
+       snd_pcm_format_t slave_format;
+       snd_pcm_subformat_t slave_subformat;
+       unsigned int slave_channels;
+};
+
+/* callback table */
+struct snd_pcm_extplug_callback {
+       /* required */
+       snd_pcm_sframes_t (*transfer)(snd_pcm_extplug_t *ext,
+                                     const snd_pcm_channel_area_t *dst_areas,
+                                     snd_pcm_uframes_t dst_offset,
+                                     const snd_pcm_channel_area_t *src_areas,
+                                     snd_pcm_uframes_t src_offset,
+                                     snd_pcm_uframes_t size);
+       int (*close)(snd_pcm_extplug_t *ext);
+       int (*hw_params)(snd_pcm_extplug_t *ext, snd_pcm_hw_params_t *params);
+       int (*hw_free)(snd_pcm_extplug_t *ext);
+       void (*dump)(snd_pcm_extplug_t *ext, snd_output_t *out);
+};
+
+
+int snd_pcm_extplug_create(snd_pcm_extplug_t *ext, const char *name,
+                          snd_config_t *root, snd_config_t *slave_conf,
+                          snd_pcm_stream_t stream, int mode);
+int snd_pcm_extplug_delete(snd_pcm_extplug_t *ext);
+
+/* clear hw_parameter setting */
+void snd_pcm_extplug_params_reset(snd_pcm_extplug_t *ext);
+
+/* hw_parameter setting */
+int snd_pcm_extplug_set_param_list(snd_pcm_extplug_t *extplug, int type, unsigned int num_list, const unsigned int *list);
+int snd_pcm_extplug_set_param_minmax(snd_pcm_extplug_t *extplug, int type, unsigned int min, unsigned int max);
+int snd_pcm_extplug_set_slave_param_list(snd_pcm_extplug_t *extplug, int type, unsigned int num_list, const unsigned int *list);
+int snd_pcm_extplug_set_slave_param_minmax(snd_pcm_extplug_t *extplug, int type, unsigned int min, unsigned int max);
+
+static inline int snd_pcm_extplug_set_param(snd_pcm_extplug_t *extplug, int type, unsigned int val)
+{
+       return snd_pcm_extplug_set_param_list(extplug, type, 1, &val);
+}
+
+static inline int snd_pcm_extplug_set_slave_param(snd_pcm_extplug_t *extplug, int type, unsigned int val)
+{
+       return snd_pcm_extplug_set_slave_param_list(extplug, type, 1, &val);
+}
+
+
+#endif /* __ALSA_PCM_EXTPLUG_H */
index 5ac29f291a31fc400dd84521b8f14a13b422c944..e093dedae12d9605e886afc73b74df78f5ba7180 100644 (file)
@@ -180,4 +180,13 @@ ALSA_1.0.9 {
     snd_pcm_ioplug_params_reset;
     snd_pcm_ioplug_set_param_minmax;
     snd_pcm_ioplug_set_param_list;
+
+    snd_pcm_extplug_create;
+    snd_pcm_extplug_delete;
+    snd_pcm_extplug_params_reset;
+    snd_pcm_extplug_set_param_list;
+    snd_pcm_extplug_set_param_minmax;
+    snd_pcm_extplug_set_slave_param_list;
+    snd_pcm_extplug_set_slave_param_minmax;
+
 } ALSA_1.0.5;
index 2ace7912e5d51307a6971d0f649c13e28cce5897..27ce61cd6eb79aa500a3c2d8ae48a955026e5da6 100644 (file)
@@ -12,14 +12,14 @@ libpcm_la_SOURCES = atomic.c mask.c interval.c \
                    pcm_meter.c pcm_hooks.c pcm_lfloat.c pcm_ladspa.c \
                    pcm_direct.c pcm_dmix.c pcm_dsnoop.c pcm_dshare.c \
                    pcm_asym.c pcm_iec958.c pcm_softvol.c pcm_symbols.c \
-                   pcm_ioplug.c
+                   pcm_ioplug.c pcm_extplug.c
 
-EXTRA_SOURCES = pcm_dmix_i386.c pcm_dmix_x86_64.c pcm_dmix_generic.c
+EXTRA_DIST = pcm_dmix_i386.c pcm_dmix_x86_64.c pcm_dmix_generic.c
 
 noinst_HEADERS = pcm_local.h pcm_plugin.h mask.h mask_inline.h \
                 interval.h interval_inline.h plugin_ops.h ladspa.h \
                 pcm_direct.h pcm_dmix_i386.h pcm_dmix_x86_64.h \
-                pcm_generic.h
+                pcm_generic.h pcm_ext_parm.h
 
 alsadir = $(datadir)/alsa
 
diff --git a/src/pcm/pcm_ext_parm.h b/src/pcm/pcm_ext_parm.h
new file mode 100644 (file)
index 0000000..b70749f
--- /dev/null
@@ -0,0 +1,27 @@
+/* hw_params */
+struct snd_ext_parm {
+       unsigned int min, max;
+       unsigned int num_list;
+       unsigned int *list;
+       unsigned int active: 1;
+       unsigned int integer: 1;
+};
+
+static inline snd_mask_t *hw_param_mask(snd_pcm_hw_params_t *params,
+                                       snd_pcm_hw_param_t var)
+{
+       return &params->masks[var - SND_PCM_HW_PARAM_FIRST_MASK];
+}
+
+static inline snd_interval_t *hw_param_interval(snd_pcm_hw_params_t *params,
+                                               snd_pcm_hw_param_t var)
+{
+       return &params->intervals[var - SND_PCM_HW_PARAM_FIRST_INTERVAL];
+}
+
+int snd_ext_parm_set_minmax(struct snd_ext_parm *parm, unsigned int min, unsigned int max);
+int snd_ext_parm_set_list(struct snd_ext_parm *parm, unsigned int num_list, const unsigned int *list);
+void snd_ext_parm_clear(struct snd_ext_parm *parm);
+int snd_interval_list(snd_interval_t *ival, int num_list, unsigned int *list);
+int snd_ext_parm_interval_refine(snd_interval_t *ival, struct snd_ext_parm *parm, int type);
+int snd_ext_parm_mask_refine(snd_mask_t *mask, struct snd_ext_parm *parm, int type);
diff --git a/src/pcm/pcm_extplug.c b/src/pcm/pcm_extplug.c
new file mode 100644 (file)
index 0000000..24137e6
--- /dev/null
@@ -0,0 +1,516 @@
+/**
+ * \file pcm/pcm_extplug.c
+ * \ingroup Plugin_SDK
+ * \brief External Plugin SDK
+ * \author Takashi Iwai <tiwai@suse.de>
+ * \date 2005
+ */
+/*
+ *  PCM - External Filter Plugin SDK
+ *  Copyright (c) 2005 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 "pcm_local.h"
+#include "pcm_plugin.h"
+#include "pcm_extplug.h"
+#include "pcm_ext_parm.h"
+
+typedef struct snd_pcm_extplug_priv {
+       snd_pcm_plugin_t plug;
+       snd_pcm_extplug_t *data;
+       struct snd_ext_parm params[SND_PCM_EXTPLUG_HW_PARAMS];
+       struct snd_ext_parm sparams[SND_PCM_EXTPLUG_HW_PARAMS];
+} extplug_priv_t;
+
+static int hw_params_type[SND_PCM_EXTPLUG_HW_PARAMS] = {
+       [SND_PCM_EXTPLUG_HW_FORMAT] = SND_PCM_HW_PARAM_FORMAT,
+       [SND_PCM_EXTPLUG_HW_CHANNELS] = SND_PCM_HW_PARAM_CHANNELS
+};
+
+#define is_mask_type(i) (hw_params_type[i] < SND_PCM_HW_PARAM_FIRST_INTERVAL)
+
+static unsigned int excl_parbits[SND_PCM_EXTPLUG_HW_PARAMS] = {
+       [SND_PCM_EXTPLUG_HW_FORMAT] = (SND_PCM_HW_PARBIT_FORMAT|
+                                      SND_PCM_HW_PARBIT_SUBFORMAT |
+                                      SND_PCM_HW_PARBIT_SAMPLE_BITS),
+       [SND_PCM_EXTPLUG_HW_CHANNELS] = (SND_PCM_HW_PARBIT_CHANNELS|
+                                        SND_PCM_HW_PARBIT_FRAME_BITS),
+};
+
+/*
+ */
+
+int snd_ext_parm_set_minmax(struct snd_ext_parm *parm, unsigned int min, unsigned int max)
+{
+       parm->num_list = 0;
+       free(parm->list);
+       parm->list = NULL;
+       parm->min = min;
+       parm->max = max;
+       parm->active = 1;
+       return 0;
+}
+
+static int val_compar(const void *ap, const void *bp)
+{
+       return *(const unsigned int *)ap - *(const unsigned int *)bp;
+}
+
+int snd_ext_parm_set_list(struct snd_ext_parm *parm, unsigned int num_list, const unsigned int *list)
+{
+       unsigned int *new_list;
+
+       new_list = malloc(sizeof(*new_list) * num_list);
+       if (new_list == NULL)
+               return -ENOMEM;
+       memcpy(new_list, list, sizeof(*new_list) * num_list);
+       qsort(new_list, num_list, sizeof(*new_list), val_compar);
+
+       free(parm->list);
+       parm->num_list = num_list;
+       parm->list = new_list;
+       parm->active = 1;
+       return 0;
+}
+
+void snd_ext_parm_clear(struct snd_ext_parm *parm)
+{
+       free(parm->list);
+       memset(parm, 0, sizeof(*parm));
+}
+
+int snd_interval_list(snd_interval_t *ival, int num_list, unsigned int *list)
+{
+       int imin, imax;
+       int changed = 0;
+
+       if (snd_interval_empty(ival))
+               return -ENOENT;
+       for (imin = 0; imin < num_list; imin++) {
+               if (ival->min == list[imin] && ! ival->openmin)
+                       break;
+               if (ival->min <= list[imin]) {
+                       ival->min = list[imin];
+                       ival->openmin = 0;
+                       changed = 1;
+                       break;
+               }
+       }
+       if (imin >= num_list)
+               return -EINVAL;
+       for (imax = num_list - 1; imax >= imin; imax--) {
+               if (ival->max == list[imax] && ! ival->openmax)
+                       break;
+               if (ival->max >= list[imax]) {
+                       ival->max = list[imax];
+                       ival->openmax = 0;
+                       changed = 1;
+                       break;
+               }
+       }
+       if (imax < imin)
+               return -EINVAL;
+       return changed;
+}
+
+int snd_ext_parm_interval_refine(snd_interval_t *ival, struct snd_ext_parm *parm, int type)
+{
+       parm += type;
+       if (! parm->active)
+               return 0;
+       ival->integer |= parm->integer;
+       if (parm->num_list) {
+               return snd_interval_list(ival, parm->num_list, parm->list);
+       } else if (parm->min || parm->max) {
+               snd_interval_t t;
+               memset(&t, 0, sizeof(t));
+               snd_interval_set_minmax(&t, parm->min, parm->max);
+               t.integer = ival->integer;
+               return snd_interval_refine(ival, &t);
+       }
+       return 0;
+}
+
+int snd_ext_parm_mask_refine(snd_mask_t *mask, struct snd_ext_parm *parm, int type)
+{
+       snd_mask_t bits;
+       unsigned int i;
+
+       parm += type;
+       memset(&bits, 0, sizeof(bits));
+       for (i = 0; i < parm->num_list; i++)
+               bits.bits[parm->list[i] / 32] |= 1U << (parm->list[i] % 32);
+       return snd_mask_refine(mask, &bits);
+}
+
+
+/*
+ */
+
+static int extplug_hw_refine(snd_pcm_hw_params_t *hw_params,
+                            struct snd_ext_parm *parm)
+{
+       int i, err, change = 0;
+       for (i = 0; i < SND_PCM_EXTPLUG_HW_PARAMS; i++) {
+               int type = hw_params_type[i];
+               if (is_mask_type(i))
+                       err = snd_ext_parm_mask_refine(hw_param_mask(hw_params, type),
+                                                      parm, i);
+               else
+                       err = snd_ext_parm_interval_refine(hw_param_interval(hw_params, type),
+                                                          parm, i);
+               if (err < 0)
+                       return err;
+               change |= err;
+       }
+       return change;
+}
+
+static int snd_pcm_extplug_hw_refine_cprepare(snd_pcm_t *pcm,
+                                             snd_pcm_hw_params_t *params)
+{
+       extplug_priv_t *ext = pcm->private_data;
+       int err;
+       snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHM };
+       err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
+                                        &access_mask);
+       if (err < 0)
+               return err;
+       err = extplug_hw_refine(params, ext->params);
+       if (err < 0)
+               return err;
+       params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
+       return 0;
+}
+
+static int snd_pcm_extplug_hw_refine_sprepare(snd_pcm_t *pcm,
+                                             snd_pcm_hw_params_t *sparams)
+{
+       extplug_priv_t *ext = pcm->private_data;
+       snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP };
+       _snd_pcm_hw_params_any(sparams);
+       _snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
+                                  &saccess_mask);
+       extplug_hw_refine(sparams, ext->sparams);
+       return 0;
+}
+
+static unsigned int get_links(struct snd_ext_parm *params)
+{
+       int i;
+       unsigned int links = -1;
+       for (i = 0; i < SND_PCM_EXTPLUG_HW_PARAMS; i++) {
+               if (params[i].active)
+                       links &= ~excl_parbits[i];
+       }
+       return links;
+}
+
+static int snd_pcm_extplug_hw_refine_schange(snd_pcm_t *pcm,
+                                            snd_pcm_hw_params_t *params,
+                                            snd_pcm_hw_params_t *sparams)
+{
+       extplug_priv_t *ext = pcm->private_data;
+       unsigned int links = get_links(ext->sparams);
+       int err, change;
+       err = extplug_hw_refine(sparams, ext->sparams);
+       if (err < 0)
+               return err;
+       change = err;
+       err = _snd_pcm_hw_params_refine(sparams, links, params);
+       if (err < 0)
+               return err;
+       change |= err;
+       return change;
+}
+       
+static int snd_pcm_extplug_hw_refine_cchange(snd_pcm_t *pcm,
+                                            snd_pcm_hw_params_t *params,
+                                            snd_pcm_hw_params_t *sparams)
+{
+       extplug_priv_t *ext = pcm->private_data;
+       unsigned int links = get_links(ext->params);
+       int err, change;
+       err = extplug_hw_refine(params, ext->params);
+       if (err < 0)
+               return err;
+       change = err;
+       err = _snd_pcm_hw_params_refine(params, links, sparams);
+       if (err < 0)
+               return err;
+       change |= err;
+       return change;
+}
+
+static int snd_pcm_extplug_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
+{
+       return snd_pcm_hw_refine_slave(pcm, params,
+                                      snd_pcm_extplug_hw_refine_cprepare,
+                                      snd_pcm_extplug_hw_refine_cchange,
+                                      snd_pcm_extplug_hw_refine_sprepare,
+                                      snd_pcm_extplug_hw_refine_schange,
+                                      snd_pcm_generic_hw_refine);
+}
+
+static int snd_pcm_extplug_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
+{
+
+       extplug_priv_t *ext = pcm->private_data;
+       snd_pcm_t *slave = ext->plug.gen.slave;
+       int err = snd_pcm_hw_params_slave(pcm, params,
+                                         snd_pcm_extplug_hw_refine_cchange,
+                                         snd_pcm_extplug_hw_refine_sprepare,
+                                         snd_pcm_extplug_hw_refine_schange,
+                                         snd_pcm_generic_hw_params);
+       if (err < 0)
+               return err;
+       ext->data->slave_format = slave->format;
+       ext->data->slave_subformat = slave->subformat;
+       ext->data->slave_channels = slave->channels;
+       ext->data->rate = slave->rate;
+       INTERNAL(snd_pcm_hw_params_get_format)(params, &ext->data->format);
+       INTERNAL(snd_pcm_hw_params_get_subformat)(params, &ext->data->subformat);
+       if (ext->data->callback->hw_params) {
+               err = ext->data->callback->hw_params(ext->data, params);
+               if (err < 0)
+                       return err;
+       }
+       return 0;
+}
+
+static int snd_pcm_extplug_hw_free(snd_pcm_t *pcm)
+{
+       extplug_priv_t *ext = pcm->private_data;
+
+       snd_pcm_hw_free(ext->plug.gen.slave);
+       if (ext->data->callback->hw_free)
+               return ext->data->callback->hw_free(ext->data);
+       return 0;
+}
+
+static snd_pcm_uframes_t
+snd_pcm_extplug_write_areas(snd_pcm_t *pcm,
+                           const snd_pcm_channel_area_t *areas,
+                           snd_pcm_uframes_t offset,
+                           snd_pcm_uframes_t size,
+                           const snd_pcm_channel_area_t *slave_areas,
+                           snd_pcm_uframes_t slave_offset,
+                           snd_pcm_uframes_t *slave_sizep)
+{
+       extplug_priv_t *ext = pcm->private_data;
+
+       if (size > *slave_sizep)
+               size = *slave_sizep;
+       ext->data->callback->transfer(ext->data, slave_areas, slave_offset,
+                                     areas, offset, size);
+       *slave_sizep = size;
+       return size;
+}
+
+static snd_pcm_uframes_t
+snd_pcm_extplug_read_areas(snd_pcm_t *pcm,
+                          const snd_pcm_channel_area_t *areas,
+                          snd_pcm_uframes_t offset,
+                          snd_pcm_uframes_t size,
+                          const snd_pcm_channel_area_t *slave_areas,
+                          snd_pcm_uframes_t slave_offset,
+                          snd_pcm_uframes_t *slave_sizep)
+{
+       extplug_priv_t *ext = pcm->private_data;
+
+       if (size > *slave_sizep)
+               size = *slave_sizep;
+       ext->data->callback->transfer(ext->data, areas, offset,
+                                     slave_areas, slave_offset, size);
+       *slave_sizep = size;
+       return size;
+}
+
+static void snd_pcm_extplug_dump(snd_pcm_t *pcm, snd_output_t *out)
+{
+       extplug_priv_t *ext = pcm->private_data;
+
+       if (ext->data->callback->dump)
+               ext->data->callback->dump(ext->data, out);
+       else {
+               if (ext->data->name)
+                       snd_output_printf(out, "%s\n", ext->data->name);
+               else
+                       snd_output_printf(out, "External PCM Plugin\n");
+               if (pcm->setup) {
+                       snd_output_printf(out, "Its setup is:\n");
+                       snd_pcm_dump_setup(pcm, out);
+               }
+       }
+       snd_output_printf(out, "Slave: ");
+       snd_pcm_dump(ext->plug.gen.slave, out);
+}
+
+static void clear_ext_params(extplug_priv_t *ext)
+{
+       int i;
+       for (i = 0; i < SND_PCM_EXTPLUG_HW_PARAMS; i++) {
+               snd_ext_parm_clear(&ext->params[i]);
+               snd_ext_parm_clear(&ext->sparams[i]);
+       }
+}
+
+static int snd_pcm_extplug_close(snd_pcm_t *pcm)
+{
+       extplug_priv_t *ext = pcm->private_data;
+
+       snd_pcm_close(ext->plug.gen.slave);
+       clear_ext_params(ext);
+       if (ext->data->callback->close)
+               ext->data->callback->close(ext->data);
+       free(ext);
+       return 0;
+}
+
+static snd_pcm_ops_t snd_pcm_extplug_ops = {
+       .close = snd_pcm_extplug_close,
+       .info = snd_pcm_generic_info,
+       .hw_refine = snd_pcm_extplug_hw_refine,
+       .hw_params = snd_pcm_extplug_hw_params,
+       .hw_free = snd_pcm_extplug_hw_free,
+       .sw_params = snd_pcm_generic_sw_params,
+       .channel_info = snd_pcm_generic_channel_info,
+       .dump = snd_pcm_extplug_dump,
+       .nonblock = snd_pcm_generic_nonblock,
+       .async = snd_pcm_generic_async,
+       .poll_revents = snd_pcm_generic_poll_revents,
+       .mmap = snd_pcm_generic_mmap,
+       .munmap = snd_pcm_generic_munmap,
+};
+
+/*
+ */
+void snd_pcm_extplug_params_reset(snd_pcm_extplug_t *extplug)
+{
+       extplug_priv_t *ext = extplug->pcm->private_data;
+       clear_ext_params(ext);
+}
+
+int snd_pcm_extplug_set_slave_param_list(snd_pcm_extplug_t *extplug, int type, unsigned int num_list, const unsigned int *list)
+{
+       extplug_priv_t *ext = extplug->pcm->private_data;
+       if (type < 0 && type >= SND_PCM_EXTPLUG_HW_PARAMS) {
+               SNDERR("EXTPLUG: invalid parameter type %d", type);
+               return -EINVAL;
+       }
+       return snd_ext_parm_set_list(&ext->sparams[type], num_list, list);
+}
+
+int snd_pcm_extplug_set_slave_param_minmax(snd_pcm_extplug_t *extplug, int type, unsigned int min, unsigned int max)
+{
+       extplug_priv_t *ext = extplug->pcm->private_data;
+       if (type < 0 && type >= SND_PCM_EXTPLUG_HW_PARAMS) {
+               SNDERR("EXTPLUG: invalid parameter type %d", type);
+               return -EINVAL;
+       }
+       if (is_mask_type(type)) {
+               SNDERR("EXTPLUG: invalid parameter type %d", type);
+               return -EINVAL;
+       }
+       return snd_ext_parm_set_minmax(&ext->sparams[type], min, max);
+}
+
+int snd_pcm_extplug_set_param_list(snd_pcm_extplug_t *extplug, int type, unsigned int num_list, const unsigned int *list)
+{
+       extplug_priv_t *ext = extplug->pcm->private_data;
+       if (type < 0 && type >= SND_PCM_EXTPLUG_HW_PARAMS) {
+               SNDERR("EXTPLUG: invalid parameter type %d", type);
+               return -EINVAL;
+       }
+       return snd_ext_parm_set_list(&ext->params[type], num_list, list);
+}
+
+int snd_pcm_extplug_set_param_minmax(snd_pcm_extplug_t *extplug, int type, unsigned int min, unsigned int max)
+{
+       extplug_priv_t *ext = extplug->pcm->private_data;
+       if (type < 0 && type >= SND_PCM_EXTPLUG_HW_PARAMS) {
+               SNDERR("EXTPLUG: invalid parameter type %d", type);
+               return -EINVAL;
+       }
+       if (is_mask_type(type)) {
+               SNDERR("EXTPLUG: invalid parameter type %d", type);
+               return -EINVAL;
+       }
+       return snd_ext_parm_set_minmax(&ext->params[type], min, max);
+}
+
+/*
+ */
+int snd_pcm_extplug_create(snd_pcm_extplug_t *extplug, const char *name,
+                          snd_config_t *root, snd_config_t *slave_conf,
+                          snd_pcm_stream_t stream, int mode)
+{
+       extplug_priv_t *ext;
+       int err;
+       snd_pcm_t *spcm, *pcm;
+       snd_config_t *sconf;
+
+       assert(extplug && extplug->callback);
+       assert(extplug->callback->transfer);
+
+       err = snd_pcm_slave_conf(root, slave_conf, &sconf, 0);
+       if (err < 0)
+               return err;
+       err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode);
+       snd_config_delete(sconf);
+       if (err < 0)
+               return err;
+
+       ext = calloc(1, sizeof(*ext));
+       if (! ext)
+               return -ENOMEM;
+
+       ext->data = extplug;
+       extplug->stream = stream;
+
+       snd_pcm_plugin_init(&ext->plug);
+       ext->plug.read = snd_pcm_extplug_read_areas;
+       ext->plug.write = snd_pcm_extplug_write_areas;
+       ext->plug.undo_read = snd_pcm_plugin_undo_read_generic;
+       ext->plug.undo_write = snd_pcm_plugin_undo_write_generic;
+       ext->plug.gen.slave = spcm;
+       ext->plug.gen.close_slave = 1;
+
+       err = snd_pcm_new(&pcm, SND_PCM_TYPE_IOPLUG, name, stream, mode);
+       if (err < 0) {
+               free(ext);
+               return err;
+       }
+
+       extplug->pcm = pcm;
+       pcm->ops = &snd_pcm_extplug_ops;
+       pcm->fast_ops = &snd_pcm_plugin_fast_ops;
+       pcm->private_data = ext;
+       pcm->poll_fd = spcm->poll_fd;
+       pcm->poll_events = spcm->poll_events;
+       snd_pcm_set_hw_ptr(pcm, &ext->plug.hw_ptr, -1, 0);
+       snd_pcm_set_appl_ptr(pcm, &ext->plug.appl_ptr, -1, 0);
+
+       return 0;
+}
+
+int snd_pcm_extplug_delete(snd_pcm_extplug_t *extplug)
+{
+       return snd_pcm_close(extplug->pcm);
+}
index 9ed12555d1329f867b0b3a120a4af0b70189c223..bf8c98823e93fb7b7a568035257ba41601e8d40b 100644 (file)
   
 #include "pcm_local.h"
 #include "pcm_ioplug.h"
+#include "pcm_ext_parm.h"
 
 /* hw_params */
-struct ioplug_parm {
-       unsigned int min, max;
-       unsigned int num_list;
-       unsigned int *list;
-       unsigned int active: 1;
-       unsigned int integer: 1;
-};
-
 typedef struct snd_pcm_ioplug_priv {
        snd_pcm_ioplug_t *data;
-       struct ioplug_parm params[SND_PCM_IOPLUG_HW_PARAMS];
+       struct snd_ext_parm params[SND_PCM_IOPLUG_HW_PARAMS];
        unsigned int last_hw;
        snd_pcm_uframes_t avail_max;
        snd_htimestamp_t trigger_tstamp;
@@ -138,18 +131,6 @@ static int snd_pcm_ioplug_prepare(snd_pcm_t *pcm)
        return 0;
 }
 
-static inline snd_mask_t *hw_param_mask(snd_pcm_hw_params_t *params,
-                                       snd_pcm_hw_param_t var)
-{
-       return &params->masks[var - SND_PCM_HW_PARAM_FIRST_MASK];
-}
-
-static inline snd_interval_t *hw_param_interval(snd_pcm_hw_params_t *params,
-                                               snd_pcm_hw_param_t var)
-{
-       return &params->intervals[var - SND_PCM_HW_PARAM_FIRST_INTERVAL];
-}
-
 static int hw_params_type[SND_PCM_IOPLUG_HW_PARAMS] = {
        [SND_PCM_IOPLUG_HW_ACCESS] = SND_PCM_HW_PARAM_ACCESS,
        [SND_PCM_IOPLUG_HW_FORMAT] = SND_PCM_HW_PARAM_FORMAT,
@@ -160,72 +141,6 @@ static int hw_params_type[SND_PCM_IOPLUG_HW_PARAMS] = {
        [SND_PCM_IOPLUG_HW_PERIODS] = SND_PCM_HW_PARAM_PERIODS,
 };
 
-static int ioplug_mask_refine(snd_mask_t *mask, struct ioplug_parm *parm)
-{
-       snd_mask_t bits;
-       unsigned int i;
-
-       memset(&bits, 0, sizeof(bits));
-       for (i = 0; i < parm->num_list; i++)
-               bits.bits[parm->list[i] / 32] |= 1U << (parm->list[i] % 32);
-       return snd_mask_refine(mask, &bits);
-}
-
-static int snd_interval_list(snd_interval_t *ival, int num_list, unsigned int *list)
-{
-       int imin, imax;
-       int changed = 0;
-
-       if (snd_interval_empty(ival))
-               return -ENOENT;
-       for (imin = 0; imin < num_list; imin++) {
-               if (ival->min == list[imin] && ! ival->openmin)
-                       break;
-               if (ival->min <= list[imin]) {
-                       ival->min = list[imin];
-                       ival->openmin = 0;
-                       changed = 1;
-                       break;
-               }
-       }
-       if (imin >= num_list)
-               return -EINVAL;
-       for (imax = num_list - 1; imax >= imin; imax--) {
-               if (ival->max == list[imax] && ! ival->openmax)
-                       break;
-               if (ival->max >= list[imax]) {
-                       ival->max = list[imax];
-                       ival->openmax = 0;
-                       changed = 1;
-                       break;
-               }
-       }
-       if (imax < imin)
-               return -EINVAL;
-       return changed;
-}
-
-static int ioplug_interval_refine(ioplug_priv_t *io, snd_pcm_hw_params_t *params, int type)
-{
-       struct ioplug_parm *parm = &io->params[type];
-       snd_interval_t *ival;
-
-       if (! parm->active)
-               return 0;
-       ival = hw_param_interval(params, hw_params_type[type]);
-       ival->integer |= parm->integer;
-       if (parm->num_list) {
-               return snd_interval_list(ival, parm->num_list, parm->list);
-       } else if (parm->min || parm->max) {
-               snd_interval_t t;
-               memset(&t, 0, sizeof(t));
-               snd_interval_set_minmax(&t, parm->min, parm->max);
-               t.integer = ival->integer;
-               return snd_interval_refine(ival, &t);
-       }
-       return 0;
-}
-
 /* x = a * b */
 static int rule_mul(snd_pcm_hw_params_t *params, int x, int a, int b)
 {
@@ -324,15 +239,16 @@ static int snd_pcm_ioplug_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
 
        /* access, format */
        for (i = SND_PCM_IOPLUG_HW_ACCESS; i <= SND_PCM_IOPLUG_HW_FORMAT; i++) {
-               err = ioplug_mask_refine(hw_param_mask(params, hw_params_type[i]),
-                                &io->params[i]);
+               err = snd_ext_parm_mask_refine(hw_param_mask(params, hw_params_type[i]),
+                                              io->params, i);
                if (err < 0)
                        return err;
                change |= err;
        }
        /* channels, rate */
        for (; i <= SND_PCM_IOPLUG_HW_RATE; i++) {
-               err = ioplug_interval_refine(io, params, i);
+               err = snd_ext_parm_interval_refine(hw_param_interval(params, hw_params_type[i]),
+                                                  io->params, i);
                if (err < 0)
                        return err;
                change |= err;
@@ -354,7 +270,8 @@ static int snd_pcm_ioplug_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
                                       SND_PCM_HW_PARAM_PERIOD_BYTES);
        if (change1 < 0)
                return change1;
-       err = ioplug_interval_refine(io, params, SND_PCM_IOPLUG_HW_PERIOD_BYTES);
+       err = snd_ext_parm_interval_refine(hw_param_interval(params, SND_PCM_HW_PARAM_PERIOD_BYTES),
+                                          io->params, SND_PCM_IOPLUG_HW_PERIOD_BYTES);
        if (err < 0)
                return err;
        change1 |= err;
@@ -376,7 +293,8 @@ static int snd_pcm_ioplug_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
 
        do {
                change2 = 0;
-               err = ioplug_interval_refine(io, params, SND_PCM_IOPLUG_HW_BUFFER_BYTES);
+               err = snd_ext_parm_interval_refine(hw_param_interval(params, SND_PCM_HW_PARAM_BUFFER_BYTES),
+                                                  io->params, SND_PCM_IOPLUG_HW_BUFFER_BYTES);
                if (err < 0)
                        return err;
                change2 |= err;
@@ -387,7 +305,8 @@ static int snd_pcm_ioplug_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
                if (err < 0)
                        return err;
                change2 |= err;
-               err = ioplug_interval_refine(io, params, SND_PCM_IOPLUG_HW_PERIODS);
+               err = snd_ext_parm_interval_refine(hw_param_interval(params, SND_PCM_HW_PARAM_PERIODS),
+                                                  io->params, SND_PCM_IOPLUG_HW_PERIODS);
                if (err < 0)
                        return err;
                change2 |= err;
@@ -723,10 +642,8 @@ static void snd_pcm_ioplug_dump(snd_pcm_t *pcm, snd_output_t *out)
 static void clear_io_params(ioplug_priv_t *io)
 {
        int i;
-       for (i = 0; i < SND_PCM_IOPLUG_HW_PARAMS; i++) {
-               free(io->params[i].list);
-               memset(&io->params[i], 0, sizeof(io->params[i]));
-       }
+       for (i = 0; i < SND_PCM_IOPLUG_HW_PARAMS; i++)
+               snd_ext_parm_clear(&io->params[i]);
 }
 
 static int snd_pcm_ioplug_close(snd_pcm_t *pcm)
@@ -783,39 +700,6 @@ static snd_pcm_fast_ops_t snd_pcm_ioplug_fast_ops = {
        .mmap_commit = snd_pcm_ioplug_mmap_commit,
 };
 
-static int ioplug_set_parm_minmax(struct ioplug_parm *parm, unsigned int min, unsigned int max)
-{
-       parm->num_list = 0;
-       free(parm->list);
-       parm->list = NULL;
-       parm->min = min;
-       parm->max = max;
-       parm->active = 1;
-       return 0;
-}
-
-static int val_compar(const void *ap, const void *bp)
-{
-       return *(const unsigned int *)ap - *(const unsigned int *)bp;
-}
-
-static int ioplug_set_parm_list(struct ioplug_parm *parm, unsigned int num_list, const unsigned int *list)
-{
-       unsigned int *new_list;
-
-       new_list = malloc(sizeof(*new_list) * num_list);
-       if (new_list == NULL)
-               return -ENOMEM;
-       memcpy(new_list, list, sizeof(*new_list) * num_list);
-       qsort(new_list, num_list, sizeof(*new_list), val_compar);
-
-       free(parm->list);
-       parm->num_list = num_list;
-       parm->list = new_list;
-       parm->active = 1;
-       return 0;
-}
-
 void snd_pcm_ioplug_params_reset(snd_pcm_ioplug_t *ioplug)
 {
        ioplug_priv_t *io = ioplug->pcm->private_data;
@@ -831,7 +715,7 @@ int snd_pcm_ioplug_set_param_list(snd_pcm_ioplug_t *ioplug, int type, unsigned i
        }
        if (type == SND_PCM_IOPLUG_HW_PERIODS)
                io->params[type].integer = 1;
-       return ioplug_set_parm_list(&io->params[type], num_list, list);
+       return snd_ext_parm_set_list(&io->params[type], num_list, list);
 }
 
 int snd_pcm_ioplug_set_param_minmax(snd_pcm_ioplug_t *ioplug, int type, unsigned int min, unsigned int max)
@@ -847,7 +731,7 @@ int snd_pcm_ioplug_set_param_minmax(snd_pcm_ioplug_t *ioplug, int type, unsigned
        }
        if (type == SND_PCM_IOPLUG_HW_PERIODS)
                io->params[type].integer = 1;
-       return ioplug_set_parm_minmax(&io->params[type], min, max);
+       return snd_ext_parm_set_minmax(&io->params[type], min, max);
 }
 
 int snd_pcm_ioplug_reinit_status(snd_pcm_ioplug_t *ioplug)