#include <sys/event.h>
#include "oss.h"
static void
copy_ring(void *dstv, const void *srcv, int buffer_bytes, int offset,
int length)
{
uint8_t *dst = dstv;
const uint8_t *src = srcv;
int first;
if (length <= 0)
return;
first = buffer_bytes - offset;
if (first > length)
first = length;
memcpy(dst + offset, src + offset, first);
if (first < length)
memcpy(dst, src, length - first);
}
int
main(int argc, char *argv[])
{
int ch, bytes;
int frag_size, frame_size, verbose = 0;
int map_pointer = 0;
int64_t read_progress = 0, write_progress = 0;
oss_syncgroup sync_group = { 0, 0, { 0 } };
struct config config_in = {
.device = "/dev/dsp",
.mode = O_RDONLY | O_EXCL | O_NONBLOCK,
.format = AFMT_S32_NE,
.sample_rate = 48000,
.mmap = 1,
};
struct config config_out = {
.device = "/dev/dsp",
.mode = O_WRONLY | O_EXCL | O_NONBLOCK,
.format = AFMT_S32_NE,
.sample_rate = 48000,
.mmap = 1,
};
struct kevent ev;
int kq;
while ((ch = getopt(argc, argv, "v")) != -1) {
switch (ch) {
case 'v':
verbose = 1;
break;
}
}
argc -= optind;
argv += optind;
if (!verbose)
printf("Use -v for verbose mode\n");
oss_init(&config_in);
oss_init(&config_out);
if (config_in.buffer_info.bytes != config_out.buffer_info.bytes)
errx(1,
"Input and output configurations have different buffer sizes");
if (config_in.audio_info.max_channels !=
config_out.audio_info.max_channels)
errx(1,
"Input and output configurations have different number of channels");
bytes = config_in.buffer_info.bytes;
frag_size = config_in.buffer_info.fragsize;
frame_size = config_in.sample_size * config_in.audio_info.max_channels;
if (frag_size != config_out.buffer_info.fragsize)
errx(1,
"Input and output configurations have different fragment sizes");
memset(config_out.buf, 0, bytes);
sync_group.mode = PCM_ENABLE_INPUT;
if (ioctl(config_in.fd, SNDCTL_DSP_SYNCGROUP, &sync_group) < 0)
err(1, "Failed to add input to syncgroup");
sync_group.mode = PCM_ENABLE_OUTPUT;
if (ioctl(config_out.fd, SNDCTL_DSP_SYNCGROUP, &sync_group) < 0)
err(1, "Failed to add output to syncgroup");
if (ioctl(config_in.fd, SNDCTL_DSP_SYNCSTART, &sync_group.id) < 0)
err(1, "Starting sync group failed");
kq = kqueue();
if (kq < 0)
err(1, "kqueue failed");
EV_SET(&ev, config_in.fd, EVFILT_READ, EV_ADD, 0, 0, NULL);
if (kevent(kq, &ev, 1, NULL, 0, NULL) < 0)
err(1, "kevent register failed");
for (;;) {
int n;
int ptr;
unsigned delta;
n = kevent(kq, NULL, 0, &ev, 1, NULL);
if (n < 0)
err(1, "kevent failed");
if (n == 0)
continue;
ptr = (int)ev.ext[0];
if (ptr < 0 || ptr >= bytes)
errx(1, "Pointer out of bounds: %d", ptr);
if ((ptr % frame_size) != 0)
errx(1, "Pointer %d not aligned to frame size %d", ptr,
frame_size);
delta = (ptr + bytes - map_pointer) % bytes;
map_pointer = ptr;
read_progress += delta;
if (ev.ext[1] != 0 && verbose)
warnx("xruns: %llu", (unsigned long long)ev.ext[1]);
if (read_progress > write_progress) {
int offset = write_progress % bytes;
int length = read_progress - write_progress;
copy_ring(config_out.buf, config_in.buf, bytes, offset,
length);
write_progress = read_progress;
if (verbose)
printf("copied %d bytes at %d (abs %lld)\n",
length, offset, (long long)write_progress);
}
}
close(kq);
if (munmap(config_in.buf, bytes) != 0)
err(1, "Memory unmap failed");
config_in.buf = NULL;
if (munmap(config_out.buf, bytes) != 0)
err(1, "Memory unmap failed");
config_out.buf = NULL;
close(config_in.fd);
close(config_out.fd);
return (0);
}