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
}
#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)
{
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)
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)
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;
}
#include <string.h>
#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;
}