mirror of
https://github.com/alsa-project/alsa-lib.git
synced 2025-10-29 05:40:25 -04:00
Exported ERR macros. Completed scopes architecture. Added defaults for {pcm,ctl,rawmidi,seq}type configuration. Fixed bogus dlclose. Implemented ncurses levelmeter (temporarily placed in src/pcm/scopes)
This commit is contained in:
parent
8f3ae2cf2e
commit
46b52167df
14 changed files with 928 additions and 353 deletions
|
|
@ -33,8 +33,17 @@
|
|||
#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
|
||||
|
||||
|
|
|
|||
|
|
@ -42,15 +42,6 @@
|
|||
#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,
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -20,189 +20,41 @@
|
|||
|
||||
#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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,46 +18,4 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#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;
|
||||
|
||||
|
|
|
|||
7
src/pcm/scopes/Makefile.am
Normal file
7
src/pcm/scopes/Makefile.am
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
|
||||
CFLAGS = -g -O2 -W -Wall
|
||||
|
||||
lib_LTLIBRARIES = liblevel.la
|
||||
|
||||
liblevel_la_SOURCES = level.c
|
||||
liblevel_la_LIBADD = -lncurses
|
||||
9
src/pcm/scopes/configure.in
Normal file
9
src/pcm/scopes/configure.in
Normal file
|
|
@ -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)
|
||||
11
src/pcm/scopes/cvscompile
Normal file
11
src/pcm/scopes/cvscompile
Normal file
|
|
@ -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
|
||||
269
src/pcm/scopes/level.c
Normal file
269
src/pcm/scopes/level.c
Normal file
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue