From 188c91b514804d48de6257e72640ef8ff8e8e830 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Marc-Andr=C3=A9=20Lureau?= Date: Sat, 1 Oct 2011 12:16:35 +0100 Subject: [PATCH] stream-restore: Support a simple fallback volume table The purpose of this patch is to make it possible to configure stream volumes before pulseaudio is run for the first time. This is useful, for example, in embedded products where the default volumes have to be sensible already in the first boot. --- src/modules/module-stream-restore.c | 97 ++++++++++++++++++++++++++++++++++++- 1 file changed, 95 insertions(+), 2 deletions(-) diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c index 4d0686f..fe7708b 100644 --- a/src/modules/module-stream-restore.c +++ b/src/modules/module-stream-restore.c @@ -68,17 +68,24 @@ PA_MODULE_USAGE( "restore_volume= " "restore_muted= " "on_hotplug= " - "on_rescue="); + "on_rescue= " + "fallback_table="); #define SAVE_INTERVAL (10 * PA_USEC_PER_SEC) #define IDENTIFICATION_PROPERTY "module-stream-restore.id" +#define DEFAULT_FALLBACK_FILE PA_DEFAULT_CONFIG_DIR"/stream-restore.table" +#define DEFAULT_FALLBACK_FILE_USER "stream-restore.table" + +#define WHITESPACE "\n\r \t" + static const char* const valid_modargs[] = { "restore_device", "restore_volume", "restore_muted", "on_hotplug", "on_rescue", + "fallback_table", NULL }; @@ -1800,7 +1807,88 @@ static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *sourc return PA_HOOK_OK; } -#define EXT_VERSION 1 +static int fill_db(struct userdata *u, const char *filename) { + FILE *f; + int n = 0; + int ret = -1; + char *fn = NULL; + + pa_assert(u); + + if (filename) + f = fopen(fn = pa_xstrdup(filename), "r"); + else + f = pa_open_config_file(DEFAULT_FALLBACK_FILE, DEFAULT_FALLBACK_FILE_USER, NULL, &fn); + + if (!f) { + if (filename) + pa_log("Failed to open %s: %s", filename, pa_cstrerror(errno)); + else + ret = 0; + + goto finish; + } + + while (!feof(f)) { + char ln[256]; + char *d, *v; + double db; + + if (!fgets(ln, sizeof(ln), f)) + break; + + n++; + + pa_strip_nl(ln); + + if (!*ln || ln[0] == '#' || ln[0] == ';') + continue; + + d = ln+strcspn(ln, WHITESPACE); + v = d+strspn(d, WHITESPACE); + + if (!*v) { + pa_log("[%s:%u] failed to parse line - too few words", fn, n); + goto finish; + } + + *d = 0; + if (pa_atod(v, &db) >= 0) { + if (db <= 0.0) { + pa_datum key, data; + struct entry e; + + pa_zero(e); + e.version = ENTRY_VERSION; + e.volume_valid = TRUE; + pa_cvolume_set(&e.volume, 1, pa_sw_volume_from_dB(db)); + pa_channel_map_init_mono(&e.channel_map); + + key.data = (void *) ln; + key.size = strlen(ln); + + data.data = (void *) &e; + data.size = sizeof(e); + + if (pa_database_set(u->database, &key, &data, FALSE) == 0) + pa_log_debug("Setting %s to %0.2f dB.", ln, db); + } else + pa_log_warn("[%s:%u] Positive dB values are not allowed, not setting entry %s.", fn, n, ln); + } else + pa_log_warn("[%s:%u] Couldn't parse '%s' as a double, not setting entry %s.", fn, n, v, ln); + } + + trigger_save(u); + ret = 0; + +finish: + if (f) + fclose(f); + + pa_xfree(fn); + + return ret; +} static void entry_apply(struct userdata *u, const char *name, struct entry *e) { pa_sink_input *si; @@ -1942,6 +2030,8 @@ PA_GCC_UNUSED static void stream_restore_dump_database(struct userdata *u) { } #endif +#define EXT_VERSION 1 + static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) { struct userdata *u; uint32_t command; @@ -2280,6 +2370,9 @@ int pa__init(pa_module*m) { pa_log_info("Successfully opened database file '%s'.", fname); pa_xfree(fname); + if (fill_db(u, pa_modargs_get_value(ma, "fallback_table", NULL)) < 0) + goto fail; + #ifdef HAVE_DBUS u->dbus_protocol = pa_dbus_protocol_get(u->core); u->dbus_entries = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); -- 2.7.4