From 8c6593dabf3253e20fead143855267570a403c9a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 14 Sep 2004 17:52:11 +0000 Subject: add module-combine remove option "stay-root" clean up pa_conf git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@199 fefdeb5f-60dc-0310-8127-8f9354f1896f --- doc/todo | 9 +- polyp/Makefile.am | 7 +- polyp/cmdline.c | 19 +-- polyp/conf.c | 8 +- polyp/conf.h | 16 ++- polyp/config | 12 +- polyp/log.c | 1 + polyp/main.c | 57 ++++---- polyp/module-combine.c | 353 ++++++++++++++++++++++++++++++++++++++++++++++ polyp/module-sine.c | 2 +- polyp/play-memchunk.c | 2 +- polyp/protocol-esound.c | 2 +- polyp/protocol-native.c | 2 +- polyp/protocol-simple.c | 2 +- polyp/resampler.c | 18 ++- polyp/resampler.h | 2 + polyp/sink-input.c | 14 +- polyp/sink-input.h | 4 +- polyp/sound-file-stream.c | 2 +- polyp/util.c | 16 +++ polyp/util.h | 2 + 21 files changed, 469 insertions(+), 81 deletions(-) create mode 100644 polyp/module-combine.c diff --git a/doc/todo b/doc/todo index 135a336d..8daabc33 100644 --- a/doc/todo +++ b/doc/todo @@ -3,22 +3,25 @@ *** 0.5 *** - make mcalign merge chunks - use ref counting in more objects (i.e. sink, source, sink_input, source_output) -- unix socket directories include user name - native library/protocol: module load/unload kill client/... autoload management rename streams/contexts - more complete pactl -- add sample directory - option to use default fragment size on alsa drivers - lazy sample cache -- per-channel volume - merge pa_context_connect_* - input latency - fix tcp/native - add volume to create_stream command in native protocol - udp based protocol +- make sure not to allow recursive auto load + +*** 0.6 **** +- per-channel volume +- unix socket directories include user name +- add sample directory ** later *** - xmlrpc/http diff --git a/polyp/Makefile.am b/polyp/Makefile.am index e9e1c295..9c672a3b 100644 --- a/polyp/Makefile.am +++ b/polyp/Makefile.am @@ -101,7 +101,8 @@ modlib_LTLIBRARIES= \ module-native-protocol-tcp.la \ module-native-protocol-unix.la \ module-native-protocol-fd.la \ - module-sine.la + module-sine.la \ + module-combine.la lib_LTLIBRARIES= \ libpolyp-@PA_MAJORMINOR@.la \ @@ -295,6 +296,10 @@ module_sine_la_SOURCES = module-sine.c module_sine_la_LDFLAGS = -module -avoid-version module_sine_la_LIBADD = $(AM_LIBADD) +module_combine_la_SOURCES = module-combine.c +module_combine_la_LDFLAGS = -module -avoid-version +module_combine_la_LIBADD = $(AM_LIBADD) + libpolyp_@PA_MAJORMINOR@_la_SOURCES = polyplib.h \ polyplib-def.h \ tagstruct.c tagstruct.h \ diff --git a/polyp/cmdline.c b/polyp/cmdline.c index 4e7cde48..47685ca9 100644 --- a/polyp/cmdline.c +++ b/polyp/cmdline.c @@ -44,7 +44,6 @@ enum { ARG_FAIL, ARG_VERBOSE, ARG_HIGH_PRIORITY, - ARG_STAY_ROOT, ARG_DISALLOW_MODULE_LOADING, ARG_EXIT_IDLE_TIME, ARG_MODULE_IDLE_TIME, @@ -63,7 +62,6 @@ static struct option long_options[] = { {"fail", 2, 0, ARG_FAIL}, {"verbose", 2, 0, ARG_VERBOSE}, {"high-priority", 2, 0, ARG_HIGH_PRIORITY}, - {"stay-root", 2, 0, ARG_STAY_ROOT}, {"disallow-module-loading", 2, 0, ARG_DISALLOW_MODULE_LOADING}, {"exit-idle-time", 2, 0, ARG_EXIT_IDLE_TIME}, {"module-idle-time", 2, 0, ARG_MODULE_IDLE_TIME}, @@ -93,7 +91,6 @@ void pa_cmdline_help(const char *argv0) { " --fail[=BOOL] Quit when startup fails\n" " --verbose[=BOOL] Be slightly more verbose\n" " --high-priority[=BOOL] Try to set high process priority (only available as root)\n" - " --stay-root[=BOOL] Don't drop root if SETUID root\n" " --disallow-module-loading[=BOOL] Disallow module loading after startup\n" " --exit-idle-time=SECS Terminate the daemon when idle and this time passed\n" " --module-idle-time=SECS Unload autoloaded modules when idle and this time passed\n" @@ -113,7 +110,6 @@ int pa_cmdline_parse(struct pa_conf *conf, int argc, char *const argv [], int *d assert(conf && argc && argv); buf = pa_strbuf_new(); - assert(buf); if (conf->script_commands) pa_strbuf_puts(buf, conf->script_commands); @@ -122,19 +118,19 @@ int pa_cmdline_parse(struct pa_conf *conf, int argc, char *const argv [], int *d switch (c) { case ARG_HELP: case 'h': - conf->help = 1; + conf->cmd = PA_CMD_HELP; break; case ARG_VERSION: - conf->version = 1; + conf->cmd = PA_CMD_VERSION; break; case ARG_DUMP_CONF: - conf->dump_conf = 1; + conf->cmd = PA_CMD_DUMP_CONF; break; case ARG_DUMP_MODULES: - conf->dump_modules = 1; + conf->cmd = PA_CMD_DUMP_MODULES; break; case ARG_LOAD: @@ -180,13 +176,6 @@ int pa_cmdline_parse(struct pa_conf *conf, int argc, char *const argv [], int *d } break; - case ARG_STAY_ROOT: - if ((conf->stay_root = optarg ? pa_parse_boolean(optarg) : 1) < 0) { - pa_log(__FILE__": --stay-root expects boolean argument\n"); - goto fail; - } - break; - case ARG_DISALLOW_MODULE_LOADING: if ((conf->disallow_module_loading = optarg ? pa_parse_boolean(optarg) : 1) < 0) { pa_log(__FILE__": --disallow-module-loading expects boolean argument\n"); diff --git a/polyp/conf.c b/polyp/conf.c index 4a11c480..9f1f6ba2 100644 --- a/polyp/conf.c +++ b/polyp/conf.c @@ -35,15 +35,11 @@ #include "strbuf.h" static const struct pa_conf default_conf = { - .help = 0, + .cmd = PA_CMD_DAEMON, .daemonize = 0, - .dump_conf = 0, - .dump_modules = 0, .fail = 1, .verbose = 0, .high_priority = 0, - .stay_root = 0, - .version = 0, .disallow_module_loading = 0, .exit_idle_time = -1, .module_idle_time = 20, @@ -143,7 +139,6 @@ static int next_assignment(struct pa_conf *c, char *lvalue, char *rvalue, unsign PARSE_BOOLEAN("fail", fail); PARSE_BOOLEAN("verbose", verbose); PARSE_BOOLEAN("high-priority", high_priority); - PARSE_BOOLEAN("stay-root", stay_root); PARSE_BOOLEAN("disallow-module-loading", disallow_module_loading); PARSE_INTEGER("exit-idle-time", exit_idle_time); @@ -269,7 +264,6 @@ char *pa_conf_dump(struct pa_conf *c) { pa_strbuf_printf(s, "daemonize = %i\n", !!c->daemonize); pa_strbuf_printf(s, "fail = %i\n", !!c->fail); pa_strbuf_printf(s, "high-priority = %i\n", !!c->high_priority); - pa_strbuf_printf(s, "stay-root = %i\n", !!c->stay_root); pa_strbuf_printf(s, "disallow-module-loading = %i\n", !!c->disallow_module_loading); pa_strbuf_printf(s, "exit-idle-time = %i\n", c->exit_idle_time); pa_strbuf_printf(s, "module-idle-time = %i\n", c->module_idle_time); diff --git a/polyp/conf.h b/polyp/conf.h index bbe253c2..da61be33 100644 --- a/polyp/conf.h +++ b/polyp/conf.h @@ -24,16 +24,20 @@ #include "log.h" +enum pa_conf_cmd { + PA_CMD_DAEMON, + PA_CMD_HELP, + PA_CMD_VERSION, + PA_CMD_DUMP_CONF, + PA_CMD_DUMP_MODULES +}; + struct pa_conf { - int help, - version, - dump_conf, - dump_modules, - daemonize, + enum pa_conf_cmd cmd; + int daemonize, fail, verbose, high_priority, - stay_root, disallow_module_loading, exit_idle_time, module_idle_time, diff --git a/polyp/config b/polyp/config index e0f8de50..b404bc40 100644 --- a/polyp/config +++ b/polyp/config @@ -31,11 +31,10 @@ ## Renice the daemon to level -15 and try to get SCHED_FIFO ## scheduling. This a good idea if you hear annyoing noise in the -## playback. However, this is a certain security issue. -; high-priority = 0 - -## Don't drop root rights on startup if called SUID root. -; stay-root = 0 +## playback. However, this is a certain security issue, since it works +## when called SUID root only. root is dropped immediately after gaining +## the nice level and SCHED_FIFO scheduling on startup. +high-priority = 0 ## Disallow module loading after startup ; disallow-module-loading = 0 @@ -48,7 +47,8 @@ module-idle-time = 20 ## The path were to look for dynamic shared objects (DSOs aka plugins). -## Specify an empty string for the default search path. +## Specify an empty string for the default search path. You may specify +## more than one path seperated by colons. ; dl-search-path = ## The default script file to load. Specify an empty string for not diff --git a/polyp/log.c b/polyp/log.c index 413266df..516cd2b0 100644 --- a/polyp/log.c +++ b/polyp/log.c @@ -41,6 +41,7 @@ void pa_log(const char *format, ...) { char *t = pa_vsprintf_malloc(format, ap); assert(user_log_func); user_log_func(t); + pa_xfree(t); } } diff --git a/polyp/main.c b/polyp/main.c index 148dfac2..9de69ca6 100644 --- a/polyp/main.c +++ b/polyp/main.c @@ -124,6 +124,11 @@ int main(int argc, char *argv[]) { pa_log_set_target(conf->auto_log_target ? PA_LOG_STDERR : conf->log_target, NULL); + if (conf->high_priority && conf->cmd == PA_CMD_DAEMON) + pa_raise_priority(); + + drop_root(); + if (conf->dl_search_path) lt_dlsetsearchpath(conf->dl_search_path); #ifdef DLSEARCHPATH @@ -131,37 +136,33 @@ int main(int argc, char *argv[]) { lt_dlsetsearchpath(DLSEARCHPATH); #endif - if (conf->dump_modules) { - pa_dump_modules(conf, argc-d, argv+d); - retval = 0; - goto finish; - } - - if (conf->dump_conf) { - char *s = pa_conf_dump(conf); - fputs(s, stdout); - pa_xfree(s); - retval = 0; - goto finish; - } + switch (conf->cmd) { + case PA_CMD_DUMP_MODULES: + pa_dump_modules(conf, argc-d, argv+d); + retval = 0; + goto finish; - if (conf->help) { - pa_cmdline_help(argv[0]); - retval = 0; - goto finish; - } + case PA_CMD_DUMP_CONF: { + char *s = pa_conf_dump(conf); + fputs(s, stdout); + pa_xfree(s); + retval = 0; + goto finish; + } - if (conf->version) { - printf(PACKAGE_NAME" "PACKAGE_VERSION"\n"); - retval = 0; - goto finish; - } + case PA_CMD_HELP : + pa_cmdline_help(argv[0]); + retval = 0; + goto finish; - if (conf->high_priority) - pa_raise_priority(); - - if (!conf->stay_root) - drop_root(); + case PA_CMD_VERSION : + printf(PACKAGE_NAME" "PACKAGE_VERSION"\n"); + retval = 0; + goto finish; + + default: + assert(conf->cmd == PA_CMD_DAEMON); + } if (conf->daemonize) { pid_t child; diff --git a/polyp/module-combine.c b/polyp/module-combine.c new file mode 100644 index 00000000..0ab9d9ec --- /dev/null +++ b/polyp/module-combine.c @@ -0,0 +1,353 @@ +/* $Id$ */ + +/*** + This file is part of polypaudio. + + polypaudio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + polypaudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with polypaudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include "module.h" +#include "llist.h" +#include "sink.h" +#include "sink-input.h" +#include "memblockq.h" +#include "log.h" +#include "util.h" +#include "xmalloc.h" +#include "modargs.h" +#include "namereg.h" + +PA_MODULE_AUTHOR("Lennart Poettering") +PA_MODULE_DESCRIPTION("Makes one playback device out of many") +PA_MODULE_VERSION(PACKAGE_VERSION) +PA_MODULE_USAGE("sink_name= master= slave=") + +#define DEFAULT_SINK_NAME "combine" +#define MEMBLOCKQ_MAXLENGTH (1024*170) +#define RENDER_SIZE (1024*10) + +#define ADJUST_TIME 5 + +static const char* const valid_modargs[] = { + "sink_name", + "master", + "slaves", + NULL +}; + +struct output { + struct userdata *userdata; + struct pa_sink_input *sink_input; + size_t counter; + struct pa_memblockq *memblockq; + pa_usec_t sink_latency; + PA_LLIST_FIELDS(struct output); +}; + +struct userdata { + struct pa_module *module; + struct pa_core *core; + struct pa_sink *sink; + unsigned n_outputs; + struct output *master; + struct pa_time_event *time_event; + + PA_LLIST_HEAD(struct output, outputs); +}; + +static void output_free(struct output *o); +static void clear_up(struct userdata *u); + +static void adjust_rates(struct userdata *u) { + struct output *o; + pa_usec_t max = 0; + uint32_t base_rate; + assert(u && u->sink); + + for (o = u->outputs; o; o = o->next) { + o->sink_latency = pa_sink_get_latency(o->sink_input->sink); + + if (o->sink_latency > max) + max = o->sink_latency; + } + + pa_log(__FILE__": [%s] maximum latency is %0.0f usec.\n", u->sink->name, (float) max); + + base_rate = u->sink->sample_spec.rate; + + for (o = u->outputs; o; o = o->next) { + pa_usec_t l; + uint32_t r = base_rate; + + l = o->sink_latency + pa_sink_input_get_latency(o->sink_input); + + if (l < max) + r -= (uint32_t) (((((double) max-l))/ADJUST_TIME)*r/ 1000000); + else if (l > max) + r += (uint32_t) (((((double) l-max))/ADJUST_TIME)*r/ 1000000); + + if (r < (uint32_t) (base_rate*0.9) || r > (uint32_t) (base_rate*1.1)) + pa_log(__FILE__": [%s] sample rates too different, not adjusting (%u vs. %u).\n", o->sink_input->name, base_rate, r); + else + pa_log(__FILE__": [%s] new rate is %u Hz; ratio is %0.3f; latency is %0.0f usec.\n", o->sink_input->name, r, (double) r / base_rate, (float) l); + + pa_sink_input_set_rate(o->sink_input, r); + } +} + +static void request_memblock(struct userdata *u) { + struct pa_memchunk chunk; + struct output *o; + assert(u && u->sink); + + if (pa_sink_render(u->sink, RENDER_SIZE, &chunk) < 0) + return; + + for (o = u->outputs; o; o = o->next) + pa_memblockq_push_align(o->memblockq, &chunk, 0); + + pa_memblock_unref(chunk.memblock); +} + +static void time_callback(struct pa_mainloop_api*a, struct pa_time_event* e, const struct timeval *tv, void *userdata) { + struct userdata *u = userdata; + struct timeval n; + assert(u && a && u->time_event == e); + + adjust_rates(u); + + gettimeofday(&n, NULL); + n.tv_sec += ADJUST_TIME; + u->sink->core->mainloop->time_restart(e, &n); +} + +static int sink_input_peek_cb(struct pa_sink_input *i, struct pa_memchunk *chunk) { + struct output *o = i->userdata; + assert(i && o && o->sink_input && chunk); + + if (pa_memblockq_peek(o->memblockq, chunk) >= 0) + return 0; + + /* Try harder */ + request_memblock(o->userdata); + + return pa_memblockq_peek(o->memblockq, chunk); +} + +static void sink_input_drop_cb(struct pa_sink_input *i, const struct pa_memchunk *chunk, size_t length) { + struct output *o = i->userdata; + assert(i && o && o->sink_input && chunk && length); + + pa_memblockq_drop(o->memblockq, chunk, length); + o->counter += length; +} + +static void sink_input_kill_cb(struct pa_sink_input *i) { + struct output *o = i->userdata; + assert(i && o && o->sink_input); + clear_up(o->userdata); +} + +static pa_usec_t sink_input_get_latency_cb(struct pa_sink_input *i) { + struct output *o = i->userdata; + assert(i && o && o->sink_input); + + return pa_bytes_to_usec(pa_memblockq_get_length(o->memblockq), &i->sample_spec); +} + +static pa_usec_t sink_get_latency_cb(struct pa_sink *s) { + struct userdata *u = s->userdata; + assert(s && u && u->sink && u->master); + + return pa_sink_input_get_latency(u->master->sink_input); +} + +static struct output *output_new(struct userdata *u, struct pa_sink *sink) { + struct output *o = NULL; + char t[256]; + assert(u && sink && u->sink); + + o = pa_xmalloc(sizeof(struct output)); + o->userdata = u; + + o->counter = 0; + o->memblockq = pa_memblockq_new(MEMBLOCKQ_MAXLENGTH, MEMBLOCKQ_MAXLENGTH, pa_frame_size(&u->sink->sample_spec), 0, 0, sink->core->memblock_stat); + + snprintf(t, sizeof(t), "%s: output #%u", u->sink->name, u->n_outputs+1); + if (!(o->sink_input = pa_sink_input_new(sink, t, &u->sink->sample_spec, 1))) + goto fail; + + o->sink_input->get_latency = sink_input_get_latency_cb; + o->sink_input->peek = sink_input_peek_cb; + o->sink_input->drop = sink_input_drop_cb; + o->sink_input->kill = sink_input_kill_cb; + o->sink_input->userdata = o; + o->sink_input->owner = u->module; + + PA_LLIST_PREPEND(struct output, u->outputs, o); + u->n_outputs++; + return o; + +fail: + + if (o) { + if (o->sink_input) + pa_sink_input_free(o->sink_input); + + if (o->memblockq) + pa_memblockq_free(o->memblockq); + + pa_xfree(o); + } + + return NULL; +} + +static void output_free(struct output *o) { + assert(o); + PA_LLIST_REMOVE(struct output, o->userdata->outputs, o); + o->userdata->n_outputs--; + pa_memblockq_free(o->memblockq); + pa_sink_input_free(o->sink_input); + pa_xfree(o); +} + +static void clear_up(struct userdata *u) { + struct output *o; + assert(u); + + if (u->time_event) { + u->core->mainloop->time_free(u->time_event); + u->time_event = NULL; + } + + while ((o = u->outputs)) + output_free(o); + + u->master = NULL; + + if (u->sink) { + pa_sink_free(u->sink); + u->sink = NULL; + } +} + +int pa__init(struct pa_core *c, struct pa_module*m) { + struct userdata *u; + struct pa_modargs *ma = NULL; + const char *master_name, *slaves; + struct pa_sink *master_sink; + char *n = NULL; + const char*split_state; + struct timeval tv; + assert(c && m); + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log(__FILE__": failed to parse module arguments\n"); + goto fail; + } + + u = pa_xmalloc(sizeof(struct userdata)); + m->userdata = u; + u->sink = NULL; + u->n_outputs = 0; + u->master = NULL; + u->module = m; + u->core = c; + u->time_event = NULL; + PA_LLIST_HEAD_INIT(struct output, u->outputs); + + if (!(master_name = pa_modargs_get_value(ma, "master", NULL)) || !(slaves = pa_modargs_get_value(ma, "slaves", NULL))) { + pa_log(__FILE__": no master or slave sinks specified\n"); + goto fail; + } + + if (!(master_sink = pa_namereg_get(c, master_name, PA_NAMEREG_SINK, 1))) { + pa_log(__FILE__": invalid master sink '%s'\n", master_name); + goto fail; + } + + if (!(u->sink = pa_sink_new(c, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &master_sink->sample_spec))) { + pa_log(__FILE__": failed to create sink\n"); + goto fail; + } + + pa_sink_set_owner(u->sink, m); + u->sink->description = pa_sprintf_malloc("Combined sink"); + u->sink->get_latency = sink_get_latency_cb; + u->sink->userdata = u; + + if (!(u->master = output_new(u, master_sink))) { + pa_log(__FILE__": failed to create master sink input on sink '%s'.\n", u->sink->name); + goto fail; + } + + split_state = NULL; + while ((n = pa_split(slaves, ",", &split_state))) { + struct pa_sink *slave_sink; + + if (!(slave_sink = pa_namereg_get(c, n, PA_NAMEREG_SINK, 1))) { + pa_log(__FILE__": invalid slave sink '%s'\n", n); + goto fail; + } + + pa_xfree(n); + + if (!output_new(u, slave_sink)) { + pa_log(__FILE__": failed to create slave sink input on sink '%s'.\n", slave_sink->name); + goto fail; + } + } + + if (u->n_outputs <= 1) + pa_log(__FILE__": WARNING: no slave sinks specified.\n"); + + gettimeofday(&tv, NULL); + tv.tv_sec += ADJUST_TIME; + u->time_event = c->mainloop->time_new(c->mainloop, &tv, time_callback, u); + + pa_modargs_free(ma); + return 0; + +fail: + pa_xfree(n); + + if (ma) + pa_modargs_free(ma); + + pa__done(c, m); + return -1; +} + +void pa__done(struct pa_core *c, struct pa_module*m) { + struct userdata *u; + assert(c && m); + + if (!(u = m->userdata)) + return; + + clear_up(u); + pa_xfree(u); +} + + diff --git a/polyp/module-sine.c b/polyp/module-sine.c index 98e0dc28..b537452a 100644 --- a/polyp/module-sine.c +++ b/polyp/module-sine.c @@ -134,7 +134,7 @@ int pa__init(struct pa_core *c, struct pa_module*m) { calc_sine(u->memblock->data, u->memblock->length, frequency); snprintf(t, sizeof(t), "Sine Generator at %u Hz", frequency); - if (!(u->sink_input = pa_sink_input_new(sink, t, &ss))) + if (!(u->sink_input = pa_sink_input_new(sink, t, &ss, 0))) goto fail; u->sink_input->peek = sink_input_peek; diff --git a/polyp/play-memchunk.c b/polyp/play-memchunk.c index e3f0c006..ace8ca69 100644 --- a/polyp/play-memchunk.c +++ b/polyp/play-memchunk.c @@ -85,7 +85,7 @@ int pa_play_memchunk(struct pa_sink *sink, const char *name, const struct pa_sam if (volume <= 0) return 0; - if (!(si = pa_sink_input_new(sink, name, ss))) + if (!(si = pa_sink_input_new(sink, name, ss, 0))) return -1; si->volume = volume; diff --git a/polyp/protocol-esound.c b/polyp/protocol-esound.c index ee64c484..103b17a3 100644 --- a/polyp/protocol-esound.c +++ b/polyp/protocol-esound.c @@ -298,7 +298,7 @@ static int esd_proto_stream_play(struct connection *c, esd_proto_t request, cons c->playback.fragment_size = l/10; assert(!c->sink_input); - c->sink_input = pa_sink_input_new(sink, name, &ss); + c->sink_input = pa_sink_input_new(sink, name, &ss, 0); assert(c->sink_input); c->sink_input->owner = c->protocol->module; diff --git a/polyp/protocol-native.c b/polyp/protocol-native.c index 2d26c2f5..c1b19760 100644 --- a/polyp/protocol-native.c +++ b/polyp/protocol-native.c @@ -269,7 +269,7 @@ static struct playback_stream* playback_stream_new(struct connection *c, struct struct pa_sink_input *sink_input; assert(c && sink && ss && name && maxlength); - if (!(sink_input = pa_sink_input_new(sink, name, ss))) + if (!(sink_input = pa_sink_input_new(sink, name, ss, 0))) return NULL; s = pa_xmalloc(sizeof(struct playback_stream)); diff --git a/polyp/protocol-simple.c b/polyp/protocol-simple.c index 00db0aa0..96444f82 100644 --- a/polyp/protocol-simple.c +++ b/polyp/protocol-simple.c @@ -304,7 +304,7 @@ static void on_connection(struct pa_socket_server*s, struct pa_iochannel *io, vo goto fail; } - if (!(c->sink_input = pa_sink_input_new(sink, c->client->name, &p->sample_spec))) { + if (!(c->sink_input = pa_sink_input_new(sink, c->client->name, &p->sample_spec, 0))) { pa_log(__FILE__": Failed to create sink input.\n"); goto fail; } diff --git a/polyp/resampler.c b/polyp/resampler.c index 173c0987..4742ce21 100644 --- a/polyp/resampler.c +++ b/polyp/resampler.c @@ -67,12 +67,9 @@ struct pa_resampler* pa_resampler_new(const struct pa_sample_spec *a, const stru r->i_buf = r->o_buf = NULL; r->i_alloc = r->o_alloc = 0; - if (a->rate != b->rate) { - r->src_state = src_new(SRC_SINC_FASTEST, r->channels, &err); - if (err != 0 || !r->src_state) - goto fail; - } else - r->src_state = NULL; + r->src_state = src_new(SRC_SINC_FASTEST, r->channels, &err); + if (err != 0 || !r->src_state) + goto fail; r->i_ss = *a; r->o_ss = *b; @@ -196,3 +193,12 @@ void pa_resampler_run(struct pa_resampler *r, const struct pa_memchunk *in, stru out->memblock = NULL; } } + +void pa_resampler_set_input_rate(struct pa_resampler *r, uint32_t rate) { + int ret; + assert(r); + + r->i_ss.rate = rate; + ret = src_set_ratio(r->src_state, (double) r->o_ss.rate / r->i_ss.rate); + assert(ret == 0); +} diff --git a/polyp/resampler.h b/polyp/resampler.h index e23d145d..a6ef30df 100644 --- a/polyp/resampler.h +++ b/polyp/resampler.h @@ -34,4 +34,6 @@ void pa_resampler_free(struct pa_resampler *r); size_t pa_resampler_request(struct pa_resampler *r, size_t out_length); void pa_resampler_run(struct pa_resampler *r, const struct pa_memchunk *in, struct pa_memchunk *out); +void pa_resampler_set_input_rate(struct pa_resampler *r, uint32_t rate); + #endif diff --git a/polyp/sink-input.c b/polyp/sink-input.c index 7763f261..0d59062d 100644 --- a/polyp/sink-input.c +++ b/polyp/sink-input.c @@ -36,7 +36,7 @@ #define CONVERT_BUFFER_LENGTH 4096 -struct pa_sink_input* pa_sink_input_new(struct pa_sink *s, const char *name, const struct pa_sample_spec *spec) { +struct pa_sink_input* pa_sink_input_new(struct pa_sink *s, const char *name, const struct pa_sample_spec *spec, int variable_rate) { struct pa_sink_input *i; struct pa_resampler *resampler = NULL; int r; @@ -48,7 +48,7 @@ struct pa_sink_input* pa_sink_input_new(struct pa_sink *s, const char *name, con return NULL; } - if (!pa_sample_spec_equal(spec, &s->sample_spec)) + if (variable_rate || !pa_sample_spec_equal(spec, &s->sample_spec)) if (!(resampler = pa_resampler_new(spec, &s->sample_spec, s->core->memblock_stat))) return NULL; @@ -199,3 +199,13 @@ void pa_sink_input_cork(struct pa_sink_input *i, int b) { if (n) pa_sink_notify(i->sink); } + +void pa_sink_input_set_rate(struct pa_sink_input *i, uint32_t rate) { + assert(i && i->resampler); + + if (i->sample_spec.rate == rate) + return; + + i->sample_spec.rate = rate; + pa_resampler_set_input_rate(i->resampler, rate); +} diff --git a/polyp/sink-input.h b/polyp/sink-input.h index e2478ed6..e5b06387 100644 --- a/polyp/sink-input.h +++ b/polyp/sink-input.h @@ -54,7 +54,7 @@ struct pa_sink_input { struct pa_resampler *resampler; }; -struct pa_sink_input* pa_sink_input_new(struct pa_sink *s, const char *name, const struct pa_sample_spec *spec); +struct pa_sink_input* pa_sink_input_new(struct pa_sink *s, const char *name, const struct pa_sample_spec *spec, int variable_rate); void pa_sink_input_free(struct pa_sink_input* i); /* Code that didn't create the input stream should call this function to @@ -70,4 +70,6 @@ void pa_sink_input_set_volume(struct pa_sink_input *i, pa_volume_t volume); void pa_sink_input_cork(struct pa_sink_input *i, int b); +void pa_sink_input_set_rate(struct pa_sink_input *i, uint32_t rate); + #endif diff --git a/polyp/sound-file-stream.c b/polyp/sound-file-stream.c index c667f5ff..e77ff119 100644 --- a/polyp/sound-file-stream.c +++ b/polyp/sound-file-stream.c @@ -142,7 +142,7 @@ int pa_play_file(struct pa_sink *sink, const char *fname, pa_volume_t volume) { goto fail; } - if (!(u->sink_input = pa_sink_input_new(sink, fname, &ss))) + if (!(u->sink_input = pa_sink_input_new(sink, fname, &ss, 0))) goto fail; u->sink_input->volume = volume; diff --git a/polyp/util.c b/polyp/util.c index 039ec264..3ab6d51a 100644 --- a/polyp/util.c +++ b/polyp/util.c @@ -373,3 +373,19 @@ int pa_parse_boolean(const char *v) { return -1; } + +char *pa_split(const char *c, const char *delimiter, const char**state) { + const char *current = *state ? *state : c; + size_t l; + + if (!*current) + return NULL; + + l = strcspn(current, delimiter); + *state = current+l; + + if (**state) + *state++; + + return pa_xstrndup(current, l); +} diff --git a/polyp/util.h b/polyp/util.h index 42e0b22b..eae98e6e 100644 --- a/polyp/util.h +++ b/polyp/util.h @@ -59,4 +59,6 @@ int pa_fd_set_cloexec(int fd, int b); int pa_parse_boolean(const char *s); +char *pa_split(const char *c, const char*delimiters, const char **state); + #endif -- cgit