OSDN Git Service

Exported ERR macros. Completed scopes architecture. Added defaults for {pcm,ctl,rawmi...
authorAbramo Bagnara <abramo@alsa-project.org>
Wed, 7 Mar 2001 12:36:05 +0000 (12:36 +0000)
committerAbramo Bagnara <abramo@alsa-project.org>
Wed, 7 Mar 2001 12:36:05 +0000 (12:36 +0000)
14 files changed:
include/header.h
include/local.h
include/pcm.h
src/control/control.c
src/pcm/Makefile.am
src/pcm/pcm.c
src/pcm/pcm_meter.c
src/pcm/pcm_meter.h
src/pcm/scopes/Makefile.am [new file with mode: 0644]
src/pcm/scopes/configure.in [new file with mode: 0644]
src/pcm/scopes/cvscompile [new file with mode: 0644]
src/pcm/scopes/level.c [new file with mode: 0644]
src/rawmidi/rawmidi.c
src/seq/seq.c

index 946755e..e0a2ae2 100644 (file)
 #include <fcntl.h>
 #include <assert.h>
 #include <sys/poll.h>
+#include <errno.h>
 
 #ifndef ATTRIBUTE_UNUSED
 #define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
 #endif
 
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
+#define SNDERR(...) snd_lib_error(__FILE__, __LINE__, __FUNCTION__, 0, __VA_ARGS__)
+#define SYSERR(...) snd_lib_error(__FILE__, __LINE__, __FUNCTION__, errno, __VA_ARGS__)
+#else
+#define SNDERR(args...) snd_lib_error(__FILE__, __LINE__, __FUNCTION__, 0, ##args)
+#define SYSERR(args...) snd_lib_error(__FILE__, __LINE__, __FUNCTION__, errno, ##args)
+#endif
+
index 28e7653..7cad3f4 100644 (file)
 #define _snd_hwdep_info sndrv_hwdep_info
 
 #include "asoundlib.h"
-#include <errno.h>
-
-#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
-#define SNDERR(...) snd_lib_error(__FILE__, __LINE__, __FUNCTION__, 0, __VA_ARGS__)
-#define SYSERR(...) snd_lib_error(__FILE__, __LINE__, __FUNCTION__, errno, __VA_ARGS__)
-#else
-#define SNDERR(args...) snd_lib_error(__FILE__, __LINE__, __FUNCTION__, 0, ##args)
-#define SYSERR(args...) snd_lib_error(__FILE__, __LINE__, __FUNCTION__, errno, ##args)
-#endif
 
 enum _snd_set_mode {
        SND_CHANGE,
index 7d2157e..8ae7333 100644 (file)
@@ -209,6 +209,8 @@ typedef struct _snd_pcm_channel_area {
        unsigned int step;              /* samples distance in bits */
 } snd_pcm_channel_area_t;
 
+typedef struct _snd_pcm_scope snd_pcm_scope_t;
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -331,6 +333,34 @@ ssize_t snd_pcm_frames_to_bytes(snd_pcm_t *pcm, snd_pcm_sframes_t frames);
 int snd_pcm_bytes_to_samples(snd_pcm_t *pcm, ssize_t bytes);
 ssize_t snd_pcm_samples_to_bytes(snd_pcm_t *pcm, int samples);
 
+/* meter */
+typedef struct _snd_pcm_scope_ops {
+       int (*enable)(snd_pcm_scope_t *scope);
+       void (*disable)(snd_pcm_scope_t *scope);
+       void (*start)(snd_pcm_scope_t *scope);
+       void (*stop)(snd_pcm_scope_t *scope);
+       void (*update)(snd_pcm_scope_t *scope);
+       void (*reset)(snd_pcm_scope_t *scope);
+       void (*close)(snd_pcm_scope_t *scope);
+} snd_pcm_scope_ops_t;
+
+snd_pcm_uframes_t snd_pcm_meter_get_bufsize(snd_pcm_t *pcm);
+unsigned int snd_pcm_meter_get_channels(snd_pcm_t *pcm);
+unsigned int snd_pcm_meter_get_rate(snd_pcm_t *pcm);
+snd_pcm_uframes_t snd_pcm_meter_get_now(snd_pcm_t *pcm);
+snd_pcm_uframes_t snd_pcm_meter_get_boundary(snd_pcm_t *pcm);
+int snd_pcm_meter_add_scope(snd_pcm_t *pcm, snd_pcm_scope_t *scope);
+snd_pcm_scope_t *snd_pcm_meter_search_scope(snd_pcm_t *pcm, const char *name);
+int snd_pcm_scope_malloc(snd_pcm_scope_t **ptr);
+void snd_pcm_scope_set_ops(snd_pcm_scope_t *scope, snd_pcm_scope_ops_t *val);
+void snd_pcm_scope_set_name(snd_pcm_scope_t *scope, const char *val);
+const char *snd_pcm_scope_get_name(snd_pcm_scope_t *scope);
+void *snd_pcm_scope_get_callback_private(snd_pcm_scope_t *scope);
+void snd_pcm_scope_set_callback_private(snd_pcm_scope_t *scope, void *val);
+int snd_pcm_scope_s16_open(snd_pcm_t *pcm, const char *name,
+                          snd_pcm_scope_t **scopep);
+int16_t *snd_pcm_scope_s16_get_channel_buffer(snd_pcm_scope_t *scope,
+                                             unsigned int channel);
 
 /* misc */
 
index ef80066..292563e 100644 (file)
@@ -197,6 +197,7 @@ int snd_ctl_wait(snd_ctl_t *ctl, int timeout)
 int snd_ctl_open(snd_ctl_t **ctlp, const char *name)
 {
        const char *str;
+       char buf[256];
        int err;
        snd_config_t *ctl_conf, *conf, *type_conf;
        snd_config_iterator_t i, next;
@@ -229,36 +230,45 @@ int snd_ctl_open(snd_ctl_t **ctlp, const char *name)
        if (err < 0)
                return err;
        err = snd_config_searchv(snd_config, &type_conf, "ctltype", str, 0);
-       snd_config_for_each(i, next, type_conf) {
-               snd_config_t *n = snd_config_iterator_entry(i);
-               const char *id = snd_config_get_id(n);
-               if (strcmp(id, "comment") == 0)
-                       continue;
-               if (strcmp(id, "lib") == 0) {
-                       err = snd_config_get_string(n, &lib);
-                       if (err < 0)
-                               return -EINVAL;
-                       continue;
-               }
-               if (strcmp(id, "open") == 0) {
-                       err = snd_config_get_string(n, &open);
-                       if (err < 0)
-                               return -EINVAL;
-                       continue;
+       if (err >= 0) {
+               snd_config_for_each(i, next, type_conf) {
+                       snd_config_t *n = snd_config_iterator_entry(i);
+                       const char *id = snd_config_get_id(n);
+                       if (strcmp(id, "comment") == 0)
+                               continue;
+                       if (strcmp(id, "lib") == 0) {
+                               err = snd_config_get_string(n, &lib);
+                               if (err < 0)
+                                       return -EINVAL;
+                               continue;
+                       }
+                       if (strcmp(id, "open") == 0) {
+                               err = snd_config_get_string(n, &open);
+                               if (err < 0)
+                                       return -EINVAL;
+                               continue;
+                       }
+                       SNDERR("Unknown field %s", id);
                        return -EINVAL;
                }
        }
-       if (!open)
-               return -EINVAL;
+       if (!open) {
+               open = buf;
+               snprintf(buf, sizeof(buf), "_snd_ctl_%s_open", str);
+       }
        if (!lib)
                lib = "libasound.so";
        h = dlopen(lib, RTLD_NOW);
-       if (!h)
+       if (!h) {
+               SNDERR("Cannot open shared library %s", lib);
                return -ENOENT;
+       }
        open_func = dlsym(h, open);
-       dlclose(h);
-       if (!open_func)
+       if (!open_func) {
+               SNDERR("symbol %s is not defined inside %s", open, lib);
+               dlclose(h);
                return -ENXIO;
+       }
        return open_func(ctlp, name, ctl_conf);
 }
 
index bcf317d..47fccb7 100644 (file)
@@ -8,7 +8,7 @@ libpcm_la_SOURCES = atomic.c mask.c interval.c \
                    pcm_shm.c pcm_file.c pcm_share.c pcm_null.c pcm_meter.c \
                    pcm_params.c
 noinst_HEADERS = atomic.h pcm_local.h pcm_plugin.h mask.h mask_inline.h \
-                interval.h interval_inline.h plugin_ops.h pcm_meter.h
+                interval.h interval_inline.h plugin_ops.h
 
 all: libpcm.la
 
index 6c29456..1effe00 100644 (file)
@@ -555,8 +555,9 @@ int snd_pcm_open(snd_pcm_t **pcmp, const char *name,
                 snd_pcm_stream_t stream, int mode)
 {
        const char *str;
+       char buf[256];
        int err;
-       snd_config_t *pcm_conf, *conf, *type_conf;
+       snd_config_t *pcm_conf, *conf, *type_conf = NULL;
        snd_config_iterator_t i, next;
        const char *lib = NULL, *open = NULL;
        int (*open_func)(snd_pcm_t **pcmp, const char *name, snd_config_t *conf, 
@@ -638,37 +639,35 @@ int snd_pcm_open(snd_pcm_t **pcmp, const char *name,
                return err;
        }
        err = snd_config_searchv(snd_config, &type_conf, "pcmtype", str, 0);
-       if (err < 0) {
-               SNDERR("Unknown PCM type %s", str);
-               return err;
-       }
-       snd_config_for_each(i, next, type_conf) {
-               snd_config_t *n = snd_config_iterator_entry(i);
-               const char *id = snd_config_get_id(n);
-               if (strcmp(id, "comment") == 0)
-                       continue;
-               if (strcmp(id, "lib") == 0) {
-                       err = snd_config_get_string(n, &lib);
-                       if (err < 0) {
-                               SNDERR("Invalid type for %s", id);
-                               return -EINVAL;
+       if (err >= 0) {
+               snd_config_for_each(i, next, type_conf) {
+                       snd_config_t *n = snd_config_iterator_entry(i);
+                       const char *id = snd_config_get_id(n);
+                       if (strcmp(id, "comment") == 0)
+                               continue;
+                       if (strcmp(id, "lib") == 0) {
+                               err = snd_config_get_string(n, &lib);
+                               if (err < 0) {
+                                       SNDERR("Invalid type for %s", id);
+                                       return -EINVAL;
+                               }
+                               continue;
                        }
-                       continue;
-               }
-               if (strcmp(id, "open") == 0) {
-                       err = snd_config_get_string(n, &open);
-                       if (err < 0) {
-                               SNDERR("Invalid type for %s", id);
-                               return -EINVAL;
+                       if (strcmp(id, "open") == 0) {
+                               err = snd_config_get_string(n, &open);
+                               if (err < 0) {
+                                       SNDERR("Invalid type for %s", id);
+                                       return -EINVAL;
+                               }
+                               continue;
                        }
-                       continue;
                        SNDERR("Unknown field %s", id);
                        return -EINVAL;
                }
        }
        if (!open) {
-               SNDERR("open is not defined");
-               return -EINVAL;
+               open = buf;
+               snprintf(buf, sizeof(buf), "_snd_pcm_%s_open", str);
        }
        if (!lib)
                lib = "libasound.so";
@@ -678,9 +677,9 @@ int snd_pcm_open(snd_pcm_t **pcmp, const char *name,
                return -ENOENT;
        }
        open_func = dlsym(h, open);
-       dlclose(h);
        if (!open_func) {
                SNDERR("symbol %s is not defined inside %s", open, lib);
+               dlclose(h);
                return -ENXIO;
        }
        return open_func(pcmp, name, pcm_conf, stream, mode);
index ffd7301..ef8ea0d 100644 (file)
   
 #include <byteswap.h>
 #include <time.h>
-#include "pcm_meter.h"
+#include <pthread.h>
+#include <asm/atomic.h>
+#include <dlfcn.h>
+#include "list.h"
+#include "pcm_local.h"
+#include "pcm_plugin.h"
 
 #define FREQUENCY 50
 
-typedef struct _snd_pcm_meter_s16 {
-       snd_pcm_adpcm_state_t *adpcm_states;
-       unsigned int index;
-       snd_pcm_uframes_t old;
-} snd_pcm_meter_s16_t;
-
-int s16_open(snd_pcm_meter_scope_t *scope)
-{
-       snd_pcm_meter_t *meter = scope->pcm->private_data;
-       snd_pcm_t *spcm = meter->slave;
-       snd_pcm_channel_area_t *a;
-       unsigned int c;
-       snd_pcm_meter_s16_t *s16;
-       int index;
-       if (spcm->format == SND_PCM_FORMAT_S16 &&
-           spcm->access == SND_PCM_ACCESS_MMAP_NONINTERLEAVED) {
-               meter->buf16 = (int16_t *) meter->buf;
-               return -EINVAL;
-       }
-       switch (spcm->format) {
-       case SND_PCM_FORMAT_A_LAW:
-       case SND_PCM_FORMAT_MU_LAW:
-       case SND_PCM_FORMAT_IMA_ADPCM:
-               index = snd_pcm_linear_put_index(SND_PCM_FORMAT_S16, SND_PCM_FORMAT_S16);
-               break;
-       case SND_PCM_FORMAT_S8:
-       case SND_PCM_FORMAT_S16_LE:
-       case SND_PCM_FORMAT_S16_BE:
-       case SND_PCM_FORMAT_S24_LE:
-       case SND_PCM_FORMAT_S24_BE:
-       case SND_PCM_FORMAT_S32_LE:
-       case SND_PCM_FORMAT_S32_BE:
-       case SND_PCM_FORMAT_U8:
-       case SND_PCM_FORMAT_U16_LE:
-       case SND_PCM_FORMAT_U16_BE:
-       case SND_PCM_FORMAT_U24_LE:
-       case SND_PCM_FORMAT_U24_BE:
-       case SND_PCM_FORMAT_U32_LE:
-       case SND_PCM_FORMAT_U32_BE:
-               index = snd_pcm_linear_convert_index(spcm->format, SND_PCM_FORMAT_S16);
-               break;
-       default:
-               return -EINVAL;
-       }
-       s16 = calloc(1, sizeof(*s16));
-       if (!s16)
-               return -ENOMEM;
-       s16->index = index;
-       if (spcm->format == SND_PCM_FORMAT_IMA_ADPCM) {
-               s16->adpcm_states = calloc(spcm->channels, sizeof(*s16->adpcm_states));
-               if (!s16->adpcm_states) {
-                       free(s16);
-                       return -ENOMEM;
-               }
-       }
-       meter->buf16 = malloc(meter->buf_size * 2 * spcm->channels);
-       if (!meter->buf16) {
-               if (s16->adpcm_states)
-                       free(s16->adpcm_states);
-               free(s16);
-               return -ENOMEM;
-       }
-       a = calloc(spcm->channels, sizeof(*a));
-       if (!a) {
-               free(meter->buf16);
-               if (s16->adpcm_states)
-                       free(s16->adpcm_states);
-               free(s16);
-               return -ENOMEM;
-       }
-       meter->buf16_areas = a;
-       for (c = 0; c < spcm->channels; c++, a++) {
-               a->addr = meter->buf16 + c * meter->buf_size;
-               a->first = 0;
-               a->step = 16;
-       }
-       scope->private_data = s16;
-       return 0;
-}
-
-void s16_close(snd_pcm_meter_scope_t *scope)
-{
-       snd_pcm_meter_t *meter = scope->pcm->private_data;
-       snd_pcm_meter_s16_t *s16 = scope->private_data;
-       if (s16->adpcm_states)
-               free(s16->adpcm_states);
-       free(s16);
-       free(meter->buf16);
-       meter->buf16 = NULL;
-       free(meter->buf16_areas);
-}
-
-void s16_start(snd_pcm_meter_scope_t *scope ATTRIBUTE_UNUSED)
-{
-}
-
-void s16_stop(snd_pcm_meter_scope_t *scope ATTRIBUTE_UNUSED)
-{
-}
-
-void s16_update(snd_pcm_meter_scope_t *scope)
-{
-       snd_pcm_meter_t *meter = scope->pcm->private_data;
-       snd_pcm_meter_s16_t *s16 = scope->private_data;
-       snd_pcm_t *spcm = meter->slave;
-       snd_pcm_sframes_t size;
-       snd_pcm_uframes_t offset;
-       size = meter->now - s16->old;
-       if (size < 0)
-               size += spcm->boundary;
-       offset = s16->old % meter->buf_size;
-       while (size > 0) {
-               snd_pcm_uframes_t frames = size;
-               snd_pcm_uframes_t cont = meter->buf_size - offset;
-               if (frames > cont)
-                       frames = cont;
-               switch (spcm->format) {
-               case SND_PCM_FORMAT_A_LAW:
-                       snd_pcm_alaw_decode(meter->buf16_areas, offset,
-                                           meter->buf_areas, offset,
-                                           spcm->channels, frames,
-                                           s16->index);
-                       break;
-               case SND_PCM_FORMAT_MU_LAW:
-                       snd_pcm_mulaw_decode(meter->buf16_areas, offset,
-                                            meter->buf_areas, offset,
-                                            spcm->channels, frames,
-                                            s16->index);
-                       break;
-               case SND_PCM_FORMAT_IMA_ADPCM:
-                       snd_pcm_adpcm_decode(meter->buf16_areas, offset,
-                                            meter->buf_areas, offset,
-                                            spcm->channels, frames,
-                                            s16->index,
-                                            s16->adpcm_states);
-                       break;
-               default:
-                       snd_pcm_linear_convert(meter->buf16_areas, offset,
-                                              meter->buf_areas, offset,
-                                              spcm->channels, frames,
-                                              s16->index);
-                       break;
-               }
-               if (frames == cont)
-                       offset = 0;
-               else
-                       offset += frames;
-               size -= frames;
-       }
-       s16->old = meter->now;
-}
-
-void s16_reset(snd_pcm_meter_scope_t *scope)
-{
-       snd_pcm_meter_t *meter = scope->pcm->private_data;
-       snd_pcm_meter_s16_t *s16 = scope->private_data;
-       s16->old = meter->now;
-}
-
-snd_pcm_meter_scope_t s16_scope = {
-       name: "s16",
-       open: s16_open,
-       start: s16_start,
-       stop: s16_stop,
-       update: s16_update,
-       reset: s16_reset,
-       close: s16_close,
-       pcm: NULL,
-       active: 0,
-       list: { 0, 0 },
-       private_data: NULL,
+struct _snd_pcm_scope {
+       int enabled;
+       const char *name;
+       snd_pcm_scope_ops_t *ops;
+       void *private_data;
+       struct list_head list;
 };
 
-void snd_pcm_meter_add_scope(snd_pcm_t *pcm, snd_pcm_meter_scope_t *scope)
-{
-       snd_pcm_meter_t *meter = pcm->private_data;
-       scope->pcm = pcm;
-       list_add_tail(&scope->list, &meter->scopes);
-}
+typedef struct _snd_pcm_meter {
+       snd_pcm_t *slave;
+       int close_slave;
+       snd_pcm_uframes_t rptr;
+       snd_pcm_uframes_t buf_size;
+       snd_pcm_channel_area_t *buf_areas;
+       snd_pcm_uframes_t now;
+       unsigned char *buf;
+       struct list_head scopes;
+       int closed;
+       int running;
+       atomic_t reset;
+       pthread_t thread;
+       pthread_mutex_t update_mutex;
+       pthread_mutex_t running_mutex;
+       pthread_cond_t running_cond;
+       struct timespec delay;
+} snd_pcm_meter_t;
 
 void snd_pcm_meter_add_frames(snd_pcm_t *pcm,
                              const snd_pcm_channel_area_t *areas,
@@ -284,18 +136,59 @@ static int snd_pcm_meter_update_scope(snd_pcm_t *pcm)
        return reset;
 }
 
+int snd_pcm_scope_remove(snd_pcm_scope_t *scope)
+{
+       if (scope->name)
+               free((void *)scope->name);
+       scope->ops->close(scope);
+       list_del(&scope->list);
+       free(scope);
+       return 0;
+}
+
+int snd_pcm_scope_enable(snd_pcm_scope_t *scope)
+{
+       int err;
+       assert(!scope->enabled);
+       err = scope->ops->enable(scope);
+       scope->enabled = (err >= 0);
+       return err;
+}
+
+int snd_pcm_scope_disable(snd_pcm_scope_t *scope)
+{
+       assert(scope->enabled);
+       scope->ops->disable(scope);
+       scope->enabled = 0;
+       return 0;
+}
+
+snd_pcm_scope_t *snd_pcm_meter_search_scope(snd_pcm_t *pcm, const char *name)
+{
+       snd_pcm_meter_t *meter;
+       struct list_head *pos;
+       assert(pcm->type == SND_PCM_TYPE_METER);
+       meter = pcm->private_data;
+       list_for_each(pos, &meter->scopes) {
+               snd_pcm_scope_t *scope;
+               scope = list_entry(pos, snd_pcm_scope_t, list);
+               if (scope->name && strcmp(scope->name, name) == 0)
+                       return scope;
+       }
+       return NULL;
+}
+
 static void *snd_pcm_meter_thread(void *data)
 {
        snd_pcm_t *pcm = data;
        snd_pcm_meter_t *meter = pcm->private_data;
        snd_pcm_t *spcm = meter->slave;
        struct list_head *pos;
-       snd_pcm_meter_scope_t *scope;
-       int err, reset;
+       snd_pcm_scope_t *scope;
+       int reset;
        list_for_each(pos, &meter->scopes) {
-               scope = list_entry(pos, snd_pcm_meter_scope_t, list);
-               err = scope->open(scope);
-               scope->active = (err >= 0);
+               scope = list_entry(pos, snd_pcm_scope_t, list);
+               snd_pcm_scope_enable(scope);
        }
        while (!meter->closed) {
                snd_pcm_sframes_t now;
@@ -309,8 +202,8 @@ static void *snd_pcm_meter_thread(void *data)
                     spcm->stream != SND_PCM_STREAM_PLAYBACK)) {
                        if (meter->running) {
                                list_for_each(pos, &meter->scopes) {
-                                       scope = list_entry(pos, snd_pcm_meter_scope_t, list);
-                                       scope->stop(scope);
+                                       scope = list_entry(pos, snd_pcm_scope_t, list);
+                                       scope->ops->stop(scope);
                                }
                                meter->running = 0;
                        }
@@ -341,45 +234,50 @@ static void *snd_pcm_meter_thread(void *data)
                }
                if (reset) {
                        list_for_each(pos, &meter->scopes) {
-                               scope = list_entry(pos, snd_pcm_meter_scope_t, list);
-                               if (scope->active)
-                                       scope->reset(scope);
+                               scope = list_entry(pos, snd_pcm_scope_t, list);
+                               if (scope->enabled)
+                                       scope->ops->reset(scope);
                        }
                        continue;
                }
                if (!meter->running) {
                        list_for_each(pos, &meter->scopes) {
-                               scope = list_entry(pos, snd_pcm_meter_scope_t, list);
-                               if (scope->active)
-                                       scope->start(scope);
+                               scope = list_entry(pos, snd_pcm_scope_t, list);
+                               if (scope->enabled)
+                                       scope->ops->start(scope);
                        }
                        meter->running = 1;
                }
                list_for_each(pos, &meter->scopes) {
-                       scope = list_entry(pos, snd_pcm_meter_scope_t, list);
-                       if (scope->active)
-                               scope->update(scope);
+                       scope = list_entry(pos, snd_pcm_scope_t, list);
+                       if (scope->enabled)
+                               scope->ops->update(scope);
                }
                nanosleep(&meter->delay, NULL);
        }
        list_for_each(pos, &meter->scopes) {
-               scope = list_entry(pos, snd_pcm_meter_scope_t, list);
-               if (scope->active)
-                       scope->close(scope);
+               scope = list_entry(pos, snd_pcm_scope_t, list);
+               if (scope->enabled)
+                       snd_pcm_scope_disable(scope);
        }
        return NULL;
 }
 
-
 static int snd_pcm_meter_close(snd_pcm_t *pcm)
 {
        snd_pcm_meter_t *meter = pcm->private_data;
+       struct list_head *pos, *npos;
        int err = 0;
        pthread_mutex_destroy(&meter->update_mutex);
        pthread_mutex_destroy(&meter->running_mutex);
        pthread_cond_destroy(&meter->running_cond);
        if (meter->close_slave)
                err = snd_pcm_close(meter->slave);
+       list_for_each_safe(pos, npos, &meter->scopes) {
+               snd_pcm_scope_t *scope;
+               scope = list_entry(pos, snd_pcm_scope_t, list);
+               snd_pcm_scope_remove(scope);
+       }
        free(meter);
        return err;
 }
@@ -735,21 +633,89 @@ int snd_pcm_meter_open(snd_pcm_t **pcmp, const char *name, unsigned int frequenc
        pthread_mutex_init(&meter->update_mutex, NULL);
        pthread_mutex_init(&meter->running_mutex, NULL);
        pthread_cond_init(&meter->running_cond, NULL);
-#if 1
-       snd_pcm_meter_add_scope(pcm, &s16_scope);
-#endif
        return 0;
 }
 
+
+int snd_pcm_meter_add_scope_conf(snd_pcm_t *pcm, const char *name,
+                                snd_config_t *conf)
+{
+       char buf[256];
+       snd_config_iterator_t i, next;
+       const char *lib = NULL, *open = NULL, *str = NULL;
+       snd_config_t *c, *type_conf;
+       int (*open_func)(snd_pcm_t *pcm, const char *name,
+                        snd_config_t *conf);
+       void *h;
+       int err;
+       err = snd_config_search(conf, "type", &c);
+       if (err < 0) {
+               SNDERR("type is not defined");
+               return err;
+       }
+       err = snd_config_get_string(c, &str);
+       if (err < 0) {
+               SNDERR("Invalid type for %s", snd_config_get_id(c));
+               return err;
+       }
+       err = snd_config_searchv(snd_config, &type_conf, "scopetype", str, 0);
+       if (err >= 0) {
+               snd_config_for_each(i, next, type_conf) {
+                       snd_config_t *n = snd_config_iterator_entry(i);
+                       const char *id = snd_config_get_id(n);
+                       if (strcmp(id, "comment") == 0)
+                               continue;
+                       if (strcmp(id, "lib") == 0) {
+                               err = snd_config_get_string(n, &lib);
+                               if (err < 0) {
+                                       SNDERR("Invalid type for %s", id);
+                                       return -EINVAL;
+                               }
+                               continue;
+                       }
+                       if (strcmp(id, "open") == 0) {
+                               err = snd_config_get_string(n, &open);
+                               if (err < 0) {
+                                       SNDERR("Invalid type for %s", id);
+                                       return -EINVAL;
+                               }
+                               continue;
+                       }
+                       SNDERR("Unknown field %s", id);
+                       return -EINVAL;
+               }
+       }
+       if (!open) {
+               open = buf;
+               snprintf(buf, sizeof(buf), "_snd_pcm_scope_%s_open", str);
+       }
+       if (!lib)
+               lib = "libasound.so";
+       h = dlopen(lib, RTLD_NOW);
+       if (!h) {
+               SNDERR("Cannot open shared library %s", lib);
+               return -ENOENT;
+       }
+       open_func = dlsym(h, open);
+       if (!open_func) {
+               SNDERR("symbol %s is not defined inside %s", open, lib);
+               dlclose(h);
+               return -ENXIO;
+       }
+       return open_func(pcm, name, conf);
+}
+
+                       
 int _snd_pcm_meter_open(snd_pcm_t **pcmp, const char *name,
-                      snd_config_t *conf, 
-                      snd_pcm_stream_t stream, int mode)
+                       snd_config_t *conf, 
+                       snd_pcm_stream_t stream, int mode)
 {
        snd_config_iterator_t i, next;
        const char *sname = NULL;
        int err;
        snd_pcm_t *spcm;
        long frequency = -1;
+       snd_config_t *scopes = NULL;
        snd_config_for_each(i, next, conf) {
                snd_config_t *n = snd_config_iterator_entry(i);
                const char *id = snd_config_get_id(n);
@@ -773,6 +739,14 @@ int _snd_pcm_meter_open(snd_pcm_t **pcmp, const char *name,
                        }
                        continue;
                }
+               if (strcmp(id, "scope") == 0) {
+                       if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
+                               SNDERR("Invalid type for %s", id);
+                               return -EINVAL;
+                       }
+                       scopes = n;
+                       continue;
+               }
                SNDERR("Unknown field %s", id);
                return -EINVAL;
        }
@@ -792,5 +766,315 @@ int _snd_pcm_meter_open(snd_pcm_t **pcmp, const char *name,
        err = snd_pcm_meter_open(pcmp, name, frequency < 0 ? FREQUENCY : frequency, spcm, 1);
        if (err < 0)
                snd_pcm_close(spcm);
-       return err;
+       if (!scopes)
+               return 0;
+       snd_config_for_each(i, next, scopes) {
+               snd_config_t *n = snd_config_iterator_entry(i);
+               const char *id = snd_config_get_id(n);
+               err = snd_pcm_meter_add_scope_conf(*pcmp, id, n);
+               if (err < 0) {
+                       snd_pcm_close(*pcmp);
+                       return -EINVAL;
+               }
+       }
+       return 0;
+}
+
+int snd_pcm_meter_add_scope(snd_pcm_t *pcm, snd_pcm_scope_t *scope)
+{
+       snd_pcm_meter_t *meter;
+       assert(pcm->type == SND_PCM_TYPE_METER);
+       meter = pcm->private_data;
+       list_add_tail(&scope->list, &meter->scopes);
+       return 0;
+}
+
+snd_pcm_uframes_t snd_pcm_meter_get_bufsize(snd_pcm_t *pcm)
+{
+       snd_pcm_meter_t *meter;
+       assert(pcm->type == SND_PCM_TYPE_METER);
+       meter = pcm->private_data;
+       return meter->buf_size;
+}
+
+unsigned int snd_pcm_meter_get_channels(snd_pcm_t *pcm)
+{
+       snd_pcm_meter_t *meter;
+       assert(pcm->type == SND_PCM_TYPE_METER);
+       meter = pcm->private_data;
+       assert(meter->slave->setup);
+       return meter->slave->channels;
+}
+
+unsigned int snd_pcm_meter_get_rate(snd_pcm_t *pcm)
+{
+       snd_pcm_meter_t *meter;
+       assert(pcm->type == SND_PCM_TYPE_METER);
+       meter = pcm->private_data;
+       assert(meter->slave->setup);
+       return meter->slave->rate;
+}
+
+snd_pcm_uframes_t snd_pcm_meter_get_now(snd_pcm_t *pcm)
+{
+       snd_pcm_meter_t *meter;
+       assert(pcm->type == SND_PCM_TYPE_METER);
+       meter = pcm->private_data;
+       assert(meter->slave->setup);
+       return meter->now;
+}
+
+snd_pcm_uframes_t snd_pcm_meter_get_boundary(snd_pcm_t *pcm)
+{
+       snd_pcm_meter_t *meter;
+       assert(pcm->type == SND_PCM_TYPE_METER);
+       meter = pcm->private_data;
+       assert(meter->slave->setup);
+       return meter->slave->boundary;
+}
+
+void snd_pcm_scope_set_name(snd_pcm_scope_t *scope, const char *val)
+{
+       scope->name = val;
+}
+
+const char *snd_pcm_scope_get_name(snd_pcm_scope_t *scope)
+{
+       return scope->name;
+}
+
+void snd_pcm_scope_set_ops(snd_pcm_scope_t *scope, snd_pcm_scope_ops_t *val)
+{
+       scope->ops = val;
+}
+
+void *snd_pcm_scope_get_callback_private(snd_pcm_scope_t *scope)
+{
+       return scope->private_data;
+}
+
+void snd_pcm_scope_set_callback_private(snd_pcm_scope_t *scope, void *val)
+{
+       scope->private_data = val;
 }
+
+typedef struct _snd_pcm_scope_s16 {
+       snd_pcm_t *pcm;
+       snd_pcm_adpcm_state_t *adpcm_states;
+       unsigned int index;
+       snd_pcm_uframes_t old;
+       int16_t *buf;
+       snd_pcm_channel_area_t *buf_areas;
+} snd_pcm_scope_s16_t;
+
+static int s16_enable(snd_pcm_scope_t *scope)
+{
+       snd_pcm_scope_s16_t *s16 = scope->private_data;
+       snd_pcm_meter_t *meter = s16->pcm->private_data;
+       snd_pcm_t *spcm = meter->slave;
+       snd_pcm_channel_area_t *a;
+       unsigned int c;
+       int index;
+       if (spcm->format == SND_PCM_FORMAT_S16 &&
+           spcm->access == SND_PCM_ACCESS_MMAP_NONINTERLEAVED) {
+               s16->buf = (int16_t *) meter->buf;
+               return -EINVAL;
+       }
+       switch (spcm->format) {
+       case SND_PCM_FORMAT_A_LAW:
+       case SND_PCM_FORMAT_MU_LAW:
+       case SND_PCM_FORMAT_IMA_ADPCM:
+               index = snd_pcm_linear_put_index(SND_PCM_FORMAT_S16, SND_PCM_FORMAT_S16);
+               break;
+       case SND_PCM_FORMAT_S8:
+       case SND_PCM_FORMAT_S16_LE:
+       case SND_PCM_FORMAT_S16_BE:
+       case SND_PCM_FORMAT_S24_LE:
+       case SND_PCM_FORMAT_S24_BE:
+       case SND_PCM_FORMAT_S32_LE:
+       case SND_PCM_FORMAT_S32_BE:
+       case SND_PCM_FORMAT_U8:
+       case SND_PCM_FORMAT_U16_LE:
+       case SND_PCM_FORMAT_U16_BE:
+       case SND_PCM_FORMAT_U24_LE:
+       case SND_PCM_FORMAT_U24_BE:
+       case SND_PCM_FORMAT_U32_LE:
+       case SND_PCM_FORMAT_U32_BE:
+               index = snd_pcm_linear_convert_index(spcm->format, SND_PCM_FORMAT_S16);
+               break;
+       default:
+               return -EINVAL;
+       }
+       s16->index = index;
+       if (spcm->format == SND_PCM_FORMAT_IMA_ADPCM) {
+               s16->adpcm_states = calloc(spcm->channels, sizeof(*s16->adpcm_states));
+               if (!s16->adpcm_states)
+                       return -ENOMEM;
+       }
+       s16->buf = malloc(meter->buf_size * 2 * spcm->channels);
+       if (!s16->buf) {
+               if (s16->adpcm_states)
+                       free(s16->adpcm_states);
+               return -ENOMEM;
+       }
+       a = calloc(spcm->channels, sizeof(*a));
+       if (!a) {
+               free(s16->buf);
+               if (s16->adpcm_states)
+                       free(s16->adpcm_states);
+               return -ENOMEM;
+       }
+       s16->buf_areas = a;
+       for (c = 0; c < spcm->channels; c++, a++) {
+               a->addr = s16->buf + c * meter->buf_size;
+               a->first = 0;
+               a->step = 16;
+       }
+       return 0;
+}
+
+static void s16_disable(snd_pcm_scope_t *scope)
+{
+       snd_pcm_scope_s16_t *s16 = scope->private_data;
+       if (s16->adpcm_states) {
+               free(s16->adpcm_states);
+               s16->adpcm_states = NULL;
+       }
+       free(s16->buf);
+       s16->buf = NULL;
+       free(s16->buf_areas);
+       s16->buf_areas = 0;
+}
+
+static void s16_close(snd_pcm_scope_t *scope)
+{
+       snd_pcm_scope_s16_t *s16 = scope->private_data;
+       free(s16);
+}
+
+static void s16_start(snd_pcm_scope_t *scope ATTRIBUTE_UNUSED)
+{
+}
+
+static void s16_stop(snd_pcm_scope_t *scope ATTRIBUTE_UNUSED)
+{
+}
+
+static void s16_update(snd_pcm_scope_t *scope)
+{
+       snd_pcm_scope_s16_t *s16 = scope->private_data;
+       snd_pcm_meter_t *meter = s16->pcm->private_data;
+       snd_pcm_t *spcm = meter->slave;
+       snd_pcm_sframes_t size;
+       snd_pcm_uframes_t offset;
+       size = meter->now - s16->old;
+       if (size < 0)
+               size += spcm->boundary;
+       offset = s16->old % meter->buf_size;
+       while (size > 0) {
+               snd_pcm_uframes_t frames = size;
+               snd_pcm_uframes_t cont = meter->buf_size - offset;
+               if (frames > cont)
+                       frames = cont;
+               switch (spcm->format) {
+               case SND_PCM_FORMAT_A_LAW:
+                       snd_pcm_alaw_decode(s16->buf_areas, offset,
+                                           meter->buf_areas, offset,
+                                           spcm->channels, frames,
+                                           s16->index);
+                       break;
+               case SND_PCM_FORMAT_MU_LAW:
+                       snd_pcm_mulaw_decode(s16->buf_areas, offset,
+                                            meter->buf_areas, offset,
+                                            spcm->channels, frames,
+                                            s16->index);
+                       break;
+               case SND_PCM_FORMAT_IMA_ADPCM:
+                       snd_pcm_adpcm_decode(s16->buf_areas, offset,
+                                            meter->buf_areas, offset,
+                                            spcm->channels, frames,
+                                            s16->index,
+                                            s16->adpcm_states);
+                       break;
+               default:
+                       snd_pcm_linear_convert(s16->buf_areas, offset,
+                                              meter->buf_areas, offset,
+                                              spcm->channels, frames,
+                                              s16->index);
+                       break;
+               }
+               if (frames == cont)
+                       offset = 0;
+               else
+                       offset += frames;
+               size -= frames;
+       }
+       s16->old = meter->now;
+}
+
+static void s16_reset(snd_pcm_scope_t *scope)
+{
+       snd_pcm_scope_s16_t *s16 = scope->private_data;
+       snd_pcm_meter_t *meter = s16->pcm->private_data;
+       s16->old = meter->now;
+}
+
+snd_pcm_scope_ops_t s16_ops = {
+       enable: s16_enable,
+       disable: s16_disable,
+       close: s16_close,
+       start: s16_start,
+       stop: s16_stop,
+       update: s16_update,
+       reset: s16_reset,
+};
+
+int snd_pcm_scope_s16_open(snd_pcm_t *pcm, const char *name,
+                          snd_pcm_scope_t **scopep)
+{
+       snd_pcm_meter_t *meter;
+       snd_pcm_scope_t *scope;
+       snd_pcm_scope_s16_t *s16;
+       assert(pcm->type == SND_PCM_TYPE_METER);
+       meter = pcm->private_data;
+       scope = calloc(1, sizeof(*scope));
+       if (!scope)
+               return -ENOMEM;
+       s16 = calloc(1, sizeof(*s16));
+       if (!s16) {
+               free(scope);
+               return -ENOMEM;
+       }
+       if (name)
+               scope->name = strdup(name);
+       s16->pcm = pcm;
+       scope->ops = &s16_ops;
+       scope->private_data = s16;
+       list_add_tail(&scope->list, &meter->scopes);
+       *scopep = scope;
+       return 0;
+}
+
+int16_t *snd_pcm_scope_s16_get_channel_buffer(snd_pcm_scope_t *scope,
+                                             unsigned int channel)
+{
+       snd_pcm_scope_s16_t *s16;
+       snd_pcm_meter_t *meter;
+       assert(scope->ops == &s16_ops);
+       s16 = scope->private_data;
+       meter = s16->pcm->private_data;
+       assert(meter->slave->setup);
+       assert(s16->buf_areas);
+       assert(channel < meter->slave->channels);
+       return s16->buf_areas[channel].addr;
+}
+
+int snd_pcm_scope_malloc(snd_pcm_scope_t **ptr)
+{
+       assert(ptr);
+       *ptr = calloc(1, sizeof(snd_pcm_scope_t));
+       if (!*ptr)
+               return -ENOMEM;
+       return 0;
+}
+
index bc1efa0..aef020f 100644 (file)
  *
  */
 
-#include <pthread.h>
-#include <asm/atomic.h>
-#include "list.h"
-#include "pcm_local.h"
-#include "pcm_plugin.h"
-
-typedef struct _snd_pcm_meter_scope snd_pcm_meter_scope_t;
-
-struct _snd_pcm_meter_scope {
-       snd_pcm_t *pcm;
-       int active;
-       char *name;
-       int (*open)(snd_pcm_meter_scope_t *scope);
-       void (*start)(snd_pcm_meter_scope_t *scope);
-       void (*stop)(snd_pcm_meter_scope_t *scope);
-       void (*update)(snd_pcm_meter_scope_t *scope);
-       void (*reset)(snd_pcm_meter_scope_t *scope);
-       void (*close)(snd_pcm_meter_scope_t *scope);
-       void *private_data;
-       struct list_head list;
-};
-
-typedef struct _snd_pcm_meter {
-       snd_pcm_t *slave;
-       int close_slave;
-       snd_pcm_uframes_t rptr;
-       snd_pcm_uframes_t buf_size;
-       snd_pcm_channel_area_t *buf_areas;
-       snd_pcm_uframes_t now;
-       unsigned char *buf;
-       struct list_head scopes;
-       int closed;
-       int running;
-       atomic_t reset;
-       pthread_t thread;
-       pthread_mutex_t update_mutex;
-       pthread_mutex_t running_mutex;
-       pthread_cond_t running_cond;
-       int16_t *buf16;
-       snd_pcm_channel_area_t *buf16_areas;
-       struct timespec delay;
-} snd_pcm_meter_t;
 
diff --git a/src/pcm/scopes/Makefile.am b/src/pcm/scopes/Makefile.am
new file mode 100644 (file)
index 0000000..634d180
--- /dev/null
@@ -0,0 +1,7 @@
+
+CFLAGS = -g -O2 -W -Wall
+
+lib_LTLIBRARIES = liblevel.la
+
+liblevel_la_SOURCES = level.c
+liblevel_la_LIBADD = -lncurses
diff --git a/src/pcm/scopes/configure.in b/src/pcm/scopes/configure.in
new file mode 100644 (file)
index 0000000..3e81615
--- /dev/null
@@ -0,0 +1,9 @@
+AC_INIT(level.c)
+AM_INIT_AUTOMAKE(scopes, 0.9)
+
+AC_PROG_CC
+AC_PROG_INSTALL
+AC_PROG_LN_S 
+AM_PROG_LIBTOOL
+
+AC_OUTPUT(Makefile)
diff --git a/src/pcm/scopes/cvscompile b/src/pcm/scopes/cvscompile
new file mode 100644 (file)
index 0000000..aa130d4
--- /dev/null
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+aclocal $ACLOCAL_FLAGS
+automake --foreign --add-missing
+autoconf
+export CFLAGS='-O2 -Wall -W -pipe -g'
+echo "CFLAGS=$CFLAGS"
+echo "./configure $@"
+./configure $@
+unset CFLAGS
+make
diff --git a/src/pcm/scopes/level.c b/src/pcm/scopes/level.c
new file mode 100644 (file)
index 0000000..750a28e
--- /dev/null
@@ -0,0 +1,269 @@
+/*
+ *  PCM - Meter level plugin (ncurses)
+ *  Copyright (c) 2001 by Abramo Bagnara <abramo@alsa-project.org>
+ *
+ *   This library is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU Library General Public License as
+ *   published by the Free Software Foundation; either version 2 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 Library General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Library General Public
+ *   License along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <curses.h>
+#include <errno.h>
+#include <sys/asoundlib.h>
+
+#define BAR_WIDTH 70
+/* milliseconds to go from 32767 to 0 */
+#define DECAY_MS 400
+/* milliseconds for peak to disappear */
+#define PEAK_MS 800
+
+typedef struct _snd_pcm_scope_level_channel {
+       int16_t level;
+       int16_t peak;
+       unsigned int peak_age;
+} snd_pcm_scope_level_channel_t;
+
+typedef struct _snd_pcm_scope_level {
+       snd_pcm_t *pcm;
+       snd_pcm_scope_t *s16;
+       snd_pcm_scope_level_channel_t *channels;
+       snd_pcm_uframes_t old;
+       int top;
+       WINDOW *win;
+       unsigned int bar_width;
+       unsigned int decay_ms;
+       unsigned int peak_ms;
+} snd_pcm_scope_level_t;
+
+static int level_enable(snd_pcm_scope_t *scope)
+{
+       snd_pcm_scope_level_t *level = snd_pcm_scope_get_callback_private(scope);
+       int y, x;
+       level->channels = calloc(snd_pcm_meter_get_channels(level->pcm), sizeof(*level->channels));
+       if (!level->channels) {
+               free(level);
+               return -ENOMEM;
+       }
+       snd_pcm_scope_set_callback_private(scope, level);
+       level->win = initscr();
+       winsdelln(level->win, snd_pcm_meter_get_channels(level->pcm));
+        getyx(level->win, y, x);
+       level->top = y;
+       return 0;
+}
+
+static void level_disable(snd_pcm_scope_t *scope)
+{
+       snd_pcm_scope_level_t *level = snd_pcm_scope_get_callback_private(scope);
+       endwin();
+       free(level->channels);
+}
+
+static void level_close(snd_pcm_scope_t *scope)
+{
+       snd_pcm_scope_level_t *level = snd_pcm_scope_get_callback_private(scope);
+       free(level);
+}
+
+static void level_start(snd_pcm_scope_t *scope ATTRIBUTE_UNUSED)
+{
+}
+
+static void level_stop(snd_pcm_scope_t *scope)
+{
+       snd_pcm_scope_level_t *level = snd_pcm_scope_get_callback_private(scope);
+       unsigned int c;
+       for (c = 0; c < snd_pcm_meter_get_channels(level->pcm); c++) {
+               move(level->top + c, 0);
+               clrtoeol();
+       }
+       move(level->top, 0);
+       refresh();
+}
+
+static void level_update(snd_pcm_scope_t *scope)
+{
+       snd_pcm_scope_level_t *level = snd_pcm_scope_get_callback_private(scope);
+       snd_pcm_t *pcm = level->pcm;
+       snd_pcm_sframes_t size;
+       snd_pcm_uframes_t size1, size2;
+       snd_pcm_uframes_t offset, cont;
+       unsigned int c, channels;
+       unsigned int ms;
+       static char bar[256] = { [0 ... 255] = '#' };
+       int max_decay;
+       size = snd_pcm_meter_get_now(pcm) - level->old;
+       if (size < 0)
+               size += snd_pcm_meter_get_boundary(pcm);
+       offset = level->old % snd_pcm_meter_get_bufsize(pcm);
+       cont = snd_pcm_meter_get_bufsize(pcm) - offset;
+       size1 = size;
+       if (size1 > cont)
+               size1 = cont;
+       size2 = size - size1;
+       ms = size * 1000 / snd_pcm_meter_get_rate(pcm);
+       max_decay = 32768 * ms / level->decay_ms;
+       channels = snd_pcm_meter_get_channels(pcm);
+       for (c = 0; c < channels; c++) {
+               int16_t *ptr;
+               int s, lev = 0;
+               snd_pcm_uframes_t n;
+               snd_pcm_scope_level_channel_t *l;
+               unsigned int lev_pos, peak_pos;
+               l = &level->channels[c];
+               ptr = snd_pcm_scope_s16_get_channel_buffer(level->s16, c) + offset;
+               for (n = size1; n > 0; n--) {
+                       s = *ptr;
+                       if (s < 0)
+                               s = -s;
+                       if (s > lev)
+                               lev = s;
+                       ptr++;
+               }
+               ptr = snd_pcm_scope_s16_get_channel_buffer(level->s16, c);
+               for (n = size2; n > 0; n--) {
+                       s = *ptr;
+                       if (s < 0)
+                               s = -s;
+                       if (s > lev)
+                               lev = s;
+                       ptr++;
+               }
+               l->level = lev;
+               l->peak_age += ms;
+               if (l->peak_age >= level->peak_ms ||
+                   lev >= l->peak) {
+                       l->peak = lev;
+                       l->peak_age = 0;
+               }
+               if (lev < l->level - max_decay)
+                       lev = l->level - max_decay;
+               move(level->top + c, 0);
+               lev_pos = lev * level->bar_width / 32768;
+               peak_pos = l->peak * level->bar_width / 32768;
+               addnstr(bar, lev_pos);
+               clrtoeol();
+               mvaddch(level->top + c, peak_pos - 1, '#');
+       }
+       move(level->top, 0);
+       refresh();
+       level->old = snd_pcm_meter_get_now(pcm);
+}
+
+static void level_reset(snd_pcm_scope_t *scope)
+{
+       snd_pcm_scope_level_t *level = snd_pcm_scope_get_callback_private(scope);
+       snd_pcm_t *pcm = level->pcm;
+       memset(level->channels, 0, snd_pcm_meter_get_channels(pcm) * sizeof(*level->channels));
+       level->old = snd_pcm_meter_get_now(pcm);
+}
+
+snd_pcm_scope_ops_t level_ops = {
+       enable: level_enable,
+       disable: level_disable,
+       close: level_close,
+       start: level_start,
+       stop: level_stop,
+       update: level_update,
+       reset: level_reset,
+};
+
+int snd_pcm_scope_level_open(snd_pcm_t *pcm, const char *name,
+                            unsigned int bar_width, unsigned int decay_ms,
+                            unsigned int peak_ms,
+                            snd_pcm_scope_t **scopep)
+{
+       snd_pcm_scope_t *scope, *s16;
+       snd_pcm_scope_level_t *level;
+       int err = snd_pcm_scope_malloc(&scope);
+       if (err < 0)
+               return err;
+       level = calloc(1, sizeof(*level));
+       if (!level) {
+               free(scope);
+               return -ENOMEM;
+       }
+       level->pcm = pcm;
+       level->bar_width = bar_width;
+       level->decay_ms = decay_ms;
+       level->peak_ms = peak_ms;
+       s16 = snd_pcm_meter_search_scope(pcm, "s16");
+       if (!s16) {
+               err = snd_pcm_scope_s16_open(pcm, "s16", &s16);
+               if (err < 0) {
+                       free(scope);
+                       free(level);
+                       return err;
+               }
+       }
+       level->s16 = s16;
+       snd_pcm_scope_set_ops(scope, &level_ops);
+       snd_pcm_scope_set_callback_private(scope, level);
+       if (name)
+               snd_pcm_scope_set_name(scope, strdup(name));
+       snd_pcm_meter_add_scope(pcm, scope);
+       *scopep = scope;
+       return 0;
+}
+
+int _snd_pcm_scope_level_open(snd_pcm_t *pcm, const char *name,
+                             snd_config_t *conf)
+{
+       snd_config_iterator_t i, next;
+       snd_pcm_scope_t *scope;
+       long bar_width = -1, decay_ms = -1, peak_ms = -1;
+       int err;
+       snd_config_for_each(i, next, conf) {
+               snd_config_t *n = snd_config_iterator_entry(i);
+               const char *id = snd_config_get_id(n);
+               if (strcmp(id, "comment") == 0)
+                       continue;
+               if (strcmp(id, "type") == 0)
+                       continue;
+               if (strcmp(id, "bar_width") == 0) {
+                       err = snd_config_get_integer(n, &bar_width);
+                       if (err < 0) {
+                               SNDERR("Invalid type for %s", id);
+                               return -EINVAL;
+                       }
+                       continue;
+               }
+               if (strcmp(id, "decay_ms") == 0) {
+                       err = snd_config_get_integer(n, &decay_ms);
+                       if (err < 0) {
+                               SNDERR("Invalid type for %s", id);
+                               return -EINVAL;
+                       }
+                       continue;
+               }
+               if (strcmp(id, "peak_ms") == 0) {
+                       err = snd_config_get_integer(n, &peak_ms);
+                       if (err < 0) {
+                               SNDERR("Invalid type for %s", id);
+                               return -EINVAL;
+                       }
+                       continue;
+               }
+               SNDERR("Unknown field %s", id);
+               return -EINVAL;
+       }
+       if (bar_width < 0)
+               bar_width = BAR_WIDTH;
+       if (decay_ms < 0)
+               decay_ms = DECAY_MS;
+       if (peak_ms < 0)
+               peak_ms = PEAK_MS;
+       return snd_pcm_scope_level_open(pcm, name, bar_width, decay_ms, peak_ms,
+                                       &scope);
+}
index 7fe4e54..8223755 100644 (file)
@@ -169,6 +169,7 @@ int snd_rawmidi_open(snd_rawmidi_t **inputp, snd_rawmidi_t **outputp,
                     const char *name, int mode)
 {
        const char *str;
+       char buf[256];
        int err;
        snd_config_t *rawmidi_conf, *conf, *type_conf;
        snd_config_iterator_t i, next;
@@ -208,37 +209,35 @@ int snd_rawmidi_open(snd_rawmidi_t **inputp, snd_rawmidi_t **outputp,
                return err;
        }
        err = snd_config_searchv(snd_config, &type_conf, "rawmiditype", str, 0);
-       if (err < 0) {
-               SNDERR("Unknown RAWMIDI type %s", str);
-               return err;
-       }
-       snd_config_for_each(i, next, type_conf) {
-               snd_config_t *n = snd_config_iterator_entry(i);
-               const char *id = snd_config_get_id(n);
-               if (strcmp(id, "comment") == 0)
-                       continue;
-               if (strcmp(id, "lib") == 0) {
-                       err = snd_config_get_string(n, &lib);
-                       if (err < 0) {
-                               SNDERR("Invalid type for %s", id);
-                               return -EINVAL;
+       if (err >= 0) {
+               snd_config_for_each(i, next, type_conf) {
+                       snd_config_t *n = snd_config_iterator_entry(i);
+                       const char *id = snd_config_get_id(n);
+                       if (strcmp(id, "comment") == 0)
+                               continue;
+                       if (strcmp(id, "lib") == 0) {
+                               err = snd_config_get_string(n, &lib);
+                               if (err < 0) {
+                                       SNDERR("Invalid type for %s", id);
+                                       return -EINVAL;
+                               }
+                               continue;
                        }
-                       continue;
-               }
-               if (strcmp(id, "open") == 0) {
-                       err = snd_config_get_string(n, &open);
-                       if (err < 0) {
-                               SNDERR("Invalid type for %s", id);
-                               return -EINVAL;
+                       if (strcmp(id, "open") == 0) {
+                               err = snd_config_get_string(n, &open);
+                               if (err < 0) {
+                                       SNDERR("Invalid type for %s", id);
+                                       return -EINVAL;
+                               }
+                               continue;
                        }
-                       continue;
                        SNDERR("Unknown field %s", id);
                        return -EINVAL;
                }
        }
        if (!open) {
-               SNDERR("open is not defined");
-               return -EINVAL;
+               open = buf;
+               snprintf(buf, sizeof(buf), "_snd_rawmidi_%s_open", str);
        }
        if (!lib)
                lib = "libasound.so";
@@ -248,9 +247,9 @@ int snd_rawmidi_open(snd_rawmidi_t **inputp, snd_rawmidi_t **outputp,
                return -ENOENT;
        }
        open_func = dlsym(h, open);
-       dlclose(h);
        if (!open_func) {
                SNDERR("symbol %s is not defined inside %s", open, lib);
+               dlclose(h);
                return -ENXIO;
        }
        err = open_func(inputp, outputp, name, rawmidi_conf, mode);
index b7e0328..f281b4a 100644 (file)
@@ -40,6 +40,7 @@ int snd_seq_open(snd_seq_t **seqp, const char *name,
                 int streams, int mode)
 {
        const char *str;
+       char buf[256];
        int err;
        snd_config_t *seq_conf, *conf, *type_conf;
        snd_config_iterator_t i, next;
@@ -73,37 +74,35 @@ int snd_seq_open(snd_seq_t **seqp, const char *name,
                return err;
        }
        err = snd_config_searchv(snd_config, &type_conf, "seqtype", str, 0);
-       if (err < 0) {
-               SNDERR("Unknown SEQ type %s", str);
-               return err;
-       }
-       snd_config_for_each(i, next, type_conf) {
-               snd_config_t *n = snd_config_iterator_entry(i);
-               const char *id = snd_config_get_id(n);
-               if (strcmp(id, "comment") == 0)
-                       continue;
-               if (strcmp(id, "lib") == 0) {
-                       err = snd_config_get_string(n, &lib);
-                       if (err < 0) {
-                               SNDERR("Invalid type for %s", id);
-                               return -EINVAL;
+       if (err >= 0) {
+               snd_config_for_each(i, next, type_conf) {
+                       snd_config_t *n = snd_config_iterator_entry(i);
+                       const char *id = snd_config_get_id(n);
+                       if (strcmp(id, "comment") == 0)
+                               continue;
+                       if (strcmp(id, "lib") == 0) {
+                               err = snd_config_get_string(n, &lib);
+                               if (err < 0) {
+                                       SNDERR("Invalid type for %s", id);
+                                       return -EINVAL;
+                               }
+                               continue;
                        }
-                       continue;
-               }
-               if (strcmp(id, "open") == 0) {
-                       err = snd_config_get_string(n, &open);
-                       if (err < 0) {
-                               SNDERR("Invalid type for %s", id);
-                               return -EINVAL;
+                       if (strcmp(id, "open") == 0) {
+                               err = snd_config_get_string(n, &open);
+                               if (err < 0) {
+                                       SNDERR("Invalid type for %s", id);
+                                       return -EINVAL;
+                               }
+                               continue;
                        }
-                       continue;
                        SNDERR("Unknown field %s", id);
                        return -EINVAL;
                }
        }
        if (!open) {
-               SNDERR("open is not defined");
-               return -EINVAL;
+               open = buf;
+               snprintf(buf, sizeof(buf), "_snd_seq_%s_open", str);
        }
        if (!lib)
                lib = "libasound.so";
@@ -113,9 +112,9 @@ int snd_seq_open(snd_seq_t **seqp, const char *name,
                return -ENOENT;
        }
        open_func = dlsym(h, open);
-       dlclose(h);
        if (!open_func) {
                SNDERR("symbol %s is not defined inside %s", open, lib);
+               dlclose(h);
                return -ENXIO;
        }
        return open_func(seqp, name, seq_conf, streams, mode);