+static int decibel_fix_parse_db_values(pa_config_parser_state *state) {
+ pa_alsa_profile_set *ps;
+ pa_alsa_decibel_fix *db_fix;
+ char **items;
+ char *item;
+ long *db_values;
+ unsigned n = 8; /* Current size of the db_values table. */
+ unsigned min_step = 0;
+ unsigned max_step = 0;
+ unsigned i = 0; /* Index to the items table. */
+ unsigned prev_step = 0;
+ double prev_db = 0;
+
+ pa_assert(state);
+
+ ps = state->userdata;
+
+ if (!(db_fix = decibel_fix_get(ps, state->section))) {
+ pa_log("[%s:%u] %s invalid in section %s", state->filename, state->lineno, state->lvalue, state->section);
+ return -1;
+ }
+
+ if (!(items = pa_split_spaces_strv(state->rvalue))) {
+ pa_log("[%s:%u] Value missing", state->filename, state->lineno);
+ return -1;
+ }
+
+ db_values = pa_xnew(long, n);
+
+ while ((item = items[i++])) {
+ char *s = item; /* Step value string. */
+ char *d = item; /* dB value string. */
+ uint32_t step;
+ double db;
+
+ /* Move d forward until it points to a colon or to the end of the item. */
+ for (; *d && *d != ':'; ++d);
+
+ if (d == s) {
+ /* item started with colon. */
+ pa_log("[%s:%u] No step value found in %s", state->filename, state->lineno, item);
+ goto fail;
+ }
+
+ if (!*d || !*(d + 1)) {
+ /* No colon found, or it was the last character in item. */
+ pa_log("[%s:%u] No dB value found in %s", state->filename, state->lineno, item);
+ goto fail;
+ }
+
+ /* pa_atou() needs a null-terminating string. Let's replace the colon
+ * with a zero byte. */
+ *d++ = '\0';
+
+ if (pa_atou(s, &step) < 0) {
+ pa_log("[%s:%u] Invalid step value: %s", state->filename, state->lineno, s);
+ goto fail;
+ }
+
+ if (pa_atod(d, &db) < 0) {
+ pa_log("[%s:%u] Invalid dB value: %s", state->filename, state->lineno, d);
+ goto fail;
+ }
+
+ if (step <= prev_step && i != 1) {
+ pa_log("[%s:%u] Step value %u not greater than the previous value %u", state->filename, state->lineno, step, prev_step);
+ goto fail;
+ }
+
+ if (db < prev_db && i != 1) {
+ pa_log("[%s:%u] Decibel value %0.2f less than the previous value %0.2f", state->filename, state->lineno, db, prev_db);
+ goto fail;
+ }
+
+ if (i == 1) {
+ min_step = step;
+ db_values[0] = (long) (db * 100.0);
+ prev_step = step;
+ prev_db = db;
+ } else {
+ /* Interpolate linearly. */
+ double db_increment = (db - prev_db) / (step - prev_step);
+
+ for (; prev_step < step; ++prev_step, prev_db += db_increment) {
+
+ /* Reallocate the db_values table if it's about to overflow. */
+ if (prev_step + 1 - min_step == n) {
+ n *= 2;
+ db_values = pa_xrenew(long, db_values, n);
+ }
+
+ db_values[prev_step + 1 - min_step] = (long) ((prev_db + db_increment) * 100.0);
+ }
+ }
+
+ max_step = step;
+ }
+
+ db_fix->min_step = min_step;
+ db_fix->max_step = max_step;
+ pa_xfree(db_fix->db_values);
+ db_fix->db_values = db_values;
+
+ pa_xstrfreev(items);
+
+ return 0;
+
+fail:
+ pa_xstrfreev(items);
+ pa_xfree(db_values);
+
+ return -1;
+}
+
+static void mapping_paths_probe(pa_alsa_mapping *m, pa_alsa_profile *profile,
+ pa_alsa_direction_t direction, pa_hashmap *used_paths) {
+
+ pa_alsa_path *p;
+ void *state;
+ snd_pcm_t *pcm_handle;
+ pa_alsa_path_set *ps;
+ snd_mixer_t *mixer_handle;
+ snd_hctl_t *hctl_handle;
+
+ if (direction == PA_ALSA_DIRECTION_OUTPUT) {
+ if (m->output_path_set)
+ return; /* Already probed */
+ m->output_path_set = ps = pa_alsa_path_set_new(m, direction, NULL); /* FIXME: Handle paths_dir */
+ pcm_handle = m->output_pcm;
+ } else {
+ if (m->input_path_set)
+ return; /* Already probed */
+ m->input_path_set = ps = pa_alsa_path_set_new(m, direction, NULL); /* FIXME: Handle paths_dir */
+ pcm_handle = m->input_pcm;
+ }
+
+ if (!ps)
+ return; /* No paths */
+
+ pa_assert(pcm_handle);
+
+ mixer_handle = pa_alsa_open_mixer_for_pcm(pcm_handle, NULL, &hctl_handle);
+ if (!mixer_handle) {
+ /* Cannot open mixer, remove all entries */
+ pa_hashmap_remove_all(ps->paths);
+ return;
+ }
+
+ PA_HASHMAP_FOREACH(p, ps->paths, state) {
+ if (pa_alsa_path_probe(p, mixer_handle, hctl_handle, m->profile_set->ignore_dB) < 0) {
+ pa_hashmap_remove(ps->paths, p);
+ }
+ }
+
+ path_set_condense(ps, mixer_handle);
+ path_set_make_path_descriptions_unique(ps);
+
+ if (mixer_handle)
+ snd_mixer_close(mixer_handle);
+
+ PA_HASHMAP_FOREACH(p, ps->paths, state)
+ pa_hashmap_put(used_paths, p, p);
+
+ pa_log_debug("Available mixer paths (after tidying):");
+ pa_alsa_path_set_dump(ps);
+}
+