stream-restore: Support a simple fallback volume table
authorMarc-André Lureau <marcandre.lureau@gmail.com>
Sat, 1 Oct 2011 11:16:35 +0000 (12:16 +0100)
committerColin Guthrie <colin@mageia.org>
Sat, 1 Oct 2011 12:25:16 +0000 (13:25 +0100)
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

index 4d0686f..fe7708b 100644 (file)
@@ -68,17 +68,24 @@ PA_MODULE_USAGE(
         "restore_volume=<Save/restore volumes?> "
         "restore_muted=<Save/restore muted states?> "
         "on_hotplug=<When new device becomes available, recheck streams?> "
-        "on_rescue=<When device becomes unavailable, recheck streams?>");
+        "on_rescue=<When device becomes unavailable, recheck streams?> "
+        "fallback_table=<filename>");
 
 #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);