#include "sink.h"
#include "source.h"
#include "client.h"
+#include "sinkinput.h"
+#include "sourceoutput.h"
struct cli {
struct core *core;
c->eof_callback = NULL;
ioline_set_callback(c->line, line_callback, c);
- ioline_puts(c->line, "Welcome to polypaudio!\n> ");
+ ioline_puts(c->line, "Welcome to polypaudio! Use \"help\" for usage information.\n> ");
return c;
}
ioline_puts(line, (t = sink_list_to_string(c->core)));
else if (!strcmp(s, "clients"))
ioline_puts(line, (t = client_list_to_string(c->core)));
- else if (!strcmp(s, "exit")) {
+ else if (!strcmp(s, "source_outputs"))
+ ioline_puts(line, (t = source_output_list_to_string(c->core)));
+ else if (!strcmp(s, "sink_inputs"))
+ ioline_puts(line, (t = sink_input_list_to_string(c->core)));
+ else if (!strcmp(s, "stat")) {
+ char txt[256];
+ snprintf(txt, sizeof(txt), "Memory blocks allocated: %u, total size: %u bytes.\n", memblock_count, memblock_total);
+ ioline_puts(line, txt);
+ } else if (!strcmp(s, "exit")) {
assert(c->core && c->core->mainloop);
mainloop_quit(c->core->mainloop, -1);
- } else if (*s)
+ } else if (!strcmp(s, "help"))
+ ioline_puts(line,
+ "Available commands:\n"
+ " modules\t\tlist modules\n"
+ " sinks\t\tlist sinks\n"
+ " sources\t\tlist sources\n"
+ " clients\t\tlist clients\n"
+ " source_outputs\tlist source outputs\n"
+ " sink_inputs\t\tlist sink inputs\n"
+ " stat\t\tshow memblock statistics\n"
+ " exit\t\tterminate the daemon\n"
+ " help\t\tshow this help\n");
+ else if (*s)
ioline_puts(line, "Unknown command\n");
free(t);
#include "memblock.h"
-unsigned n_blocks = 0;
+unsigned memblock_count = 0, memblock_total = 0;
struct memblock *memblock_new(size_t length) {
struct memblock *b = malloc(sizeof(struct memblock)+length);
b->ref = 1;
b->length = length;
b->data = b+1;
- n_blocks++;
+ memblock_count++;
+ memblock_total += length;
return b;
}
b->ref = 1;
b->length = length;
b->data = d;
- n_blocks++;
+ memblock_count++;
+ memblock_total += length;
return b;
}
b->ref = 1;
b->length = length;
b->data = d;
- n_blocks++;
+ memblock_count++;
+ memblock_total += length;
return b;
}
if (b->ref == 0) {
if (b->type == MEMBLOCK_DYNAMIC)
free(b->data);
+
+ memblock_count--;
+ memblock_total -= b->length;
+
free(b);
- n_blocks--;
}
}
#define memblock_assert_exclusive(b) assert((b)->ref == 1)
-extern unsigned n_blocks;
+extern unsigned memblock_count, memblock_total;
#endif
struct memblock_list *blocks, *blocks_tail;
unsigned n_blocks;
size_t total_length, maxlength, base, prebuf;
- int measure_latency;
- uint32_t latency;
+ int measure_delay;
+ uint32_t delay;
};
struct memblockq* memblockq_new(size_t maxlength, size_t base, size_t prebuf) {
assert(bq->maxlength >= base);
- bq->measure_latency = 1;
- bq->latency = 0;
+ bq->measure_delay = 0;
+ bq->delay = 0;
return bq;
}
q = malloc(sizeof(struct memblock_list));
assert(q);
- if (bq->measure_latency)
+ if (bq->measure_delay)
gettimeofday(&q->stamp, NULL);
else
timerclear(&q->stamp);
if (l > bq->blocks->chunk.length)
l = bq->blocks->chunk.length;
- if (bq->measure_latency)
- bq->latency = age(&bq->blocks->stamp);
+ if (bq->measure_delay)
+ bq->delay = age(&bq->blocks->stamp);
bq->blocks->chunk.index += l;
bq->blocks->chunk.length -= l;
return bq->total_length + length <= bq->maxlength;
}
-uint32_t memblockq_get_latency(struct memblockq *bq) {
- return bq->latency;
+uint32_t memblockq_get_delay(struct memblockq *bq) {
+ assert(bq);
+ return bq->delay;
+}
+
+uint32_t memblockq_get_length(struct memblockq *bq) {
+ assert(bq);
+ return bq->total_length;
}
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);
+uint32_t memblockq_get_delay(struct memblockq *bq);
+uint32_t memblockq_get_length(struct memblockq *bq);
#endif
struct memchunk memchunk, silence;
- uint32_t in_fragment_size, out_fragment_size, sample_size, sample_usec;
+ uint32_t in_fragment_size, out_fragment_size, sample_size;
int fd;
};
static uint32_t sink_get_latency_cb(struct sink *s) {
int arg;
struct userdata *u = s->userdata;
- assert(s && u);
+ assert(s && u && u->sink);
if (ioctl(u->fd, SNDCTL_DSP_GETODELAY, &arg) < 0) {
fprintf(stderr, "module-oss: device doesn't support SNDCTL_DSP_GETODELAY.\n");
return 0;
}
- return arg/u->sample_size*u->sample_usec;
+ return samples_usec(arg, &s->sample_spec);
}
int module_init(struct core *c, struct module*m) {
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;
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;
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;
}
/*** sink_input callbacks ***/
-static int sink_input_peek_cb(struct sink_input *i, struct memchunk *chunk, uint8_t *volume) {
+static int sink_input_peek_cb(struct sink_input *i, struct memchunk *chunk) {
struct connection*c = i->userdata;
- assert(i && c && chunk && volume);
+ assert(i && c && chunk);
if (memblockq_peek(c->input_memblockq, chunk) < 0)
return -1;
- *volume = 0xFF;
return 0;
}
destroy_connection((struct connection *) i->userdata);
}
+
+static uint32_t sink_input_get_latency_cb(struct sink_input *i) {
+ struct connection*c = i->userdata;
+ assert(i && c);
+ return samples_usec(memblockq_get_length(c->input_memblockq), &DEFAULT_SAMPLE_SPEC);
+}
+
/*** source_output callbacks ***/
static void source_output_push_cb(struct source_output *o, struct memchunk *chunk) {
destroy_connection((struct connection *) o->userdata);
}
-
/*** client callbacks ***/
static void client_kill_cb(struct client *c) {
c->sink_input->peek = sink_input_peek_cb;
c->sink_input->drop = sink_input_drop_cb;
c->sink_input->kill = sink_input_kill_cb;
+ c->sink_input->get_latency = sink_input_get_latency_cb;
c->sink_input->userdata = c;
l = bytes_per_second(&DEFAULT_SAMPLE_SPEC)/2; /* half a second */
data += sizeof(int16_t);
}
}
+
+uint32_t samples_usec(size_t length, struct sample_spec *spec) {
+ assert(spec);
+
+ return (uint32_t) (((double) length /sample_size(spec))/spec->rate*1000000);
+}
size_t bytes_per_second(struct sample_spec *spec);
size_t sample_size(struct sample_spec *spec);
+uint32_t samples_usec(size_t length, struct sample_spec *spec);
#endif
for (i = idxset_first(s->inputs, &index); maxinfo > 0 && i; i = idxset_next(s->inputs, &index)) {
assert(i->peek);
- if (i->peek(i, &info->chunk, &info->volume) < 0)
+ if (i->peek(i, &info->chunk) < 0)
continue;
+ info->volume = i->volume;
+
assert(info->chunk.memblock && info->chunk.memblock->data && info->chunk.length);
info->userdata = i;
#include <string.h>
#include "sinkinput.h"
+#include "strbuf.h"
struct sink_input* sink_input_new(struct sink *s, struct sample_spec *spec, const char *name) {
struct sink_input *i;
i->peek = NULL;
i->drop = NULL;
i->kill = NULL;
+ i->get_latency = NULL;
i->userdata = NULL;
assert(s->core);
if (i->kill)
i->kill(i);
}
+
+char *sink_input_list_to_string(struct core *c) {
+ struct strbuf *s;
+ struct sink_input *i;
+ uint32_t index = IDXSET_INVALID;
+ assert(c);
+
+ s = strbuf_new();
+ assert(s);
+
+ strbuf_printf(s, "%u sink input(s) available.\n", idxset_ncontents(c->sink_inputs));
+
+ for (i = idxset_first(c->sink_inputs, &index); i; i = idxset_next(c->sink_inputs, &index)) {
+ assert(i->sink);
+ strbuf_printf(s, " index: %u, name: <%s>, sink: <%u>; volume: <0x%02x>, latency: <%u usec>\n",
+ i->index,
+ i->name,
+ i->sink->index,
+ (unsigned) i->volume,
+ sink_input_get_latency(i));
+ }
+
+ return strbuf_tostring_free(s);
+}
+
+uint32_t sink_input_get_latency(struct sink_input *i) {
+ uint32_t l = 0;
+
+ assert(i);
+ if (i->get_latency)
+ l += i->get_latency(i);
+
+ assert(i->sink);
+ l += sink_get_latency(i->sink);
+
+ return l;
+}
char *name;
struct sink *sink;
struct sample_spec spec;
+ uint8_t volume;
- int (*peek) (struct sink_input *i, struct memchunk *chunk, uint8_t *volume);
+ int (*peek) (struct sink_input *i, struct memchunk *chunk);
void (*drop) (struct sink_input *i, size_t length);
void (*kill) (struct sink_input *i);
+ uint32_t (*get_latency) (struct sink_input *i);
+
void *userdata;
};
* request destruction of it */
void sink_input_kill(struct sink_input *i);
+uint32_t sink_input_get_latency(struct sink_input *i);
+char *sink_input_list_to_string(struct core *c);
#endif
#include <string.h>
#include "sourceoutput.h"
+#include "strbuf.h"
struct source_output* source_output_new(struct source *s, struct sample_spec *spec, const char *name) {
struct source_output *o;
if (i->kill)
i->kill(i);
}
+
+char *source_output_list_to_string(struct core *c) {
+ struct strbuf *s;
+ struct source_output *o;
+ uint32_t index = IDXSET_INVALID;
+ assert(c);
+
+ s = strbuf_new();
+ assert(s);
+
+ strbuf_printf(s, "%u source outputs(s) available.\n", idxset_ncontents(c->source_outputs));
+
+ for (o = idxset_first(c->source_outputs, &index); o; o = idxset_next(c->source_outputs, &index)) {
+ assert(o->source);
+ strbuf_printf(s, " %c index: %u, name: <%s>, source: <%u>\n",
+ o->index,
+ o->name,
+ o->source->index);
+ }
+
+ return strbuf_tostring_free(s);
+}
void source_output_kill(struct source_output*o);
+char *source_output_list_to_string(struct core *c);
+
#endif