From f5e461caa3121a3ee539de1dc4b9b7ce16bbf412 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 30 Nov 1999 09:25:59 +0000 Subject: [PATCH] PCM loopback API improved and added back the functionality. Some mmap() changes for the OSS emulation. --- include/mixer.h | 2 +- include/pcm.h | 14 ++++- src/pcm/pcm_loopback.c | 139 ++++++++++++++++++++++++++++++++--------- test/loopback.c | 61 +++++++++++++++++- 4 files changed, 182 insertions(+), 34 deletions(-) diff --git a/include/mixer.h b/include/mixer.h index 07fa48bf..f705fede 100644 --- a/include/mixer.h +++ b/include/mixer.h @@ -6,7 +6,7 @@ ****************************************************************************/ typedef struct snd_mixer_callbacks { - void *private_data; /* should be used by an application */ + void *private_data; /* should be used with an application */ void (*rebuild) (void *private_data); void (*element) (void *private_data, int cmd, snd_mixer_eid_t *eid); void (*group) (void *private_data, int cmd, snd_mixer_gid_t *gid); diff --git a/include/pcm.h b/include/pcm.h index 2c06d769..c4fb558d 100644 --- a/include/pcm.h +++ b/include/pcm.h @@ -144,13 +144,23 @@ int snd_pcm_plugin_build_voices(int src_format, int src_voices, extern "C" { #endif -int snd_pcm_loopback_open(snd_pcm_loopback_t **handle, int card, int device, int subchn, int mode); +typedef struct snd_pcm_loopback_callbacks { + void *private_data; /* should be used with an application */ + size_t max_buffer_size; /* zero = default (64kB) */ + void (*data) (void *private_data, char *buffer, size_t count); + void (*position_change) (void *private_data, unsigned int pos); + void (*format_change) (void *private_data, snd_pcm_format_t *format); + char *reserved[32]; /* reserved for the future use - must be NULL!!! */ +} snd_pcm_loopback_callbacks_t; + +int snd_pcm_loopback_open(snd_pcm_loopback_t **handle, int card, int device, int subdev, int mode); int snd_pcm_loopback_close(snd_pcm_loopback_t *handle); int snd_pcm_loopback_file_descriptor(snd_pcm_loopback_t *handle); int snd_pcm_loopback_block_mode(snd_pcm_loopback_t *handle, int enable); int snd_pcm_loopback_stream_mode(snd_pcm_loopback_t *handle, int mode); int snd_pcm_loopback_format(snd_pcm_loopback_t *handle, snd_pcm_format_t * format); -ssize_t snd_pcm_loopback_read(snd_pcm_loopback_t *handle, void *buffer, size_t size); +int snd_pcm_loopback_status(snd_pcm_loopback_t *handle, snd_pcm_loopback_status_t * status); +ssize_t snd_pcm_loopback_read(snd_pcm_loopback_t *handle, snd_pcm_loopback_callbacks_t * callbacks); #ifdef __cplusplus } diff --git a/src/pcm/pcm_loopback.c b/src/pcm/pcm_loopback.c index d9b03545..5aaea004 100644 --- a/src/pcm/pcm_loopback.c +++ b/src/pcm/pcm_loopback.c @@ -29,13 +29,16 @@ #include "asoundlib.h" #define SND_FILE_PCM_LB "/proc/asound/%i/pcmloopD%iS%i%s" -#define SND_PCM_LB_VERSION_MAX SND_PROTOCOL_VERSION( 1, 0, 0 ) +#define SND_PCM_LB_VERSION_MAX SND_PROTOCOL_VERSION(2, 0, 0) struct snd_pcm_loopback { int card; int device; int fd; -} ; + long mode; + size_t buffer_size; + char *buffer; +}; int snd_pcm_loopback_open(snd_pcm_loopback_t **handle, int card, int device, int subchn, int mode) { @@ -70,39 +73,35 @@ int snd_pcm_loopback_open(snd_pcm_loopback_t **handle, int card, int device, int lb->card = card; lb->device = device; lb->fd = fd; + lb->mode = SND_PCM_LB_STREAM_MODE_RAW; *handle = lb; return 0; } -int snd_pcm_loopback_close(snd_pcm_loopback_t *handle) +int snd_pcm_loopback_close(snd_pcm_loopback_t *lb) { - snd_pcm_loopback_t *lb; int res; - lb = handle; if (!lb) return -EINVAL; + if (lb->buffer) + free(lb->buffer); res = close(lb->fd) < 0 ? -errno : 0; free(lb); return res; } -int snd_pcm_loopback_file_descriptor(snd_pcm_loopback_t *handle) +int snd_pcm_loopback_file_descriptor(snd_pcm_loopback_t *lb) { - snd_pcm_loopback_t *lb; - - lb = handle; if (!lb) return -EINVAL; return lb->fd; } -int snd_pcm_loopback_block_mode(snd_pcm_loopback_t *handle, int enable) +int snd_pcm_loopback_block_mode(snd_pcm_loopback_t *lb, int enable) { - snd_pcm_loopback_t *lb; long flags; - lb = handle; if (!lb) return -EINVAL; if ((flags = fcntl(lb->fd, F_GETFL)) < 0) @@ -116,24 +115,19 @@ int snd_pcm_loopback_block_mode(snd_pcm_loopback_t *handle, int enable) return 0; } -int snd_pcm_loopback_stream_mode(snd_pcm_loopback_t *handle, int mode) +int snd_pcm_loopback_stream_mode(snd_pcm_loopback_t *lb, int mode) { - snd_pcm_loopback_t *lb; - long lmode = mode; - - lb = handle; - if (!lb) + if (!lb || (mode != SND_PCM_LB_STREAM_MODE_RAW && + mode != SND_PCM_LB_STREAM_MODE_PACKET)) return -EINVAL; - if (ioctl(lb->fd, SND_PCM_LB_IOCTL_STREAM_MODE, &lmode) < 0) + lb->mode = mode; + if (ioctl(lb->fd, SND_PCM_LB_IOCTL_STREAM_MODE, &lb->mode) < 0) return -errno; return 0; } -int snd_pcm_loopback_format(snd_pcm_loopback_t *handle, snd_pcm_format_t * format) +int snd_pcm_loopback_format(snd_pcm_loopback_t *lb, snd_pcm_format_t * format) { - snd_pcm_loopback_t *lb; - - lb = handle; if (!lb) return -EINVAL; if (ioctl(lb->fd, SND_PCM_LB_IOCTL_FORMAT, format) < 0) @@ -141,16 +135,101 @@ int snd_pcm_loopback_format(snd_pcm_loopback_t *handle, snd_pcm_format_t * forma return 0; } -ssize_t snd_pcm_loopback_read(snd_pcm_loopback_t *handle, void *buffer, size_t size) +int snd_pcm_loopback_status(snd_pcm_loopback_t *lb, snd_pcm_loopback_status_t * status) { - snd_pcm_loopback_t *lb; - ssize_t result; - - lb = handle; if (!lb) return -EINVAL; - result = read(lb->fd, buffer, size); - if (result < 0) + if (ioctl(lb->fd, SND_PCM_LB_IOCTL_FORMAT, status) < 0) return -errno; + return 0; +} + +ssize_t snd_pcm_loopback_read(snd_pcm_loopback_t *lb, snd_pcm_loopback_callbacks_t *callbacks) +{ + ssize_t result = 0, res, count; + size_t size; + char *buf; + snd_pcm_loopback_header_t header; + + if (!lb || !callbacks) + return -EINVAL; + if (callbacks->max_buffer_size == 0) + size = 64 * 1024; + else + size = callbacks->max_buffer_size < 16 ? 16 : callbacks->max_buffer_size; + while (1) { + if (lb->mode == SND_PCM_LB_STREAM_MODE_RAW) { + header.size = size; + header.type = SND_PCM_LB_TYPE_DATA; + } else { + res = read(lb->fd, &header, sizeof(header)); + if (res < 0) + return -errno; + if (res == 0) + break; + if (res != sizeof(header)) + return -EBADFD; + result += res; + } + switch (header.type) { + case SND_PCM_LB_TYPE_DATA: + if (lb->buffer_size < size) { + buf = (char *) realloc(lb->buffer, size); + if (buf == NULL) + return -ENOMEM; + lb->buffer = buf; + lb->buffer_size = size; + } else { + buf = lb->buffer; + } + while (header.size > 0) { + count = header.size; + if (count > size) + count = size; + res = read(lb->fd, buf, count); + if (res < 0) + return -errno; + result += res; + if (lb->mode == SND_PCM_LB_STREAM_MODE_PACKET && res != count) + return -EBADFD; + if (res == 0) + break; + if (callbacks->data) + callbacks->data(callbacks->private_data, buf, res); + if (res < count && lb->mode == SND_PCM_LB_STREAM_MODE_RAW) + break; + header.size -= res; + } + break; + case SND_PCM_LB_TYPE_FORMAT: + { + snd_pcm_format_t format; + + res = read(lb->fd, &format, sizeof(format)); + if (res < 0) + return -errno; + result += res; + if (res != sizeof(format)) + return -EBADFD; + if (callbacks->format_change) + callbacks->format_change(callbacks->private_data, &format); + } + break; + case SND_PCM_LB_TYPE_POSITION: + { + unsigned int pos; + + res = read(lb->fd, &pos, sizeof(pos)); + if (res < 0) + return -errno; + result += res; + if (res != sizeof(pos)) + return -EBADFD; + if (callbacks->position_change) + callbacks->position_change(callbacks->private_data, pos); + } + break; + } + } return result; } diff --git a/test/loopback.c b/test/loopback.c index f39c2d00..d9e0cdbe 100644 --- a/test/loopback.c +++ b/test/loopback.c @@ -3,16 +3,75 @@ #include #include "../include/asoundlib.h" +#define CARD 0 +#define DEVICE 0 +#define SUBDEV 0 +#define MODE SND_PCM_LB_STREAM_MODE_PACKET + +static void show_format1(const char *prefix, snd_pcm_format_t *format) +{ + printf("%sinterleave = %i, rate = %i, voices = %i, format = %i\n", + prefix, + format->interleave ? 1 : 0, + format->rate, + format->voices, + format->format); +} + +static void show_format(snd_pcm_loopback_t *handle) +{ + snd_pcm_format_t format; + int err; + + err = snd_pcm_loopback_format(handle, &format); + if (err < 0) { + fprintf(stderr, "format failed: %s\n", snd_strerror(err)); + exit(0); + } + show_format1("Format: ", &format); +} + +static void data(void *private_data, char *buf, size_t count) +{ + printf("DATA> count = %li\n", (long)count); +} + +static void format_change(void *private_data, snd_pcm_format_t *format) +{ + show_format1("Format change> ", format); +} + +static void position_change(void *private_data, unsigned int pos) +{ + printf("Position change> %u\n", pos); +} + int main(int argc, char *argv[]) { int err; + ssize_t res; snd_pcm_loopback_t *handle; + snd_pcm_loopback_callbacks_t callbacks; - err = snd_pcm_loopback_open(&handle, 0, 0, SND_PCM_LB_OPEN_PLAYBACK); + err = snd_pcm_loopback_open(&handle, CARD, DEVICE, SUBDEV, SND_PCM_LB_OPEN_PLAYBACK); if (err < 0) { fprintf(stderr, "open error: %s\n", snd_strerror(err)); exit(0); } + err = snd_pcm_loopback_stream_mode(handle, MODE); + if (err < 0) { + fprintf(stderr, "stream mode setup failed: %s\n", snd_strerror(err)); + exit(0); + } + show_format(handle); + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.data = data; + callbacks.format_change = format_change; + callbacks.position_change = position_change; + while ((res = snd_pcm_loopback_read(handle, &callbacks)) >= 0) { + if (res > 0) + printf("Read ok.. - %i\n", res); + } snd_pcm_loopback_close(handle); return 0; } -- 2.47.1