From 4b86ff0e6cfea2c34be71ebe1102eb307bd5dc0b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 16 Jun 2004 00:05:30 +0000 Subject: [PATCH] got mmap oss output working git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@20 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/Makefile.am | 11 +- src/main.c | 3 +- src/memblock.c | 31 ----- src/memblock.h | 4 - src/memblockq.c | 48 ++++++- src/memblockq.h | 1 + src/module-oss-mmap.c | 369 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/module-oss.c | 60 ++++---- src/oss.c | 48 +++++++ src/oss.h | 8 ++ src/protocol-simple.c | 21 ++- src/sample.c | 21 ++- src/sample.h | 5 +- src/sink.c | 65 +++++++-- src/sink.h | 6 +- src/source.c | 2 +- src/todo | 14 +- 17 files changed, 606 insertions(+), 111 deletions(-) create mode 100644 src/module-oss-mmap.c create mode 100644 src/oss.c create mode 100644 src/oss.h diff --git a/src/Makefile.am b/src/Makefile.am index 40e2039..8ce5202 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -22,7 +22,7 @@ bin_PROGRAMS = polypaudio pkglib_LTLIBRARIES=libprotocol-simple.la module-simple-protocol-tcp.la \ libsocket-server.la module-pipe-sink.la libpstream.la libiochannel.la \ - libpacket.la module-oss.la + libpacket.la module-oss.la module-oss-mmap.la liboss.la polypaudio_SOURCES = idxset.c queue.c strbuf.c mainloop.c \ memblock.c sample.c memblockq.c client.c \ @@ -50,6 +50,9 @@ libiochannel_la_LDFLAGS = -avoid-version libpacket_la_SOURCES = packet.c libpacket_la_LDFLAGS = -avoid-version +liboss_la_SOURCES = oss.c +liboss_la_LDFLAGS = -avoid-version + module_simple_protocol_tcp_la_SOURCES = module-simple-protocol-tcp.c module_simple_protocol_tcp_la_LDFLAGS = -module -avoid-version module_simple_protocol_tcp_la_LIBADD = libprotocol-simple.la libiochannel.la @@ -60,4 +63,8 @@ module_pipe_sink_la_LIBADD = libiochannel.la module_oss_la_SOURCES = module-oss.c module_oss_la_LDFLAGS = -module -avoid-version -module_oss_la_LIBADD = libiochannel.la +module_oss_la_LIBADD = libiochannel.la liboss.la + +module_oss_mmap_la_SOURCES = module-oss-mmap.c +module_oss_mmap_la_LDFLAGS = -module -avoid-version +module_oss_mmap_la_LIBADD = libiochannel.la liboss.la diff --git a/src/main.c b/src/main.c index a42eaa8..bce0782 100644 --- a/src/main.c +++ b/src/main.c @@ -30,11 +30,10 @@ int main(int argc, char *argv[]) { mainloop_source_new_signal(m, SIGINT, signal_callback, NULL); signal(SIGPIPE, SIG_IGN); - module_load(c, "module-oss", "/dev/dsp1"); + module_load(c, "module-oss-mmap", "/dev/dsp1"); module_load(c, "module-pipe-sink", NULL); module_load(c, "module-simple-protocol-tcp", NULL); - fprintf(stderr, "main: mainloop entry.\n"); while (mainloop_iterate(m, 1) == 0); /* fprintf(stderr, "main: %u blocks\n", n_blocks);*/ diff --git a/src/memblock.c b/src/memblock.c index 2d34676..6f05918 100644 --- a/src/memblock.c +++ b/src/memblock.c @@ -2,8 +2,6 @@ #include #include #include -#include -#include #include "memblock.h" @@ -16,7 +14,6 @@ struct memblock *memblock_new(size_t length) { b->length = length; b->data = b+1; n_blocks++; - timerclear(&b->stamp); return b; } @@ -27,7 +24,6 @@ struct memblock *memblock_new_fixed(void *d, size_t length) { b->length = length; b->data = d; n_blocks++; - timerclear(&b->stamp); return b; } @@ -38,7 +34,6 @@ struct memblock *memblock_new_dynamic(void *d, size_t length) { b->length = length; b->data = d; n_blocks++; - timerclear(&b->stamp); return b; } @@ -77,29 +72,3 @@ void memblock_unref_fixed(struct memblock *b) { b->type = MEMBLOCK_DYNAMIC; } -void memblock_stamp(struct memblock*b) { - assert(b); - gettimeofday(&b->stamp, NULL); -} - -uint32_t memblock_age(struct memblock*b) { - assert(b); - struct timeval tv; - uint32_t r; - - if (b->stamp.tv_sec == 0) - return (suseconds_t) -1; - - gettimeofday(&tv, NULL); - - /*fprintf(stderr, "memblock: (%lu,%lu) -- (%lu,%lu)\r", b->stamp.tv_sec, b->stamp.tv_usec, tv.tv_sec, tv.tv_usec);*/ - - r = (tv.tv_sec-b->stamp.tv_sec) * 1000000; - - if (tv.tv_usec >= b->stamp.tv_usec) - r += tv.tv_usec - b->stamp.tv_usec; - else - r -= b->stamp.tv_usec - tv.tv_usec; - - return r; -} diff --git a/src/memblock.h b/src/memblock.h index c0fb670..52f1fe5 100644 --- a/src/memblock.h +++ b/src/memblock.h @@ -11,7 +11,6 @@ struct memblock { unsigned ref; size_t length; void *data; - struct timeval stamp; }; struct memchunk { @@ -28,9 +27,6 @@ struct memblock* memblock_ref(struct memblock*b); void memblock_unref_fixed(struct memblock*b); -void memblock_stamp(struct memblock*b); -uint32_t memblock_age(struct memblock*b); - #define memblock_assert_exclusive(b) assert((b)->ref == 1) extern unsigned n_blocks; diff --git a/src/memblockq.c b/src/memblockq.c index 3c0d432..9e9c109 100644 --- a/src/memblockq.c +++ b/src/memblockq.c @@ -1,3 +1,5 @@ +#include +#include #include #include #include @@ -7,18 +9,17 @@ struct memblock_list { struct memblock_list *next; struct memchunk chunk; + struct timeval stamp; }; struct memblockq { struct memblock_list *blocks, *blocks_tail; unsigned n_blocks; - size_t total_length; - size_t maxlength; - size_t base; - size_t prebuf; + size_t total_length, maxlength, base, prebuf; + int measure_latency; + uint32_t latency; }; - struct memblockq* memblockq_new(size_t maxlength, size_t base, size_t prebuf) { struct memblockq* bq; assert(maxlength && base); @@ -37,6 +38,9 @@ struct memblockq* memblockq_new(size_t maxlength, size_t base, size_t prebuf) { assert(bq->maxlength >= base); + bq->measure_latency = 1; + bq->latency = 0; + return bq; } @@ -60,6 +64,11 @@ void memblockq_push(struct memblockq* bq, struct memchunk *chunk, size_t delta) q = malloc(sizeof(struct memblock_list)); assert(q); + if (bq->measure_latency) + gettimeofday(&q->stamp, NULL); + else + timerclear(&q->stamp); + q->chunk = *chunk; memblock_ref(q->chunk.memblock); assert(q->chunk.index+q->chunk.length <= q->chunk.memblock->length); @@ -113,6 +122,26 @@ int memblockq_pop(struct memblockq* bq, struct memchunk *chunk) { return 0; } +static uint32_t age(struct timeval *tv) { + assert(tv); + struct timeval now; + uint32_t r; + + if (tv->tv_sec == 0) + return 0; + + gettimeofday(&now, NULL); + + r = (now.tv_sec-tv->tv_sec) * 1000000; + + if (now.tv_usec >= tv->tv_usec) + r += now.tv_usec - tv->tv_usec; + else + r -= tv->tv_usec - now.tv_usec; + + return r; +} + void memblockq_drop(struct memblockq *bq, size_t length) { assert(bq); @@ -122,7 +151,10 @@ void memblockq_drop(struct memblockq *bq, size_t length) { if (l > bq->blocks->chunk.length) l = bq->blocks->chunk.length; - + + if (bq->measure_latency) + bq->latency = age(&bq->blocks->stamp); + bq->blocks->chunk.index += l; bq->blocks->chunk.length -= l; bq->total_length -= l; @@ -178,3 +210,7 @@ int memblockq_is_writable(struct memblockq *bq, size_t length) { assert(length <= bq->maxlength); return bq->total_length + length <= bq->maxlength; } + +uint32_t memblockq_get_latency(struct memblockq *bq) { + return bq->latency; +} diff --git a/src/memblockq.h b/src/memblockq.h index 25c2f2d..9543b40 100644 --- a/src/memblockq.h +++ b/src/memblockq.h @@ -22,5 +22,6 @@ void memblockq_empty(struct memblockq *bq); int memblockq_is_readable(struct memblockq *bq); int memblockq_is_writable(struct memblockq *bq, size_t length); +uint32_t memblockq_get_latency(struct memblockq *bq); #endif diff --git a/src/module-oss-mmap.c b/src/module-oss-mmap.c new file mode 100644 index 0000000..bfc3a6f --- /dev/null +++ b/src/module-oss-mmap.c @@ -0,0 +1,369 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "iochannel.h" +#include "sink.h" +#include "source.h" +#include "module.h" +#include "oss.h" + +struct userdata { + struct sink *sink; + struct source *source; + struct core *core; + struct sample_spec sample_spec; + + size_t in_fragment_size, out_fragment_size, in_fragments, out_fragments, sample_size, out_fill; + uint32_t sample_usec; + + int fd; + + void *in_mmap, *out_mmap; + size_t in_mmap_length, out_mmap_length; + + struct mainloop_source *mainloop_source; + + struct memblock **in_memblocks, **out_memblocks; + unsigned out_current, in_current; +}; + +void module_done(struct core *c, struct module*m); + +static void out_clear_memblocks(struct userdata*u, unsigned n) { + unsigned i = u->out_current; + assert(u && u->out_memblocks); + + if (n > u->out_fragments) + n = u->out_fragments; + + while (n > 0) { + if (u->out_memblocks[i]) { + memblock_unref_fixed(u->out_memblocks[i]); + u->out_memblocks[i] = NULL; + } + + i++; + while (i >= u->out_fragments) + i -= u->out_fragments; + + n--; + } +} + +static void out_fill_memblocks(struct userdata *u, unsigned n) { + assert(u && u->out_memblocks); + + while (n > 0) { + struct memchunk chunk; + + if (!u->out_memblocks[u->out_current]) { + + u->out_memblocks[u->out_current] = chunk.memblock = memblock_new_fixed(u->out_mmap+u->out_fragment_size*u->out_current, u->out_fragment_size); + chunk.length = chunk.memblock->length; + chunk.index = 0; + + sink_render_into_full(u->sink, &chunk); + } + + u->out_current++; + while (u->out_current >= u->out_fragments) + u->out_current -= u->out_fragments; + + n--; + } +} + +static void do_write(struct userdata *u) { + struct count_info info; + assert(u && u->sink); + + if (ioctl(u->fd, SNDCTL_DSP_GETOPTR, &info) < 0) { + fprintf(stderr, "SNDCTL_DSP_GETOPTR: %s\n", strerror(errno)); + return; + } + + u->out_fill = (u->out_fragment_size * u->out_fragments) - (info.ptr % u->out_fragment_size); + + if (!info.blocks) + return; + + out_clear_memblocks(u, info.blocks); + out_fill_memblocks(u, info.blocks); + +} + +static void in_post_memblocks(struct userdata *u, unsigned n) { + assert(u && u->in_memblocks); + + while (n > 0) { + struct memchunk chunk; + + if (!u->in_memblocks[u->in_current]) { + u->in_memblocks[u->in_current] = chunk.memblock = memblock_new_fixed(u->in_mmap+u->in_fragment_size*u->in_current, u->in_fragment_size); + chunk.length = chunk.memblock->length; + chunk.index = 0; + + source_post(u->source, &chunk); + } + + u->in_current++; + while (u->in_current >= u->in_fragments) + u->in_current -= u->in_fragments; + + n--; + } +} + +static void in_clear_memblocks(struct userdata*u, unsigned n) { + unsigned i = u->in_current; + assert(u && u->in_memblocks); + + if (n > u->in_fragments) + n = u->in_fragments; + + while (n > 0) { + if (u->in_memblocks[i]) { + memblock_unref_fixed(u->in_memblocks[i]); + u->in_memblocks[i] = NULL; + } + + i++; + while (i >= u->in_fragments) + i -= u->in_fragments; + + n--; + } +} + +static void do_read(struct userdata *u) { + struct count_info info; + assert(u && u->source); + + if (ioctl(u->fd, SNDCTL_DSP_GETIPTR, &info) < 0) { + fprintf(stderr, "SNDCTL_DSP_GETIPTR: %s\n", strerror(errno)); + return; + } + + if (!info.blocks) + return; + + in_post_memblocks(u, info.blocks); + in_clear_memblocks(u, u->in_fragments/2); +}; + +static void io_callback(struct mainloop_source*s, int fd, enum mainloop_io_event event, void *userdata) { + struct userdata *u = userdata; + + if (event & MAINLOOP_IO_EVENT_IN) + do_read(u); + if (event & MAINLOOP_IO_EVENT_OUT) + do_write(u); +} + +static uint32_t sink_get_latency_cb(struct sink *s) { + struct userdata *u = s->userdata; + assert(s && u); + + do_write(u); + return u->out_fill/u->sample_size*u->sample_usec; +} + +int module_init(struct core *c, struct module*m) { + struct audio_buf_info info; + struct userdata *u = NULL; + char *p; + int frag_size; + int mode, caps, caps_read = 0; + int enable_bits = 0, zero = 0; + assert(c && m); + + m->userdata = u = malloc(sizeof(struct userdata)); + assert(u); + memset(u, 0, sizeof(struct userdata)); + u->fd = -1; + u->core = c; + + p = m->argument ? m->argument : "/dev/dsp"; + if ((u->fd = open(p, (mode = O_RDWR)|O_NDELAY)) >= 0) { + ioctl(u->fd, SNDCTL_DSP_SETDUPLEX, 0); + + if (ioctl(u->fd, SNDCTL_DSP_GETCAPS, &caps) < 0) { + fprintf(stderr, "SNDCTL_DSP_GETCAPS: %s\n", strerror(errno)); + goto fail; + } + + if (!(caps & DSP_CAP_DUPLEX)) { + close(u->fd); + u->fd = -1; + } else + caps_read = 1; + } + + if (u->fd < 0) { + if ((u->fd = open(p, (mode = O_WRONLY)|O_NDELAY)) < 0) { + if ((u->fd = open(p, (mode = O_RDONLY)|O_NDELAY)) < 0) { + fprintf(stderr, "open('%s'): %s\n", p, strerror(errno)); + goto fail; + } + } + } + + if (!caps_read) { + if (ioctl(u->fd, SNDCTL_DSP_GETCAPS, &caps) < 0) { + fprintf(stderr, "SNDCTL_DSP_GETCAPS: %s\n", strerror(errno)); + goto fail; + } + } + + if (!(caps & DSP_CAP_MMAP) || !(caps & DSP_CAP_REALTIME) || !(caps & DSP_CAP_TRIGGER)) { + fprintf(stderr, "OSS device not mmap capable.\n"); + goto fail; + } + + fprintf(stderr, "module-oss: device opened in %s mode.\n", mode == O_WRONLY ? "O_WRONLY" : (mode == O_RDONLY ? "O_RDONLY" : "O_RDWR")); + + frag_size = ((int) 12 << 16) | 10; /* nfrags = 12; frag_size = 2^10 */ + if (ioctl(u->fd, SNDCTL_DSP_SETFRAGMENT, &frag_size) < 0) { + fprintf(stderr, "SNDCTL_DSP_SETFRAGMENT: %s\n", strerror(errno)); + goto fail; + } + + if (oss_auto_format(u->fd, &u->sample_spec) < 0) + goto fail; + + if (mode != O_WRONLY) { + if (ioctl(u->fd, SNDCTL_DSP_GETISPACE, &info) < 0) { + fprintf(stderr, "SNDCTL_DSP_GETISPACE: %s\n", strerror(errno)); + goto fail; + } + + fprintf(stderr, "module-oss-mmap: input -- %u fragments of size %u.\n", info.fragstotal, info.fragsize); + u->in_mmap_length = (u->in_fragment_size = info.fragsize) * (u->in_fragments = info.fragstotal); + + if ((u->in_mmap = mmap(NULL, u->in_mmap_length, PROT_READ, MAP_SHARED, u->fd, 0)) == MAP_FAILED) { + if (mode == O_RDWR) { + fprintf(stderr, "module-oss-mmap: mmap failed for input. Changing to O_WRONLY mode.\n"); + mode = O_WRONLY; + } else { + fprintf(stderr, "modeule-oss-mmap: mmap(): %s\n", strerror(errno)); + goto fail; + } + } else { + + u->source = source_new(c, "dsp", &u->sample_spec); + assert(u->source); + u->source->userdata = u; + + u->in_memblocks = malloc(sizeof(struct memblock *)*u->in_fragments); + memset(u->in_memblocks, 0, sizeof(struct memblock *)*u->in_fragments); + + enable_bits |= PCM_ENABLE_INPUT; + } + } + + if (m != O_RDONLY) { + if (ioctl(u->fd, SNDCTL_DSP_GETOSPACE, &info) < 0) { + fprintf(stderr, "SNDCTL_DSP_GETOSPACE: %s\n", strerror(errno)); + goto fail; + } + + fprintf(stderr, "module-oss: output -- %u fragments of size %u.\n", info.fragstotal, info.fragsize); + u->out_mmap_length = (u->out_fragment_size = info.fragsize) * (u->out_fragments = info.fragstotal); + + if ((u->out_mmap = mmap(NULL, u->out_mmap_length, PROT_WRITE, MAP_SHARED, u->fd, 0)) == MAP_FAILED) { + if (mode == O_RDWR) { + fprintf(stderr, "module-oss-mmap: mmap filed for output. Changing to O_RDONLY mode.\n"); + mode = O_RDONLY; + } else { + fprintf(stderr, "modeule-oss-mmap: mmap(): %s\n", strerror(errno)); + goto fail; + } + } else { + silence_memory(u->out_mmap, u->out_mmap_length, &u->sample_spec); + + u->sink = sink_new(c, "dsp", &u->sample_spec); + assert(u->sink); + u->sink->get_latency = sink_get_latency_cb; + u->sink->userdata = u; + + u->out_memblocks = malloc(sizeof(struct memblock *)*u->out_fragments); + memset(u->out_memblocks, 0, sizeof(struct memblock *)*u->out_fragments); + + enable_bits |= PCM_ENABLE_OUTPUT; + } + } + + zero = 0; + if (ioctl(u->fd, SNDCTL_DSP_SETTRIGGER, &zero) < 0) { + fprintf(stderr, "SNDCTL_DSP_SETTRIGGER: %s\n", strerror(errno)); + goto fail; + } + + if (ioctl(u->fd, SNDCTL_DSP_SETTRIGGER, &enable_bits) < 0) { + fprintf(stderr, "SNDCTL_DSP_SETTRIGGER: %s\n", strerror(errno)); + goto fail; + } + + assert(u->source || u->sink); + + u->sample_size = sample_size(&u->sample_spec); + u->sample_usec = 1000000/u->sample_spec.rate; + + u->mainloop_source = mainloop_source_new_io(c->mainloop, u->fd, (u->source ? MAINLOOP_IO_EVENT_IN : 0) | (u->sink ? MAINLOOP_IO_EVENT_OUT : 0), io_callback, u); + assert(u->mainloop_source); + + return 0; + +fail: + module_done(c, m); + + return -1; +} + +void module_done(struct core *c, struct module*m) { + struct userdata *u; + assert(c && m); + + u = m->userdata; + assert(u); + + if (u->out_memblocks) { + out_clear_memblocks(u, u->out_fragments); + free(u->out_memblocks); + } + + if (u->in_memblocks) { + in_clear_memblocks(u, u->in_fragments); + free(u->in_memblocks); + } + + if (u->in_mmap && u->in_mmap != MAP_FAILED) + munmap(u->in_mmap, u->in_mmap_length); + + if (u->out_mmap && u->out_mmap != MAP_FAILED) + munmap(u->out_mmap, u->out_mmap_length); + + if (u->sink) + sink_free(u->sink); + + if (u->source) + source_free(u->source); + + if (u->mainloop_source) + mainloop_source_free(u->mainloop_source); + + if (u->fd >= 0) + close(u->fd); + + free(u); +} diff --git a/src/module-oss.c b/src/module-oss.c index bb8112b..170d864 100644 --- a/src/module-oss.c +++ b/src/module-oss.c @@ -14,6 +14,8 @@ #include "sink.h" #include "source.h" #include "module.h" +#include "oss.h" +#include "sample.h" struct userdata { struct sink *sink; @@ -23,7 +25,9 @@ struct userdata { struct memchunk memchunk, silence; - uint32_t in_fragment_size, out_fragment_size, sample_size; + uint32_t in_fragment_size, out_fragment_size, sample_size, sample_usec; + + int fd; }; static void do_write(struct userdata *u) { @@ -92,12 +96,26 @@ static void io_callback(struct iochannel *io, void*userdata) { do_read(u); } +static uint32_t sink_get_latency_cb(struct sink *s) { + int arg; + struct userdata *u = s->userdata; + assert(s && u); + + if (ioctl(u->fd, SNDCTL_DSP_GETODELAY, &arg) < 0) { + fprintf(stderr, "module-oss: device doesn't support SNDCTL_DSP_GETODELAY.\n"); + s->get_latency = NULL; + return 0; + } + + return arg/u->sample_size*u->sample_usec; +} + int module_init(struct core *c, struct module*m) { struct audio_buf_info info; struct userdata *u = NULL; char *p; int fd = -1; - int format, channels, speed, frag_size, in_frag_size, out_frag_size; + int frag_size, in_frag_size, out_frag_size; int mode; struct sample_spec ss; assert(c && m); @@ -136,37 +154,8 @@ int module_init(struct core *c, struct module*m) { goto fail; } - format = AFMT_S16_NE; - if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != AFMT_S16_NE) { - int f = AFMT_S16_NE == AFMT_S16_LE ? AFMT_S16_BE : AFMT_S16_LE; - format = f; - if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != f) { - format = AFMT_U8; - if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != AFMT_U8) { - fprintf(stderr, "SNDCTL_DSP_SETFMT: %s\n", format != AFMT_U8 ? "No supported sample format" : strerror(errno)); - goto fail; - } else - ss.format = SAMPLE_U8; - } else - ss.format = f == AFMT_S16_LE ? SAMPLE_S16LE : SAMPLE_S16BE; - } else - ss.format = SAMPLE_S16NE; - - channels = 2; - if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) < 0) { - fprintf(stderr, "SNDCTL_DSP_CHANNELS: %s\n", strerror(errno)); - goto fail; - } - assert(channels); - ss.channels = channels; - - speed = 44100; - if (ioctl(fd, SNDCTL_DSP_SPEED, &speed) < 0) { - fprintf(stderr, "SNDCTL_DSP_SPEED: %s\n", strerror(errno)); + if (oss_auto_format(fd, &ss) < 0) goto fail; - } - assert(speed); - ss.rate = speed; if (ioctl(fd, SNDCTL_DSP_GETBLKSIZE, &frag_size) < 0) { fprintf(stderr, "SNDCTL_DSP_GETBLKSIZE: %s\n", strerror(errno)); @@ -193,12 +182,15 @@ int module_init(struct core *c, struct module*m) { if (mode != O_RDONLY) { u->sink = sink_new(c, "dsp", &ss); assert(u->sink); + u->sink->get_latency = sink_get_latency_cb; + u->sink->userdata = u; } else u->sink = NULL; if (mode != O_WRONLY) { u->source = source_new(c, "dsp", &ss); assert(u->source); + u->source->userdata = u; } else u->source = NULL; @@ -207,16 +199,18 @@ int module_init(struct core *c, struct module*m) { u->io = iochannel_new(c->mainloop, u->source ? fd : -1, u->sink ? fd : 0); assert(u->io); iochannel_set_callback(u->io, io_callback, u); + u->fd = fd; u->memchunk.memblock = NULL; u->memchunk.length = 0; u->sample_size = sample_size(&ss); + u->sample_usec = 1000000/ss.rate; u->out_fragment_size = out_frag_size; u->in_fragment_size = in_frag_size; u->silence.memblock = memblock_new(u->silence.length = u->out_fragment_size); assert(u->silence.memblock); - silence(u->silence.memblock, &ss); + silence_memblock(u->silence.memblock, &ss); u->silence.index = 0; m->userdata = u; diff --git a/src/oss.c b/src/oss.c new file mode 100644 index 0000000..7b1315c --- /dev/null +++ b/src/oss.c @@ -0,0 +1,48 @@ +#include +#include +#include +#include +#include +#include + +#include "oss.h" + +int oss_auto_format(int fd, struct sample_spec *ss) { + int format, channels, speed; + + assert(fd >= 0 && ss); + + format = AFMT_S16_NE; + if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != AFMT_S16_NE) { + int f = AFMT_S16_NE == AFMT_S16_LE ? AFMT_S16_BE : AFMT_S16_LE; + format = f; + if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != f) { + format = AFMT_U8; + if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != AFMT_U8) { + fprintf(stderr, "SNDCTL_DSP_SETFMT: %s\n", format != AFMT_U8 ? "No supported sample format" : strerror(errno)); + return -1; + } else + ss->format = SAMPLE_U8; + } else + ss->format = f == AFMT_S16_LE ? SAMPLE_S16LE : SAMPLE_S16BE; + } else + ss->format = SAMPLE_S16NE; + + channels = 2; + if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) < 0) { + fprintf(stderr, "SNDCTL_DSP_CHANNELS: %s\n", strerror(errno)); + return -1; + } + assert(channels); + ss->channels = channels; + + speed = 44100; + if (ioctl(fd, SNDCTL_DSP_SPEED, &speed) < 0) { + fprintf(stderr, "SNDCTL_DSP_SPEED: %s\n", strerror(errno)); + return -1; + } + assert(speed); + ss->rate = speed; + + return 0; +} diff --git a/src/oss.h b/src/oss.h new file mode 100644 index 0000000..35d2dd0 --- /dev/null +++ b/src/oss.h @@ -0,0 +1,8 @@ +#ifndef fooosshfoo +#define fooosshfoo + +#include "sample.h" + +int oss_auto_format(int fd, struct sample_spec *ss); + +#endif diff --git a/src/protocol-simple.c b/src/protocol-simple.c index 1803936..f779a56 100644 --- a/src/protocol-simple.c +++ b/src/protocol-simple.c @@ -56,6 +56,7 @@ static void destroy_connection(struct connection *c) { static int do_read(struct connection *c) { struct memchunk chunk; ssize_t r; + uint32_t u1, u2; if (!iochannel_is_readable(c->io)) return 0; @@ -66,8 +67,6 @@ static int do_read(struct connection *c) { chunk.memblock = memblock_new(BUFSIZE); assert(chunk.memblock); - memblock_stamp(chunk.memblock); - if ((r = iochannel_read(c->io, chunk.memblock->data, BUFSIZE)) <= 0) { fprintf(stderr, "read(): %s\n", r == 0 ? "EOF" : strerror(errno)); memblock_unref(chunk.memblock); @@ -82,6 +81,13 @@ static int do_read(struct connection *c) { memblockq_push(c->input_memblockq, &chunk, 0); memblock_unref(chunk.memblock); sink_notify(c->sink_input->sink); + + + u1 = memblockq_get_latency(c->input_memblockq); + u2 = sink_get_latency(c->sink_input->sink); + + fprintf(stderr, "latency: %u+%u=%u\r", u1, u2, u1+u2); + return 0; } @@ -96,7 +102,9 @@ static int do_write(struct connection *c) { return 0; assert(c->output_memblockq); - memblockq_peek(c->output_memblockq, &chunk); + if (memblockq_peek(c->output_memblockq, &chunk) < 0) + return 0; + assert(chunk.memblock && chunk.length); if ((r = iochannel_write(c->io, chunk.memblock->data+chunk.index, chunk.length)) < 0) { @@ -145,6 +153,9 @@ static void source_output_push_cb(struct source_output *o, struct memchunk *chun assert(o && c && chunk); memblockq_push(c->output_memblockq, chunk, 0); + + if (do_write(c) < 0) + destroy_connection(c); } static void source_output_kill_cb(struct source_output *o) { @@ -205,7 +216,7 @@ static void on_connection(struct socket_server*s, struct iochannel *io, void *us c->source_output->kill = source_output_kill_cb; c->source_output->userdata = c; - l = 5*bytes_per_second(&DEFAULT_SAMPLE_SPEC); + l = 5*bytes_per_second(&DEFAULT_SAMPLE_SPEC); /* 5s */ c->output_memblockq = memblockq_new(l, sample_size(&DEFAULT_SAMPLE_SPEC), l/2); } @@ -225,7 +236,7 @@ static void on_connection(struct socket_server*s, struct iochannel *io, void *us c->sink_input->kill = sink_input_kill_cb; c->sink_input->userdata = c; - l = 5*bytes_per_second(&DEFAULT_SAMPLE_SPEC); + l = bytes_per_second(&DEFAULT_SAMPLE_SPEC)/2; /* half a second */ c->input_memblockq = memblockq_new(l, sample_size(&DEFAULT_SAMPLE_SPEC), l/2); } diff --git a/src/sample.c b/src/sample.c index 21c0462..81fe699 100644 --- a/src/sample.c +++ b/src/sample.c @@ -9,10 +9,22 @@ struct sample_spec default_sample_spec = { .channels = 2 }; -struct memblock *silence(struct memblock* b, struct sample_spec *spec) { - char c = 0; - assert(b && spec); +struct memblock *silence_memblock(struct memblock* b, struct sample_spec *spec) { + assert(b && b->data && spec); memblock_assert_exclusive(b); + silence_memory(b->data, b->length, spec); + return b; +} + +void silence_memchunk(struct memchunk *c, struct sample_spec *spec) { + assert(c && c->memblock && c->memblock->data && spec && c->length); + memblock_assert_exclusive(c->memblock); + silence_memory(c->memblock->data+c->index, c->length, spec); +} + +void silence_memory(void *p, size_t length, struct sample_spec *spec) { + char c = 0; + assert(p && length && spec); switch (spec->format) { case SAMPLE_U8: @@ -29,8 +41,7 @@ struct memblock *silence(struct memblock* b, struct sample_spec *spec) { break; } - memset(b->data, c, b->length); - return b; + memset(p, c, length); } size_t sample_size(struct sample_spec *spec) { diff --git a/src/sample.h b/src/sample.h index f8ba669..23fc088 100644 --- a/src/sample.h +++ b/src/sample.h @@ -26,8 +26,9 @@ struct sample_spec { extern struct sample_spec default_sample_spec; -struct memblock *silence(struct memblock* b, struct sample_spec *spec); - +struct memblock *silence_memblock(struct memblock* b, struct sample_spec *spec); +void silence_memchunk(struct memchunk *c, struct sample_spec *spec); +void silence_memory(void *p, size_t length, struct sample_spec *spec); struct mix_info { struct memchunk chunk; diff --git a/src/sink.c b/src/sink.c index 951191d..4d3206d 100644 --- a/src/sink.c +++ b/src/sink.c @@ -33,6 +33,7 @@ struct sink* sink_new(struct core *core, const char *name, const struct sample_s s->volume = 0xFF; s->notify = NULL; + s->get_latency = NULL; s->userdata = NULL; r = idxset_put(core->sinks, s, &s->index); @@ -138,14 +139,18 @@ int sink_render(struct sink*s, size_t length, struct memchunk *result) { } inputs_drop(s, info, n, l); + + assert(s->monitor_source); + source_post(s->monitor_source, result); + return 0; } -int sink_render_into(struct sink*s, struct memblock *target, struct memchunk *result) { +int sink_render_into(struct sink*s, struct memchunk *target) { struct mix_info info[MAX_MIX_CHANNELS]; unsigned n; size_t l; - assert(s && target && target->length && target->data && result); + assert(s && target && target->length && target->memblock && target->memblock->data); n = fill_mix_info(s, info, MAX_MIX_CHANNELS); @@ -160,18 +165,52 @@ int sink_render_into(struct sink*s, struct memblock *target, struct memchunk *re if (l > info[0].chunk.length) l = info[0].chunk.length; - result->memblock = target; - memcpy(target->data, info[0].chunk.memblock->data + info[0].chunk.index, l); - result->length = target->length = l; - result->index = 0; - } else { + memcpy(target->memblock->data+target->index, info[0].chunk.memblock->data + info[0].chunk.index, l); + target->length = l; + } else + target->length = l = mix_chunks(info, n, target->memblock->data+target->index, target->length, &s->sample_spec, s->volume); + + assert(l); + inputs_drop(s, info, n, l); - result->memblock = target; - result->length = l = mix_chunks(info, n, target->data, target->length, &s->sample_spec, s->volume); - result->index = 0; - assert(l); - } + assert(s->monitor_source); + source_post(s->monitor_source, target); - inputs_drop(s, info, n, l); return 0; } + +void sink_render_into_full(struct sink *s, struct memchunk *target) { + struct memchunk chunk; + size_t l, d; + assert(s && target && target->memblock && target->length && target->memblock->data); + + l = target->length; + d = 0; + while (l > 0) { + chunk = *target; + chunk.index += d; + chunk.length -= d; + + if (sink_render_into(s, &chunk) < 0) + break; + + d += chunk.length; + l -= chunk.length; + } + + if (l > 0) { + chunk = *target; + chunk.index += d; + chunk.length -= d; + silence_memchunk(&chunk, &s->sample_spec); + } +} + +uint32_t sink_get_latency(struct sink *s) { + assert(s); + + if (!s->get_latency) + return 0; + + return s->get_latency(s); +} diff --git a/src/sink.h b/src/sink.h index ccfa259..1a9fb8c 100644 --- a/src/sink.h +++ b/src/sink.h @@ -23,6 +23,7 @@ struct sink { uint8_t volume; void (*notify)(struct sink*sink); + uint32_t (*get_latency)(struct sink *s); void *userdata; }; @@ -30,7 +31,10 @@ struct sink* sink_new(struct core *core, const char *name, const struct sample_s void sink_free(struct sink* s); int sink_render(struct sink*s, size_t length, struct memchunk *result); -int sink_render_into(struct sink*s, struct memblock *target, struct memchunk *result); +int sink_render_into(struct sink*s, struct memchunk *target); +void sink_render_into_full(struct sink *s, struct memchunk *target); + +uint32_t sink_get_latency(struct sink *s); void sink_notify(struct sink*s); diff --git a/src/source.c b/src/source.c index e01e9f8..c950b54 100644 --- a/src/source.c +++ b/src/source.c @@ -59,7 +59,7 @@ void source_notify(struct source*s) { static int do_post(void *p, uint32_t index, int *del, void*userdata) { struct memchunk *chunk = userdata; struct source_output *o = p; - assert(o && o->push && index && del && chunk); + assert(o && o->push && del && chunk); o->push(o, chunk); return 0; diff --git a/src/todo b/src/todo index 563ab80..2ff7cc6 100644 --- a/src/todo +++ b/src/todo @@ -1,12 +1,11 @@ -- mixing -- latenzoptimierung -- optimierung von rebuild_pollfds() -- resampling +- simple control protocol - native protocol/library -- oss/mmap -- esound prodocol +- resampling +- esound protocol - config-parser +- record-testing -- 0.1 +- optimierung von rebuild_pollfds() - future cancellation - client-ui @@ -16,3 +15,6 @@ drivers: - portaudio - mplayer - python + +modules: +- alsa? -- 2.7.4