# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
AC_PREREQ(2.57)
-AC_INIT([polypaudio],[0.4],[mzcbylcnhqvb (at) 0pointer (dot) de])
+AC_INIT([polypaudio],[0.5],[mzcbylcnhqvb (at) 0pointer (dot) de])
AC_CONFIG_SRCDIR([polyp/main.c])
AC_CONFIG_HEADERS([config.h])
AM_INIT_AUTOMAKE([foreign -Wall])
- add sample directory
- config file for command line arguments
- option to use default fragment size on alsa drivers
+- keep volume in xmms-polyp (and allow volume changing when not playing)
+- lazy sample cache
+- per-channel volume
+- add version number to library names
+- extend pa_usec_t to 64 bit
+- make use of network latency in all apps
+- rename streams/contexts
** later ***
- xmlrpc/http
***********
backends for:
-- mplayer
-- sdl
-- gstreamer
- portaudio
+- sdl
+- gstreamer (semi-done)
+- alsa-lib
+- OSS (esddsp style)
" -X SECS Terminate the daemon after the last client quit and this time passed\n"
" -h Show this help\n"
" -l TARGET Specify the log target (syslog, stderr, auto)\n"
+ " -p DIR Append a directory to the search path for dynamic modules\n"
" -V Show version\n", e, cfg);
pa_xfree(cfg);
cmdline->fail = cmdline->auto_log_target = 1;
cmdline->quit_after_last_client_time = -1;
cmdline->log_target = -1;
+ cmdline->dl_searchdir = NULL;
buf = pa_strbuf_new();
assert(buf);
- while ((c = getopt(argc, argv, "L:F:CDhfvrRVndX:l:")) != -1) {
+ while ((c = getopt(argc, argv, "L:F:CDhfvrRVndX:l:p:")) != -1) {
switch (c) {
case 'L':
pa_strbuf_printf(buf, "load %s\n", optarg);
case 'X':
cmdline->quit_after_last_client_time = atoi(optarg);
break;
+ case 'p':
+ if (cmdline->dl_searchdir)
+ pa_xfree(cmdline->dl_searchdir);
+ cmdline->dl_searchdir = pa_xstrdup(optarg);
+ break;
case 'l':
if (!strcmp(optarg, "syslog")) {
cmdline->auto_log_target = 0;
void pa_cmdline_free(struct pa_cmdline *cmd) {
assert(cmd);
pa_xfree(cmd->cli_commands);
+ pa_xfree(cmd->dl_searchdir);
pa_xfree(cmd);
}
quit_after_last_client_time,
auto_log_target;
char *cli_commands;
+ char *dl_searchdir;
enum pa_log_target log_target;
};
r = lt_dlinit();
assert(r == 0);
+
+ if (cmdline->dl_searchdir)
+ lt_dladdsearchdir(cmdline->dl_searchdir);
+
#ifdef DLSEARCHDIR
lt_dladdsearchdir(DLSEARCHDIR);
#endif
}
}
-static void dispatch_pollfds(struct pa_mainloop *m) {
+static int dispatch_pollfds(struct pa_mainloop *m) {
uint32_t index = PA_IDXSET_INVALID;
struct pa_io_event *e;
+ int r = 0;
for (e = pa_idxset_first(m->io_events, &index); e; e = pa_idxset_next(m->io_events, &index)) {
if (e->dead || !e->pollfd || !e->pollfd->revents)
(e->pollfd->revents & POLLERR ? PA_IO_EVENT_ERROR : 0),
e->userdata);
e->pollfd->revents = 0;
+ r++;
}
+
+ return r;
}
-static void dispatch_defer(struct pa_mainloop *m) {
+static int dispatch_defer(struct pa_mainloop *m) {
uint32_t index;
struct pa_defer_event *e;
+ int r = 0;
for (e = pa_idxset_first(m->defer_events, &index); e; e = pa_idxset_next(m->defer_events, &index)) {
if (e->dead || !e->enabled)
assert(e->callback);
e->callback(&m->api, e, e->userdata);
+ r++;
}
+
+ return r;
}
static int calc_next_timeout(struct pa_mainloop *m) {
return t;
}
-static void dispatch_timeout(struct pa_mainloop *m) {
+static int dispatch_timeout(struct pa_mainloop *m) {
uint32_t index;
struct pa_time_event *e;
struct timeval now;
int got_time = 0;
+ int r = 0;
assert(m);
if (pa_idxset_isempty(m->time_events))
- return;
+ return 0;
for (e = pa_idxset_first(m->time_events, &index); e; e = pa_idxset_next(m->time_events, &index)) {
e->enabled = 0;
e->callback(&m->api, e, &e->timeval, e->userdata);
+
+ r++;
}
}
+
+ return r;
}
int pa_mainloop_iterate(struct pa_mainloop *m, int block, int *retval) {
- int r;
+ int r, t, dispatched = 0;
assert(m && !m->running);
if(m->quit) {
if (retval)
*retval = m->retval;
- return 1;
+ return -2;
}
m->running = 1;
scan_dead(m);
- dispatch_defer(m);
+ dispatched += dispatch_defer(m);
if (m->rebuild_pollfds) {
rebuild_pollfds(m);
m->rebuild_pollfds = 0;
}
- do {
- int t = block ? calc_next_timeout(m) : 0;
- /*pa_log(__FILE__": %u\n", t);*/
- r = poll(m->pollfds, m->n_pollfds, t);
- } while (r < 0 && errno == EINTR);
+ t = block ? calc_next_timeout(m) : 0;
+ r = poll(m->pollfds, m->n_pollfds, t);
- dispatch_timeout(m);
-
- if (r > 0)
- dispatch_pollfds(m);
- else if (r < 0)
- pa_log(__FILE__": select(): %s\n", strerror(errno));
+ if (r < 0) {
+ if (errno == EINTR)
+ r = 0;
+ else
+ pa_log(__FILE__": select(): %s\n", strerror(errno));
+ } else {
+ dispatched += dispatch_timeout(m);
+
+ if (r > 0)
+ dispatched += dispatch_pollfds(m);
+ }
m->running = 0;
- return r < 0 ? -1 : 0;
+
+/* pa_log("dispatched: %i\n", dispatched); */
+
+ return r < 0 ? -1 : dispatched;
}
int pa_mainloop_run(struct pa_mainloop *m, int *retval) {
int r;
- while ((r = pa_mainloop_iterate(m, 1, retval)) == 0);
- return r;
+ while ((r = pa_mainloop_iterate(m, 1, retval)) >= 0);
+
+ if (r == -2)
+ return 1;
+ else if (r < 0)
+ return -1;
+ else
+ return 0;
}
void pa_mainloop_quit(struct pa_mainloop *m, int r) {
on error or exit request. If block is nonzero, block for events if
none are queued. Optionally return the return value as specified with
the main loop's quit() routine in the integer variable retval points
-to */
+to. On success returns the number of source dispatched in this iteration. */
int pa_mainloop_iterate(struct pa_mainloop *m, int block, int *retval);
/** Run unlimited iterations of the main loop object until the main loop's quit() routine is called. */
void pa_memblockq_drop(struct pa_memblockq *bq, const struct pa_memchunk *chunk, size_t length) {
assert(bq && chunk && length);
- if (!bq->blocks || memcmp(&bq->blocks->chunk, chunk, sizeof(struct pa_memchunk)))
+ if (!bq->blocks || memcmp(&bq->blocks->chunk, chunk, sizeof(struct pa_memchunk)))
return;
-
+
assert(length <= bq->blocks->chunk.length);
pa_memblockq_skip(bq, length);
}
int pa_context_is_pending(struct pa_context *c) {
assert(c && c->ref >= 1);
- if (c->state != PA_CONTEXT_READY)
- return 0;
-
- assert(c->pstream && c->pdispatch);
- return pa_pstream_is_pending(c->pstream) || pa_pdispatch_is_pending(c->pdispatch);
+/* pa_log("pstream: %i\n", pa_pstream_is_pending(c->pstream)); */
+/* pa_log("pdispatch: %i\n", pa_pdispatch_is_pending(c->pdispatch)); */
+
+ return (c->pstream && pa_pstream_is_pending(c->pstream)) || (c->pdispatch && pa_pdispatch_is_pending(c->pdispatch)) || c->client;
}
static void set_dispatch_callbacks(struct pa_operation *o);
/** A structure for latency info. See pa_stream_get_latency(). The
* total latency a sample that is written with pa_stream_write() takes
- * to be played is buffer_usec+sink_usec. The buffer to which
+ * to be played may be estimated by
+ * buffer_usec+sink_usec+transport_usec. The buffer to which
* buffer_usec relates may be manipulated freely (with
* pa_stream_write()'s delta argument, pa_stream_flush() and friends),
* the playback buffer sink_usec relates to is a FIFO which cannot be
struct pa_latency_info {
pa_usec_t buffer_usec; /**< Time in usecs the current buffer takes to play */
pa_usec_t sink_usec; /**< Time in usecs a sample takes to be played on the sink. */
+ pa_usec_t transport_usec; /**< Estimated time in usecs a sample takes to be transferred to the daemon. \since 0.5 */
int playing; /**< Non-zero when the stream is currently playing */
uint32_t queue_length; /**< Queue size in bytes. */
};
#include "polyplib-internal.h"
#include "xmalloc.h"
#include "pstream-util.h"
+#include "util.h"
struct pa_stream *pa_stream_new(struct pa_context *c, const char *name, const struct pa_sample_spec *ss) {
struct pa_stream *s;
if (pa_tagstruct_getu32(t, &s->channel) < 0 ||
((s->direction != PA_STREAM_UPLOAD) && pa_tagstruct_getu32(t, &s->device_index) < 0) ||
+ ((s->direction == PA_STREAM_PLAYBACK) && pa_tagstruct_getu32(t, &s->requested_bytes) < 0) ||
!pa_tagstruct_eof(t)) {
pa_context_fail(s->context, PA_ERROR_PROTOCOL);
goto finish;
pa_dynarray_put((s->direction == PA_STREAM_RECORD) ? s->context->record_streams : s->context->playback_streams, s->channel, s);
pa_stream_set_state(s, PA_STREAM_READY);
+ if (s->requested_bytes && s->ref > 1 && s->write_callback)
+ s->write_callback(s, s->requested_bytes, s->write_userdata);
+
finish:
pa_stream_unref(s);
}
static void stream_get_latency_callback(struct pa_pdispatch *pd, uint32_t command, uint32_t tag, struct pa_tagstruct *t, void *userdata) {
struct pa_operation *o = userdata;
struct pa_latency_info i, *p = NULL;
+ struct timeval local, remote, now;
assert(pd && o && o->stream && o->context);
if (command != PA_COMMAND_REPLY) {
pa_tagstruct_getu32(t, &i.sink_usec) < 0 ||
pa_tagstruct_get_boolean(t, &i.playing) < 0 ||
pa_tagstruct_getu32(t, &i.queue_length) < 0 ||
+ pa_tagstruct_get_timeval(t, &local) < 0 ||
+ pa_tagstruct_get_timeval(t, &remote) < 0 ||
!pa_tagstruct_eof(t)) {
pa_context_fail(o->context, PA_ERROR_PROTOCOL);
goto finish;
} else
p = &i;
+ gettimeofday(&now, NULL);
+
+ if (pa_timeval_cmp(&local, &remote) < 0 && pa_timeval_cmp(&remote, &now))
+ /* local and remote seem to have synchronized clocks */
+ i.transport_usec = pa_timeval_diff(&remote, &local);
+ else
+ /* clocks are not synchronized, let's estimate latency then */
+ i.transport_usec = pa_timeval_diff(&now, &local)/2;
+
if (o->callback) {
void (*cb)(struct pa_stream *s, const struct pa_latency_info *i, void *userdata) = o->callback;
cb(o->stream, p, o->userdata);
uint32_t tag;
struct pa_operation *o;
struct pa_tagstruct *t;
+ struct timeval now;
o = pa_operation_new(s->context, s);
assert(o);
pa_tagstruct_putu32(t, PA_COMMAND_GET_PLAYBACK_LATENCY);
pa_tagstruct_putu32(t, tag = s->context->ctag++);
pa_tagstruct_putu32(t, s->channel);
+
+ gettimeofday(&now, NULL);
+ pa_tagstruct_put_timeval(t, &now);
+
pa_pstream_send_tagstruct(s->context->pstream, t);
pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, stream_get_latency_callback, o);
if (!(l = pa_memblockq_missing(s->memblockq)))
return;
-
+
if (l <= s->requested_bytes)
return;
pa_tagstruct_putu32(t, l);
pa_pstream_send_tagstruct(s->connection->pstream, t);
- /*pa_log(__FILE__": Requesting %u bytes\n", l);*/
+/* pa_log(__FILE__": Requesting %u bytes\n", l); */
}
static void send_memblock(struct connection *c) {
pa_tagstruct_putu32(reply, s->index);
assert(s->sink_input);
pa_tagstruct_putu32(reply, s->sink_input->index);
+ pa_tagstruct_putu32(reply, s->requested_bytes = pa_memblockq_missing(s->memblockq));
pa_pstream_send_tagstruct(c->pstream, reply);
request_bytes(s);
}
struct connection *c = userdata;
struct pa_tagstruct *reply;
struct playback_stream *s;
+ struct timeval tv, now;
uint32_t index;
assert(c && t);
if (pa_tagstruct_getu32(t, &index) < 0 ||
+ pa_tagstruct_get_timeval(t, &tv) < 0 ||
!pa_tagstruct_eof(t)) {
protocol_error(c);
return;
pa_tagstruct_putu32(reply, pa_sink_get_latency(s->sink_input->sink));
pa_tagstruct_put_boolean(reply, pa_memblockq_is_readable(s->memblockq));
pa_tagstruct_putu32(reply, pa_memblockq_get_length(s->memblockq));
+ pa_tagstruct_put_timeval(reply, &tv);
+ gettimeofday(&now, NULL);
+ pa_tagstruct_put_timeval(reply, &now);
pa_pstream_send_tagstruct(c->pstream, reply);
}
/*pa_log(__FILE__": after_recv: %u\n", pa_memblockq_get_length(p->memblockq));*/
pa_sink_notify(p->sink_input->sink);
- /*pa_log(__FILE__": Recieved %u bytes.\n", chunk->length);*/
+/* pa_log(__FILE__": Recieved %u bytes.\n", chunk->length); */
} else {
struct upload_stream *u = (struct upload_stream*) stream;
struct item_info *i;
assert(p && packet);
- /*pa_log(__FILE__": push-packet %p\n", packet);*/
+/* pa_log(__FILE__": push-packet %p\n", packet); */
i = pa_xmalloc(sizeof(struct item_info));
i->type = PA_PSTREAM_ITEM_PACKET;
void pa_pstream_send_memblock(struct pa_pstream*p, uint32_t channel, uint32_t delta, const struct pa_memchunk *chunk) {
struct item_info *i;
assert(p && channel != (uint32_t) -1 && chunk);
+
+/* pa_log(__FILE__": push-memblock %p\n", chunk); */
i = pa_xmalloc(sizeof(struct item_info));
i->type = PA_PSTREAM_ITEM_MEMBLOCK;
#include <config.h>
#endif
-#include <stdlib.h>
#include <assert.h>
#include <samplerate.h>
#include "resampler.h"
#include "sconv.h"
#include "xmalloc.h"
+#include "log.h"
struct pa_resampler {
struct pa_sample_spec i_ss, o_ss;
/* How many input samples? */
ins = in->length/r->i_sz;
+/* pa_log("%u / %u = %u\n", in->length, r->i_sz, ins); */
+
/* How much space for output samples? */
if (r->src_state)
ons = (ins*r->o_ss.rate/r->i_ss.rate)+1024;
eff_ins = ins;
eff_ons = ons;
}
+
+/* pa_log("eff_ins = %u \n", eff_ins); */
+
out->memblock = pa_memblock_new(out->length = (ons*r->o_sz), r->memblock_stat);
out->index = 0;
if (r->i_alloc < eff_ins)
r->i_buf = pa_xrealloc(r->i_buf, sizeof(float) * (r->i_alloc = eff_ins));
assert(r->i_buf);
-
+
+/* pa_log("eff_ins = %u \n", eff_ins); */
+
r->to_float32_func(eff_ins, (uint8_t*) in->memblock->data+in->index, i_nchannels, r->i_buf);
if (r->src_state) {
} else
cbuf = r->i_buf;
- r->from_float32_func(eff_ons, cbuf, (uint8_t*)out->memblock->data+out->index, o_nchannels);
+ if (eff_ons)
+ r->from_float32_func(eff_ons, cbuf, (uint8_t*)out->memblock->data+out->index, o_nchannels);
out->length = ons*r->o_sz;
+
+
+ if (!out->length) {
+ pa_memblock_unref(out->memblock);
+ out->memblock = NULL;
+ }
}
#include "endianmacros.h"
#include "sconv.h"
+#include "log.h"
#ifndef INT16_FROM
#define INT16_FROM INT16_FROM_LE
void pa_sconv_s16le_from_float32(unsigned n, const float *a, void *b, unsigned bn) {
int16_t *cb = b;
+
+/* pa_log("%u %p %p %u\n", n, a, b, bn); */
+
assert(n && a && b && bn);
for (; n > 0; n--) {
if (!i->resampler)
return i->peek(i, chunk);
- if (!i->resampled_chunk.memblock) {
+ while (!i->resampled_chunk.memblock) {
struct pa_memchunk tchunk;
size_t l;
int ret;
l = pa_resampler_request(i->resampler, CONVERT_BUFFER_LENGTH);
- if (tchunk.length > l)
- tchunk.length = l;
+ if (l > tchunk.length)
+ l = tchunk.length;
- i->drop(i, &tchunk, tchunk.length);
+ i->drop(i, &tchunk, l);
+ tchunk.length = l;
pa_resampler_run(i->resampler, &tchunk, &i->resampled_chunk);
pa_memblock_unref(tchunk.memblock);
TAG_ARBITRARY = 'x',
TAG_BOOLEAN_TRUE = '1',
TAG_BOOLEAN_FALSE = '0',
+ TAG_TIMEVAL = 'T',
};
struct pa_tagstruct {
t->length += 1;
}
+void pa_tagstruct_put_timeval(struct pa_tagstruct*t, const struct timeval *tv) {
+ assert(t);
+ extend(t, 9);
+ t->data[t->length] = TAG_TIMEVAL;
+ *((uint32_t*) (t->data+t->length+1)) = htonl(tv->tv_sec);
+ *((uint32_t*) (t->data+t->length+5)) = htonl(tv->tv_usec);
+ t->length += 9;
+}
+
int pa_tagstruct_gets(struct pa_tagstruct*t, const char **s) {
int error = 0;
size_t n;
return 0;
}
+int pa_tagstruct_get_timeval(struct pa_tagstruct*t, struct timeval *tv) {
+
+ if (t->rindex+9 > t->length)
+ return -1;
+
+ if (t->data[t->rindex] != TAG_TIMEVAL)
+ return -1;
+
+ tv->tv_sec = ntohl(*((uint32_t*) (t->data+t->rindex+1)));
+ tv->tv_usec = ntohl(*((uint32_t*) (t->data+t->rindex+5)));
+ t->rindex += 9;
+ return 0;
+
+}
void pa_tagstruct_put_sample_spec(struct pa_tagstruct *t, const struct pa_sample_spec *ss);
void pa_tagstruct_put_arbitrary(struct pa_tagstruct*t, const void *p, size_t length);
void pa_tagstruct_put_boolean(struct pa_tagstruct*t, int b);
+void pa_tagstruct_put_timeval(struct pa_tagstruct*t, const struct timeval *tv);
int pa_tagstruct_gets(struct pa_tagstruct*t, const char **s);
int pa_tagstruct_getu32(struct pa_tagstruct*t, uint32_t *i);
int pa_tagstruct_get_sample_spec(struct pa_tagstruct *t, struct pa_sample_spec *ss);
int pa_tagstruct_get_arbitrary(struct pa_tagstruct *t, const void **p, size_t length);
int pa_tagstruct_get_boolean(struct pa_tagstruct *t, int *b);
+int pa_tagstruct_get_timeval(struct pa_tagstruct*t, struct timeval *tv);
int pa_tagstruct_eof(struct pa_tagstruct*t);
const uint8_t* pa_tagstruct_data(struct pa_tagstruct*t, size_t *l);
return s;
}
-uint32_t pa_age(struct timeval *tv) {
- struct timeval now;
- uint32_t r;
- assert(tv);
+pa_usec_t pa_timeval_diff(const struct timeval *a, const struct timeval *b) {
+ pa_usec_t r;
+ assert(a && b);
+
+ if (pa_timeval_cmp(a, b) < 0) {
+ const struct timeval *c;
+ c = a;
+ a = b;
+ b = c;
+ }
- if (tv->tv_sec == 0)
- return 0;
+ r = (a->tv_sec - b->tv_sec)* 1000000;
- 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;
+ if (a->tv_usec > b->tv_usec)
+ r += (a->tv_usec - b->tv_usec);
+ else if (a->tv_usec < b->tv_usec)
+ r -= (b->tv_usec - a->tv_usec);
return r;
}
+int pa_timeval_cmp(const struct timeval *a, const struct timeval *b) {
+ assert(a && b);
+
+ if (a->tv_sec < b->tv_sec)
+ return -1;
+
+ if (a->tv_sec > b->tv_sec)
+ return 1;
+
+ if (a->tv_usec < b->tv_usec)
+ return -1;
+
+ if (a->tv_usec > b->tv_usec)
+ return 1;
+
+ return 0;
+}
+
+pa_usec_t pa_age(const struct timeval *tv) {
+ struct timeval now;
+ assert(tv);
+ gettimeofday(&now, NULL);
+ return pa_timeval_diff(&now, tv);
+}
+
#define NICE_LEVEL (-15)
void pa_raise_priority(void) {
#include <stdarg.h>
#include "gcc-printf.h"
+#include "sample.h"
void pa_make_nonblock_fd(int fd);
char *pa_get_user_name(char *s, size_t l);
char *pa_get_host_name(char *s, size_t l);
-uint32_t pa_age(struct timeval *tv);
+pa_usec_t pa_timeval_diff(const struct timeval *a, const struct timeval *b);
+int pa_timeval_cmp(const struct timeval *a, const struct timeval *b);
+pa_usec_t pa_age(const struct timeval *tv);
void pa_raise_priority(void);
void pa_reset_priority(void);