From 450ad85b35bd600ed020f7ec119d51e7e7bc01a4 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 27 Sep 2004 21:05:55 +0000 Subject: try to use file sample type for cache entries and play-file playback allow paplay to use STDIN add new module: module-match git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@244 fefdeb5f-60dc-0310-8127-8f9354f1896f --- polyp/Makefile.am | 7 +- polyp/module-match.c | 217 ++++++++++++++++++++++++++++++++++++++++++++++ polyp/paplay.c | 26 +++--- polyp/scache.c | 9 +- polyp/sound-file-stream.c | 19 +++- polyp/sound-file.c | 25 ++++-- polyp/util.c | 7 ++ polyp/util.h | 2 + 8 files changed, 292 insertions(+), 20 deletions(-) create mode 100644 polyp/module-match.c (limited to 'polyp') diff --git a/polyp/Makefile.am b/polyp/Makefile.am index 2d14b420..d73c27bc 100644 --- a/polyp/Makefile.am +++ b/polyp/Makefile.am @@ -105,7 +105,8 @@ modlib_LTLIBRARIES= \ module-sine.la \ module-combine.la \ module-esound-compat-spawnfd.la \ - module-esound-compat-spawnpid.la + module-esound-compat-spawnpid.la \ + module-match.la lib_LTLIBRARIES= \ libpolyp-@PA_MAJORMINOR@.la \ @@ -305,6 +306,10 @@ module_combine_la_SOURCES = module-combine.c module_combine_la_LDFLAGS = -module -avoid-version module_combine_la_LIBADD = $(AM_LIBADD) +module_match_la_SOURCES = module-match.c +module_match_la_LDFLAGS = -module -avoid-version +module_match_la_LIBADD = $(AM_LIBADD) + module_esound_compat_spawnfd_la_SOURCES = module-esound-compat-spawnfd.c module_esound_compat_spawnfd_la_LDFLAGS = -module -avoid-version module_esound_compat_spawnfd_la_LIBADD = $(AM_LIBADD) diff --git a/polyp/module-match.c b/polyp/module-match.c new file mode 100644 index 00000000..964ff2fd --- /dev/null +++ b/polyp/module-match.c @@ -0,0 +1,217 @@ +/* $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 +#include +#include +#include +#include +#include + +#include "module.h" +#include "util.h" +#include "modargs.h" +#include "log.h" +#include "subscribe.h" +#include "xmalloc.h" +#include "sink-input.h" + +PA_MODULE_AUTHOR("Lennart Poettering") +PA_MODULE_DESCRIPTION("Sink input matching module") +PA_MODULE_USAGE("table=") +PA_MODULE_VERSION(PACKAGE_VERSION) + +#define WHITESPACE "\n\r \t" + +static const char* const valid_modargs[] = { + "table", + NULL, +}; + +struct rule { + regex_t regex; + pa_volume_t volume; + struct rule *next; +}; + +struct userdata { + struct rule *rules; + struct pa_subscription *subscription; +}; + +static int load_rules(struct userdata *u, const char *filename) { + FILE *f; + int n = 0; + int ret = -1; + struct rule *end = NULL; + + if (!(f = fopen(filename, "r"))) { + pa_log(__FILE__": failed to open file '%s': %s\n", filename, strerror(errno)); + goto finish; + } + + while (!feof(f)) { + char *d, *v, *e = NULL; + pa_volume_t volume; + regex_t regex; + char ln[256]; + struct rule *rule; + + if (!fgets(ln, sizeof(ln), f)) + break; + + n++; + + pa_strip_nl(ln); + + if (ln[0] == '#' || !*ln ) + continue; + + d = ln+strcspn(ln, WHITESPACE); + v = d+strspn(d, WHITESPACE); + + + if (!*v) { + pa_log(__FILE__ ": [%s:%u] failed to parse line - too few words\n", filename, n); + goto finish; + } + + *d = 0; + + volume = (pa_volume_t) strtol(v, &e, 0); + + if (!e || *e) { + pa_log(__FILE__": [%s:%u] failed to parse volume\n", filename, n); + goto finish; + } + + if (regcomp(®ex, ln, REG_EXTENDED|REG_NOSUB) != 0) { + pa_log(__FILE__": [%s:%u] invalid regular expression\n", filename, n); + goto finish; + } + + rule = pa_xmalloc(sizeof(struct rule)); + rule->regex = regex; + rule->volume = volume; + rule->next = NULL; + + if (end) + end->next = rule; + else + u->rules = rule; + end = rule; + + *d = 0; + } + + ret = 0; + +finish: + if (f) + fclose(f); + + return ret; +} + +static void callback(struct pa_core *c, enum pa_subscription_event_type t, uint32_t index, void *userdata) { + struct userdata *u = userdata; + struct pa_sink_input *si; + struct rule *r; + assert(c && u); + + if (t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW)) + return; + + if (!(si = pa_idxset_get_by_index(c->sink_inputs, index))) { + pa_log(__FILE__": WARNING: failed to get sink input\n"); + return; + } + + if (!si->name) + return; + + for (r = u->rules; r; r = r->next) { + if (!regexec(&r->regex, si->name, 0, NULL, 0)) { + pa_log(__FILE__": changing volume of sink input '%s' to 0x%03x\n", si->name, r->volume); + pa_sink_input_set_volume(si, r->volume); + } + } +} + +int pa__init(struct pa_core *c, struct pa_module*m) { + struct pa_modargs *ma = NULL; + int ret = -1; + const char *table_file; + struct userdata *u; + assert(c && m); + + if (!(ma = pa_modargs_new(m->argument, valid_modargs)) || + !(table_file = pa_modargs_get_value(ma, "table", NULL))) { + pa_log(__FILE__": Failed to parse module arguments\n"); + goto finish; + } + + u = pa_xmalloc(sizeof(struct userdata)); + u->rules = NULL; + u->subscription = NULL; + + if (load_rules(u, table_file) < 0) + goto finish; + + u->subscription = pa_subscription_new(c, PA_SUBSCRIPTION_MASK_SINK_INPUT, callback, u); + + ret = 0; + +finish: + if (ma) + pa_modargs_free(ma); + + return ret; +} + +void pa__done(struct pa_core *c, struct pa_module*m) { + struct userdata* u; + struct rule *r, *n; + assert(c && m); + + if (!(u = m->userdata)) + return; + + if (u->subscription) + pa_subscription_free(u->subscription); + + for (r = u->rules; r; r = n) { + n = r->next; + + regfree(&r->regex); + pa_xfree(r); + } + + pa_xfree(u); +} + + diff --git a/polyp/paplay.c b/polyp/paplay.c index cc973a7d..89358a51 100644 --- a/polyp/paplay.c +++ b/polyp/paplay.c @@ -192,7 +192,7 @@ static void exit_signal_callback(struct pa_mainloop_api*m, struct pa_signal_even static void help(const char *argv0) { - printf("%s [options] FILE\n\n" + printf("%s [options] [FILE]\n\n" " -h, --help Show this help\n" " --version Show version\n\n" " -v, --verbose Enable verbose operations\n\n" @@ -213,7 +213,7 @@ enum { int main(int argc, char *argv[]) { struct pa_mainloop* m = NULL; int ret = 1, r, c; - char *bn, *server = NULL; + char *bn, *server = NULL, *filename; SF_INFO sfinfo; static const struct option long_options[] = { @@ -281,24 +281,28 @@ int main(int argc, char *argv[]) { } } - if (optind >= argc) { - fprintf(stderr, "Missing file name.\n"); - goto quit; - } + filename = optind < argc ? argv[optind] : "STDIN"; + + if (!client_name) client_name = strdup(bn); if (!stream_name) - stream_name = strdup(argv[optind]); + stream_name = strdup(filename); memset(&sfinfo, 0, sizeof(sfinfo)); - - if (!(sndfile = sf_open(argv[optind], SFM_READ, &sfinfo))) { - fprintf(stderr, "Faile to open file '%s'\n", argv[optind]); + + if (optind < argc) + sndfile = sf_open(filename, SFM_READ, &sfinfo); + else + sndfile = sf_open_fd(STDIN_FILENO, SFM_READ, &sfinfo, 0); + + if (!sndfile) { + fprintf(stderr, "Failed to open file '%s'\n", filename); goto quit; } - + sample_spec.rate = sfinfo.samplerate; sample_spec.channels = sfinfo.channels; diff --git a/polyp/scache.c b/polyp/scache.c index b17f3242..32977854 100644 --- a/polyp/scache.c +++ b/polyp/scache.c @@ -37,6 +37,7 @@ #include "subscribe.h" #include "namereg.h" #include "sound-file.h" +#include "util.h" #define UNLOAD_POLL_TIME 2 @@ -199,6 +200,7 @@ void pa_scache_free(struct pa_core *c) { int pa_scache_play_item(struct pa_core *c, const char *name, struct pa_sink *sink, uint32_t volume) { struct pa_scache_entry *e; + char *t; assert(c && name && sink); if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 1))) @@ -214,8 +216,13 @@ int pa_scache_play_item(struct pa_core *c, const char *name, struct pa_sink *sin if (!e->memchunk.memblock) return -1; - if (pa_play_memchunk(sink, name, &e->sample_spec, &e->memchunk, pa_volume_multiply(volume, e->volume)) < 0) + t = pa_sprintf_malloc("sample:%s", name); + if (pa_play_memchunk(sink, t, &e->sample_spec, &e->memchunk, pa_volume_multiply(volume, e->volume)) < 0) { + free(t); return -1; + } + + free(t); if (e->lazy) time(&e->last_used_time); diff --git a/polyp/sound-file-stream.c b/polyp/sound-file-stream.c index b77d6d61..347d45a7 100644 --- a/polyp/sound-file-stream.c +++ b/polyp/sound-file-stream.c @@ -41,6 +41,7 @@ struct userdata { SNDFILE *sndfile; struct pa_sink_input *sink_input; struct pa_memchunk memchunk; + sf_count_t (*readf_function)(SNDFILE *sndfile, void *ptr, sf_count_t frames); }; static void free_userdata(struct userdata *u) { @@ -78,7 +79,7 @@ static int sink_input_peek(struct pa_sink_input *i, struct pa_memchunk *chunk) { u->memchunk.memblock = pa_memblock_new(BUF_SIZE, i->sink->core->memblock_stat); u->memchunk.index = 0; - samples = sf_readf_float(u->sndfile, u->memchunk.memblock->data, samples); + samples = u->readf_function(u->sndfile, u->memchunk.memblock->data, samples); u->memchunk.length = samples*fs; if (!u->memchunk.length) { @@ -136,7 +137,21 @@ int pa_play_file(struct pa_sink *sink, const char *fname, pa_volume_t volume) { goto fail; } - ss.format = PA_SAMPLE_FLOAT32; + switch (sfinfo.format & 0xFF) { + case SF_FORMAT_PCM_16: + case SF_FORMAT_PCM_U8: + case SF_FORMAT_ULAW: + case SF_FORMAT_ALAW: + ss.format = PA_SAMPLE_S16NE; + u->readf_function = (sf_count_t (*)(SNDFILE *sndfile, void *ptr, sf_count_t frames)) sf_readf_short; + break; + case SF_FORMAT_FLOAT: + default: + ss.format = PA_SAMPLE_FLOAT32NE; + u->readf_function = (sf_count_t (*)(SNDFILE *sndfile, void *ptr, sf_count_t frames)) sf_readf_float; + break; + } + ss.rate = sfinfo.samplerate; ss.channels = sfinfo.channels; diff --git a/polyp/sound-file.c b/polyp/sound-file.c index d1f7e0f7..c63aa628 100644 --- a/polyp/sound-file.c +++ b/polyp/sound-file.c @@ -39,19 +39,34 @@ int pa_sound_file_load(const char *fname, struct pa_sample_spec *ss, struct pa_m SF_INFO sfinfo; int ret = -1; size_t l; + sf_count_t (*readf_function)(SNDFILE *sndfile, void *ptr, sf_count_t frames); assert(fname && ss && chunk); - memset(&sfinfo, 0, sizeof(sfinfo)); - chunk->memblock = NULL; chunk->index = chunk->length = 0; - + + memset(&sfinfo, 0, sizeof(sfinfo)); + if (!(sf = sf_open(fname, SFM_READ, &sfinfo))) { pa_log(__FILE__": Failed to open file %s\n", fname); goto finish; } - ss->format = PA_SAMPLE_FLOAT32; + switch (sfinfo.format & 0xFF) { + case SF_FORMAT_PCM_16: + case SF_FORMAT_PCM_U8: + case SF_FORMAT_ULAW: + case SF_FORMAT_ALAW: + ss->format = PA_SAMPLE_S16NE; + readf_function = (sf_count_t (*)(SNDFILE *sndfile, void *ptr, sf_count_t frames)) sf_readf_short; + break; + case SF_FORMAT_FLOAT: + default: + ss->format = PA_SAMPLE_FLOAT32NE; + readf_function = (sf_count_t (*)(SNDFILE *sndfile, void *ptr, sf_count_t frames)) sf_readf_float; + break; + } + ss->rate = sfinfo.samplerate; ss->channels = sfinfo.channels; @@ -70,7 +85,7 @@ int pa_sound_file_load(const char *fname, struct pa_sample_spec *ss, struct pa_m chunk->index = 0; chunk->length = l; - if (sf_readf_float(sf, chunk->memblock->data, sfinfo.frames) != sfinfo.frames) { + if (readf_function(sf, chunk->memblock->data, sfinfo.frames) != sfinfo.frames) { pa_log(__FILE__": Premature file end\n"); goto finish; } diff --git a/polyp/util.c b/polyp/util.c index 166a9a45..fa33ffff 100644 --- a/polyp/util.c +++ b/polyp/util.c @@ -518,3 +518,10 @@ int pa_lock_file(int fd, int b) { return 0; } + +char* pa_strip_nl(char *s) { + assert(s); + + s[strcspn(s, "\r\n")] = 0; + return s; +} diff --git a/polyp/util.h b/polyp/util.h index 571bb29b..e1910c83 100644 --- a/polyp/util.h +++ b/polyp/util.h @@ -62,6 +62,8 @@ int pa_parse_boolean(const char *s); char *pa_split(const char *c, const char*delimiters, const char **state); char *pa_split_spaces(const char *c, const char **state); +char *pa_strip_nl(char *s); + const char *pa_strsignal(int sig); int pa_parse_resample_method(const char *string); -- cgit