diff options
Diffstat (limited to 'src/modules/alsa')
21 files changed, 1376 insertions, 225 deletions
diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c index 946cbe24..3eef5f9c 100644 --- a/src/modules/alsa/alsa-mixer.c +++ b/src/modules/alsa/alsa-mixer.c @@ -491,6 +491,15 @@ static void option_free(pa_alsa_option *o) { pa_xfree(o); } +static void decibel_fix_free(pa_alsa_decibel_fix *db_fix) { + pa_assert(db_fix); + + pa_xfree(db_fix->name); + pa_xfree(db_fix->db_values); + + pa_xfree(db_fix); +} + static void element_free(pa_alsa_element *e) { pa_alsa_option *o; pa_assert(e); @@ -500,6 +509,9 @@ static void element_free(pa_alsa_element *e) { option_free(o); } + if (e->db_fix) + decibel_fix_free(e->db_fix); + pa_xfree(e->alsa_name); pa_xfree(e); } @@ -593,14 +605,60 @@ static int element_get_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_chann long value = 0; if (e->direction == PA_ALSA_DIRECTION_OUTPUT) { - if (snd_mixer_selem_has_playback_channel(me, c)) - r = snd_mixer_selem_get_playback_dB(me, c, &value); - else + if (snd_mixer_selem_has_playback_channel(me, c)) { + if (e->db_fix) { + if ((r = snd_mixer_selem_get_playback_volume(me, c, &value)) >= 0) { + /* If the channel volume is outside the limits set + * by the dB fix, we clamp the hw volume to be + * within the limits. */ + if (value < e->db_fix->min_step) { + value = e->db_fix->min_step; + snd_mixer_selem_set_playback_volume(me, c, value); + pa_log_debug("Playback volume for element %s channel %i was below the dB fix limit. " + "Volume reset to %0.2f dB.", e->alsa_name, c, + e->db_fix->db_values[value - e->db_fix->min_step] / 100.0); + } else if (value > e->db_fix->max_step) { + value = e->db_fix->max_step; + snd_mixer_selem_set_playback_volume(me, c, value); + pa_log_debug("Playback volume for element %s channel %i was over the dB fix limit. " + "Volume reset to %0.2f dB.", e->alsa_name, c, + e->db_fix->db_values[value - e->db_fix->min_step] / 100.0); + } + + /* Volume step -> dB value conversion. */ + value = e->db_fix->db_values[value - e->db_fix->min_step]; + } + } else + r = snd_mixer_selem_get_playback_dB(me, c, &value); + } else r = -1; } else { - if (snd_mixer_selem_has_capture_channel(me, c)) - r = snd_mixer_selem_get_capture_dB(me, c, &value); - else + if (snd_mixer_selem_has_capture_channel(me, c)) { + if (e->db_fix) { + if ((r = snd_mixer_selem_get_capture_volume(me, c, &value)) >= 0) { + /* If the channel volume is outside the limits set + * by the dB fix, we clamp the hw volume to be + * within the limits. */ + if (value < e->db_fix->min_step) { + value = e->db_fix->min_step; + snd_mixer_selem_set_capture_volume(me, c, value); + pa_log_debug("Capture volume for element %s channel %i was below the dB fix limit. " + "Volume reset to %0.2f dB.", e->alsa_name, c, + e->db_fix->db_values[value - e->db_fix->min_step] / 100.0); + } else if (value > e->db_fix->max_step) { + value = e->db_fix->max_step; + snd_mixer_selem_set_capture_volume(me, c, value); + pa_log_debug("Capture volume for element %s channel %i was over the dB fix limit. " + "Volume reset to %0.2f dB.", e->alsa_name, c, + e->db_fix->db_values[value - e->db_fix->min_step] / 100.0); + } + + /* Volume step -> dB value conversion. */ + value = e->db_fix->db_values[value - e->db_fix->min_step]; + } + } else + r = snd_mixer_selem_get_capture_dB(me, c, &value); + } else r = -1; } @@ -760,6 +818,37 @@ int pa_alsa_path_get_mute(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t *muted) { return 0; } +/* Finds the closest item in db_fix->db_values and returns the corresponding + * step. *db_value is replaced with the value from the db_values table. + * Rounding is done based on the rounding parameter: -1 means rounding down and + * +1 means rounding up. */ +static long decibel_fix_get_step(pa_alsa_decibel_fix *db_fix, long *db_value, int rounding) { + unsigned i = 0; + unsigned max_i = 0; + + pa_assert(db_fix); + pa_assert(db_value); + pa_assert(rounding != 0); + + max_i = db_fix->max_step - db_fix->min_step; + + if (rounding > 0) { + for (i = 0; i < max_i; i++) { + if (db_fix->db_values[i] >= *db_value) + break; + } + } else { + for (i = 0; i < max_i; i++) { + if (db_fix->db_values[i + 1] > *db_value) + break; + } + } + + *db_value = db_fix->db_values[i]; + + return i + db_fix->min_step; +} + static int element_set_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v, pa_bool_t write_to_hw) { snd_mixer_selem_id_t *sid; @@ -804,31 +893,55 @@ static int element_set_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_chann if (e->has_dB) { long value = to_alsa_dB(f); + int rounding = value > 0 ? -1 : +1; + + if (e->volume_limit >= 0 && value > (e->max_dB * 100)) + value = e->max_dB * 100; if (e->direction == PA_ALSA_DIRECTION_OUTPUT) { - /* If we call set_play_volume() without checking first - * if the channel is available, ALSA behaves ver + /* If we call set_playback_volume() without checking first + * if the channel is available, ALSA behaves very * strangely and doesn't fail the call */ if (snd_mixer_selem_has_playback_channel(me, c)) { - if (write_to_hw) { - if ((r = snd_mixer_selem_set_playback_dB(me, c, value, +1)) >= 0) - r = snd_mixer_selem_get_playback_dB(me, c, &value); + if (e->db_fix) { + if (write_to_hw) + r = snd_mixer_selem_set_playback_volume(me, c, decibel_fix_get_step(e->db_fix, &value, rounding)); + else { + decibel_fix_get_step(e->db_fix, &value, rounding); + r = 0; + } + } else { - long alsa_val; - if ((r = snd_mixer_selem_ask_playback_dB_vol(me, value, +1, &alsa_val)) >= 0) - r = snd_mixer_selem_ask_playback_vol_dB(me, alsa_val, &value); + if (write_to_hw) { + if ((r = snd_mixer_selem_set_playback_dB(me, c, value, rounding)) >= 0) + r = snd_mixer_selem_get_playback_dB(me, c, &value); + } else { + long alsa_val; + if ((r = snd_mixer_selem_ask_playback_dB_vol(me, value, rounding, &alsa_val)) >= 0) + r = snd_mixer_selem_ask_playback_vol_dB(me, alsa_val, &value); + } } } else r = -1; } else { if (snd_mixer_selem_has_capture_channel(me, c)) { - if (write_to_hw) { - if ((r = snd_mixer_selem_set_capture_dB(me, c, value, +1)) >= 0) - r = snd_mixer_selem_get_capture_dB(me, c, &value); + if (e->db_fix) { + if (write_to_hw) + r = snd_mixer_selem_set_capture_volume(me, c, decibel_fix_get_step(e->db_fix, &value, rounding)); + else { + decibel_fix_get_step(e->db_fix, &value, rounding); + r = 0; + } + } else { - long alsa_val; - if ((r = snd_mixer_selem_ask_capture_dB_vol(me, value, +1, &alsa_val)) >= 0) - r = snd_mixer_selem_ask_capture_vol_dB(me, alsa_val, &value); + if (write_to_hw) { + if ((r = snd_mixer_selem_set_capture_dB(me, c, value, rounding)) >= 0) + r = snd_mixer_selem_get_capture_dB(me, c, &value); + } else { + long alsa_val; + if ((r = snd_mixer_selem_ask_capture_dB_vol(me, value, rounding, &alsa_val)) >= 0) + r = snd_mixer_selem_ask_capture_vol_dB(me, alsa_val, &value); + } } } else r = -1; @@ -1012,9 +1125,19 @@ static int element_zero_volume(pa_alsa_element *e, snd_mixer_t *m) { } if (e->direction == PA_ALSA_DIRECTION_OUTPUT) - r = snd_mixer_selem_set_playback_dB_all(me, 0, +1); + if (e->db_fix) { + long value = 0; + + r = snd_mixer_selem_set_playback_volume_all(me, decibel_fix_get_step(e->db_fix, &value, +1)); + } else + r = snd_mixer_selem_set_playback_dB_all(me, 0, +1); else - r = snd_mixer_selem_set_capture_dB_all(me, 0, +1); + if (e->db_fix) { + long value = 0; + + r = snd_mixer_selem_set_capture_volume_all(me, decibel_fix_get_step(e->db_fix, &value, +1)); + } else + r = snd_mixer_selem_set_capture_dB_all(me, 0, +1); if (r < 0) pa_log_warn("Failed to set volume to 0dB of %s: %s", e->alsa_name, pa_alsa_strerror(errno)); @@ -1121,6 +1244,40 @@ static int check_required(pa_alsa_element *e, snd_mixer_elem_t *me) { if (e->required_absent == PA_ALSA_REQUIRED_ANY && (has_switch || has_volume || has_enumeration)) return -1; + if (e->required_any != PA_ALSA_REQUIRED_IGNORE) { + switch (e->required_any) { + case PA_ALSA_REQUIRED_VOLUME: + e->path->req_any_present |= (e->volume_use != PA_ALSA_VOLUME_IGNORE); + break; + case PA_ALSA_REQUIRED_SWITCH: + e->path->req_any_present |= (e->switch_use != PA_ALSA_SWITCH_IGNORE); + break; + case PA_ALSA_REQUIRED_ENUMERATION: + e->path->req_any_present |= (e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE); + break; + case PA_ALSA_REQUIRED_ANY: + e->path->req_any_present |= + (e->volume_use != PA_ALSA_VOLUME_IGNORE) || + (e->switch_use != PA_ALSA_SWITCH_IGNORE) || + (e->enumeration_use != PA_ALSA_ENUMERATION_IGNORE); + break; + default: + pa_assert_not_reached(); + } + } + + if (e->enumeration_use == PA_ALSA_ENUMERATION_SELECT) { + pa_alsa_option *o; + PA_LLIST_FOREACH(o, e->options) { + e->path->req_any_present |= (o->required_any != PA_ALSA_REQUIRED_IGNORE) && + (o->alsa_idx >= 0); + if (o->required != PA_ALSA_REQUIRED_IGNORE && o->alsa_idx < 0) + return -1; + if (o->required_absent != PA_ALSA_REQUIRED_IGNORE && o->alsa_idx >= 0) + return -1; + } + } + return 0; } @@ -1130,6 +1287,7 @@ static int element_probe(pa_alsa_element *e, snd_mixer_t *m) { pa_assert(m); pa_assert(e); + pa_assert(e->path); SELEM_INIT(sid, e->alsa_name); @@ -1197,26 +1355,6 @@ static int element_probe(pa_alsa_element *e, snd_mixer_t *m) { e->direction_try_other = FALSE; if (e->direction == PA_ALSA_DIRECTION_OUTPUT) - e->has_dB = snd_mixer_selem_get_playback_dB_range(me, &min_dB, &max_dB) >= 0; - else - e->has_dB = snd_mixer_selem_get_capture_dB_range(me, &min_dB, &max_dB) >= 0; - - if (e->has_dB) { -#ifdef HAVE_VALGRIND_MEMCHECK_H - VALGRIND_MAKE_MEM_DEFINED(&min_dB, sizeof(min_dB)); - VALGRIND_MAKE_MEM_DEFINED(&max_dB, sizeof(max_dB)); -#endif - - e->min_dB = ((double) min_dB) / 100.0; - e->max_dB = ((double) max_dB) / 100.0; - - if (min_dB >= max_dB) { - pa_log_warn("Your kernel driver is broken: it reports a volume range from %0.2f dB to %0.2f dB which makes no sense.", e->min_dB, e->max_dB); - e->has_dB = FALSE; - } - } - - if (e->direction == PA_ALSA_DIRECTION_OUTPUT) r = snd_mixer_selem_get_playback_volume_range(me, &e->min_volume, &e->max_volume); else r = snd_mixer_selem_get_capture_volume_range(me, &e->min_volume, &e->max_volume); @@ -1226,7 +1364,6 @@ static int element_probe(pa_alsa_element *e, snd_mixer_t *m) { return -1; } - if (e->min_volume >= e->max_volume) { pa_log_warn("Your kernel driver is broken: it reports a volume range from %li to %li which makes no sense.", e->min_volume, e->max_volume); e->volume_use = PA_ALSA_VOLUME_IGNORE; @@ -1235,6 +1372,75 @@ static int element_probe(pa_alsa_element *e, snd_mixer_t *m) { pa_bool_t is_mono; pa_channel_position_t p; + if (e->db_fix && + ((e->min_volume > e->db_fix->min_step) || + (e->max_volume < e->db_fix->max_step))) { + pa_log_warn("The step range of the decibel fix for element %s (%li-%li) doesn't fit to the " + "real hardware range (%li-%li). Disabling the decibel fix.", e->alsa_name, + e->db_fix->min_step, e->db_fix->max_step, + e->min_volume, e->max_volume); + + decibel_fix_free(e->db_fix); + e->db_fix = NULL; + } + + if (e->db_fix) { + e->has_dB = TRUE; + e->min_volume = e->db_fix->min_step; + e->max_volume = e->db_fix->max_step; + min_dB = e->db_fix->db_values[0]; + max_dB = e->db_fix->db_values[e->db_fix->max_step - e->db_fix->min_step]; + } else if (e->direction == PA_ALSA_DIRECTION_OUTPUT) + e->has_dB = snd_mixer_selem_get_playback_dB_range(me, &min_dB, &max_dB) >= 0; + else + e->has_dB = snd_mixer_selem_get_capture_dB_range(me, &min_dB, &max_dB) >= 0; + + if (e->has_dB) { +#ifdef HAVE_VALGRIND_MEMCHECK_H + VALGRIND_MAKE_MEM_DEFINED(&min_dB, sizeof(min_dB)); + VALGRIND_MAKE_MEM_DEFINED(&max_dB, sizeof(max_dB)); +#endif + + e->min_dB = ((double) min_dB) / 100.0; + e->max_dB = ((double) max_dB) / 100.0; + + if (min_dB >= max_dB) { + pa_assert(!e->db_fix); + pa_log_warn("Your kernel driver is broken: it reports a volume range from %0.2f dB to %0.2f dB which makes no sense.", e->min_dB, e->max_dB); + e->has_dB = FALSE; + } + } + + if (e->volume_limit >= 0) { + if (e->volume_limit <= e->min_volume || e->volume_limit > e->max_volume) + pa_log_warn("Volume limit for element %s of path %s is invalid: %li isn't within the valid range " + "%li-%li. The volume limit is ignored.", + e->alsa_name, e->path->name, e->volume_limit, e->min_volume + 1, e->max_volume); + + else { + e->max_volume = e->volume_limit; + + if (e->has_dB) { + if (e->db_fix) { + e->db_fix->max_step = e->max_volume; + e->max_dB = ((double) e->db_fix->db_values[e->db_fix->max_step - e->db_fix->min_step]) / 100.0; + + } else { + if (e->direction == PA_ALSA_DIRECTION_OUTPUT) + r = snd_mixer_selem_ask_playback_vol_dB(me, e->max_volume, &max_dB); + else + r = snd_mixer_selem_ask_capture_vol_dB(me, e->max_volume, &max_dB); + + if (r < 0) { + pa_log_warn("Failed to get dB value of %s: %s", e->alsa_name, pa_alsa_strerror(r)); + e->has_dB = FALSE; + } else + e->max_dB = ((double) max_dB) / 100.0; + } + } + } + } + if (e->direction == PA_ALSA_DIRECTION_OUTPUT) is_mono = snd_mixer_selem_is_playback_mono(me) > 0; else @@ -1293,9 +1499,6 @@ static int element_probe(pa_alsa_element *e, snd_mixer_t *m) { } - if (check_required(e, me) < 0) - return -1; - if (e->switch_use == PA_ALSA_SWITCH_SELECT) { pa_alsa_option *o; @@ -1327,6 +1530,9 @@ static int element_probe(pa_alsa_element *e, snd_mixer_t *m) { } } + if (check_required(e, me) < 0) + return -1; + return 0; } @@ -1358,6 +1564,7 @@ static pa_alsa_element* element_get(pa_alsa_path *p, const char *section, pa_boo e->path = p; e->alsa_name = pa_xstrdup(section); e->direction = p->direction; + e->volume_limit = -1; PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e); @@ -1581,20 +1788,23 @@ static int element_parse_required( pa_alsa_path *p = userdata; pa_alsa_element *e; + pa_alsa_option *o; pa_alsa_required_t req; pa_assert(p); - if (!(e = element_get(p, section, TRUE))) { + e = element_get(p, section, TRUE); + o = option_get(p, section); + if (!e && !o) { pa_log("[%s:%u] Required makes no sense in '%s'", filename, line, section); return -1; } if (pa_streq(rvalue, "ignore")) req = PA_ALSA_REQUIRED_IGNORE; - else if (pa_streq(rvalue, "switch")) + else if (pa_streq(rvalue, "switch") && e) req = PA_ALSA_REQUIRED_SWITCH; - else if (pa_streq(rvalue, "volume")) + else if (pa_streq(rvalue, "volume") && e) req = PA_ALSA_REQUIRED_VOLUME; else if (pa_streq(rvalue, "enumeration")) req = PA_ALSA_REQUIRED_ENUMERATION; @@ -1605,10 +1815,28 @@ static int element_parse_required( return -1; } - if (pa_streq(lvalue, "required-absent")) - e->required_absent = req; - else - e->required = req; + if (pa_streq(lvalue, "required-absent")) { + if (e) + e->required_absent = req; + if (o) + o->required_absent = req; + } + else if (pa_streq(lvalue, "required-any")) { + if (e) { + e->required_any = req; + e->path->has_req_any = TRUE; + } + if (o) { + o->required_any = req; + o->element->path->has_req_any = TRUE; + } + } + else { + if (e) + e->required = req; + if (o) + o->required = req; + } return 0; } @@ -1671,6 +1899,33 @@ static int element_parse_direction_try_other( return 0; } +static int element_parse_volume_limit( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + const char *rvalue, + void *data, + void *userdata) { + + pa_alsa_path *p = userdata; + pa_alsa_element *e; + uint32_t volume_limit; + + if (!(e = element_get(p, section, TRUE))) { + pa_log("[%s:%u] volume-limit makes no sense in '%s'", filename, line, section); + return -1; + } + + if (pa_atou(rvalue, &volume_limit) < 0 || volume_limit > LONG_MAX) { + pa_log("[%s:%u] Invalid value for volume-limit", filename, line); + return -1; + } + + e->volume_limit = volume_limit; + return 0; +} + static pa_channel_position_mask_t parse_mask(const char *m) { pa_channel_position_mask_t v; @@ -1804,8 +2059,11 @@ static int option_verify(pa_alsa_option *o) { { "input", N_("Input") }, { "input-docking", N_("Docking Station Input") }, { "input-docking-microphone", N_("Docking Station Microphone") }, + { "input-docking-linein", N_("Docking Station Line-In") }, { "input-linein", N_("Line-In") }, { "input-microphone", N_("Microphone") }, + { "input-microphone-front", N_("Front Microphone") }, + { "input-microphone-rear", N_("Rear Microphone") }, { "input-microphone-external", N_("External Microphone") }, { "input-microphone-internal", N_("Internal Microphone") }, { "input-radio", N_("Radio") }, @@ -1857,7 +2115,10 @@ static int element_verify(pa_alsa_element *e) { pa_assert(e); +// pa_log_debug("Element %s, path %s: r=%d, r-any=%d, r-abs=%d", e->alsa_name, e->path->name, e->required, e->required_any, e->required_absent); if ((e->required != PA_ALSA_REQUIRED_IGNORE && e->required == e->required_absent) || + (e->required_any != PA_ALSA_REQUIRED_IGNORE && e->required_any == e->required_absent) || + (e->required_absent == PA_ALSA_REQUIRED_ANY && e->required_any != PA_ALSA_REQUIRED_IGNORE) || (e->required_absent == PA_ALSA_REQUIRED_ANY && e->required != PA_ALSA_REQUIRED_IGNORE)) { pa_log("Element %s cannot be required and absent at the same time.", e->alsa_name); return -1; @@ -1879,6 +2140,10 @@ static int path_verify(pa_alsa_path *p) { static const struct description_map well_known_descriptions[] = { { "analog-input", N_("Analog Input") }, { "analog-input-microphone", N_("Analog Microphone") }, + { "analog-input-microphone-front", N_("Front Microphone") }, + { "analog-input-microphone-rear", N_("Rear Microphone") }, + { "analog-input-microphone-dock", N_("Docking Station Microphone") }, + { "analog-input-microphone-internal", N_("Internal Microphone") }, { "analog-input-linein", N_("Analog Line-In") }, { "analog-input-radio", N_("Analog Radio") }, { "analog-input-video", N_("Analog Video") }, @@ -1934,9 +2199,11 @@ pa_alsa_path* pa_alsa_path_new(const char *fname, pa_alsa_direction_t direction) { "override-map.2", element_parse_override_map, NULL, NULL }, /* ... later on we might add override-map.3 and so on here ... */ { "required", element_parse_required, NULL, NULL }, + { "required-any", element_parse_required, NULL, NULL }, { "required-absent", element_parse_required, NULL, NULL }, { "direction", element_parse_direction, NULL, NULL }, { "direction-try-other", element_parse_direction_try_other, NULL, NULL }, + { "volume-limit", element_parse_volume_limit, NULL, NULL }, { NULL, NULL, NULL, NULL } }; @@ -1987,11 +2254,13 @@ pa_alsa_path* pa_alsa_path_synthesize(const char*element, pa_alsa_direction_t di e->path = p; e->alsa_name = pa_xstrdup(element); e->direction = direction; + e->volume_limit = -1; e->switch_use = PA_ALSA_SWITCH_MUTE; e->volume_use = PA_ALSA_VOLUME_MERGE; PA_LLIST_PREPEND(pa_alsa_element, p->elements, e); + p->last_element = e; return p; } @@ -2131,6 +2400,7 @@ int pa_alsa_path_probe(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t ignore_dB) { pa_alsa_element *e; double min_dB[PA_CHANNEL_POSITION_MAX], max_dB[PA_CHANNEL_POSITION_MAX]; pa_channel_position_t t; + pa_channel_position_mask_t path_volume_channels = 0; pa_assert(p); pa_assert(m); @@ -2149,6 +2419,7 @@ int pa_alsa_path_probe(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t ignore_dB) { pa_log_debug("Probe of element '%s' failed.", e->alsa_name); return -1; } + pa_log_debug("Probe of element '%s' succeeded (volume=%d, switch=%d, enumeration=%d).", e->alsa_name, e->volume_use, e->switch_use, e->enumeration_use); if (ignore_dB) e->has_dB = FALSE; @@ -2166,6 +2437,7 @@ int pa_alsa_path_probe(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t ignore_dB) { if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) { min_dB[t] = e->min_dB; max_dB[t] = e->max_dB; + path_volume_channels |= PA_CHANNEL_POSITION_MASK(t); } p->has_dB = TRUE; @@ -2176,17 +2448,21 @@ int pa_alsa_path_probe(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t ignore_dB) { if (PA_CHANNEL_POSITION_MASK(t) & e->merged_mask) { min_dB[t] += e->min_dB; max_dB[t] += e->max_dB; + path_volume_channels |= PA_CHANNEL_POSITION_MASK(t); } - } else + } else { /* Hmm, there's another element before us * which cannot do dB volumes, so we we need * to 'neutralize' this slider */ e->volume_use = PA_ALSA_VOLUME_ZERO; + pa_log_info("Zeroing volume of '%s' on path '%s'", e->alsa_name, p->name); + } } - } else if (p->has_volume) + } else if (p->has_volume) { /* We can't use this volume, so let's ignore it */ e->volume_use = PA_ALSA_VOLUME_IGNORE; - + pa_log_info("Ignoring volume of '%s' on path '%s' (missing dB info)", e->alsa_name, p->name); + } p->has_volume = TRUE; } @@ -2194,6 +2470,12 @@ int pa_alsa_path_probe(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t ignore_dB) { p->has_mute = TRUE; } + if (p->has_req_any && !p->req_any_present) { + p->supported = FALSE; + pa_log_debug("Skipping path '%s', none of required-any elements preset.", p->name); + return -1; + } + path_drop_unsupported(p); path_make_options_unique(p); path_create_settings(p); @@ -2205,11 +2487,13 @@ int pa_alsa_path_probe(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t ignore_dB) { p->max_dB = -INFINITY; for (t = 0; t < PA_CHANNEL_POSITION_MAX; t++) { - if (p->min_dB > min_dB[t]) - p->min_dB = min_dB[t]; + if (path_volume_channels & PA_CHANNEL_POSITION_MASK(t)) { + if (p->min_dB > min_dB[t]) + p->min_dB = min_dB[t]; - if (p->max_dB < max_dB[t]) - p->max_dB = max_dB[t]; + if (p->max_dB < max_dB[t]) + p->max_dB = max_dB[t]; + } } return 0; @@ -2239,13 +2523,15 @@ void pa_alsa_element_dump(pa_alsa_element *e) { pa_alsa_option *o; pa_assert(e); - pa_log_debug("Element %s, direction=%i, switch=%i, volume=%i, enumeration=%i, required=%i, required_absent=%i, mask=0x%llx, n_channels=%u, override_map=%s", + pa_log_debug("Element %s, direction=%i, switch=%i, volume=%i, volume_limit=%li, enumeration=%i, required=%i, required_any=%i, required_absent=%i, mask=0x%llx, n_channels=%u, override_map=%s", e->alsa_name, e->direction, e->switch_use, e->volume_use, + e->volume_limit, e->enumeration_use, e->required, + e->required_any, e->required_absent, (long long unsigned) e->merged_mask, e->n_channels, @@ -2324,8 +2610,12 @@ void pa_alsa_path_set_set_callback(pa_alsa_path_set *ps, snd_mixer_t *m, snd_mix pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t direction) { pa_alsa_path_set *ps; char **pn = NULL, **en = NULL, **ie; + pa_alsa_decibel_fix *db_fix; + void *state; pa_assert(m); + pa_assert(m->profile_set); + pa_assert(m->profile_set->decibel_fixes); pa_assert(direction == PA_ALSA_DIRECTION_OUTPUT || direction == PA_ALSA_DIRECTION_INPUT); if (m->direction != PA_ALSA_DIRECTION_ANY && m->direction != direction) @@ -2367,7 +2657,7 @@ pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t d pa_xfree(fn); } - return ps; + goto finish; } if (direction == PA_ALSA_DIRECTION_OUTPUT) @@ -2390,11 +2680,16 @@ pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t d /* Mark all other passed elements for require-absent */ for (je = en; *je; je++) { pa_alsa_element *e; + + if (je == ie) + continue; + e = pa_xnew0(pa_alsa_element, 1); e->path = p; e->alsa_name = pa_xstrdup(*je); e->direction = direction; e->required_absent = PA_ALSA_REQUIRED_ANY; + e->volume_limit = -1; PA_LLIST_INSERT_AFTER(pa_alsa_element, p->elements, p->last_element, e); p->last_element = e; @@ -2404,6 +2699,28 @@ pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t d ps->last_path = p; } +finish: + /* Assign decibel fixes to elements. */ + PA_HASHMAP_FOREACH(db_fix, m->profile_set->decibel_fixes, state) { + pa_alsa_path *p; + + PA_LLIST_FOREACH(p, ps->paths) { + pa_alsa_element *e; + + PA_LLIST_FOREACH(e, p->elements) { + if (e->volume_use != PA_ALSA_VOLUME_IGNORE && pa_streq(db_fix->name, e->alsa_name)) { + /* The profile set that contains the dB fix may be freed + * before the element, so we have to copy the dB fix + * object. */ + e->db_fix = pa_xnewdup(pa_alsa_decibel_fix, db_fix, 1); + e->db_fix->profile_set = NULL; + e->db_fix->name = pa_xstrdup(db_fix->name); + e->db_fix->db_values = pa_xmemdup(db_fix->db_values, (db_fix->max_step - db_fix->min_step + 1) * sizeof(long)); + } + } + } + } + return ps; } @@ -2580,6 +2897,15 @@ void pa_alsa_profile_set_free(pa_alsa_profile_set *ps) { pa_hashmap_free(ps->mappings, NULL, NULL); } + if (ps->decibel_fixes) { + pa_alsa_decibel_fix *db_fix; + + while ((db_fix = pa_hashmap_steal_first(ps->decibel_fixes))) + decibel_fix_free(db_fix); + + pa_hashmap_free(ps->decibel_fixes, NULL, NULL); + } + pa_xfree(ps); } @@ -2624,6 +2950,26 @@ static pa_alsa_profile *profile_get(pa_alsa_profile_set *ps, const char *name) { return p; } +static pa_alsa_decibel_fix *decibel_fix_get(pa_alsa_profile_set *ps, const char *name) { + pa_alsa_decibel_fix *db_fix; + + if (!pa_startswith(name, "DecibelFix ")) + return NULL; + + name += 11; + + if ((db_fix = pa_hashmap_get(ps->decibel_fixes, name))) + return db_fix; + + db_fix = pa_xnew0(pa_alsa_decibel_fix, 1); + db_fix->profile_set = ps; + db_fix->name = pa_xstrdup(name); + + pa_hashmap_put(ps->decibel_fixes, db_fix->name, db_fix); + + return db_fix; +} + static int mapping_parse_device_strings( const char *filename, unsigned line, @@ -2894,6 +3240,130 @@ static int profile_parse_skip_probe( return 0; } +static int decibel_fix_parse_db_values( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + const char *rvalue, + void *data, + void *userdata) { + + pa_alsa_profile_set *ps = userdata; + 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(filename); + pa_assert(section); + pa_assert(lvalue); + pa_assert(rvalue); + pa_assert(ps); + + if (!(db_fix = decibel_fix_get(ps, section))) { + pa_log("[%s:%u] %s invalid in section %s", filename, line, lvalue, section); + return -1; + } + + if (!(items = pa_split_spaces_strv(rvalue))) { + pa_log("[%s:%u] Value missing", pa_strnull(filename), line); + 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", filename, line, 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", filename, line, 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", filename, line, s); + goto fail; + } + + if (pa_atod(d, &db) < 0) { + pa_log("[%s:%u] Invalid dB value: %s", filename, line, d); + goto fail; + } + + if (step <= prev_step && i != 1) { + pa_log("[%s:%u] Step value %u not greater than the previous value %u", filename, line, 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", filename, line, 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 int mapping_verify(pa_alsa_mapping *m, const pa_channel_map *bonus) { static const struct description_map well_known_descriptions[] = { @@ -2931,7 +3401,7 @@ static int mapping_verify(pa_alsa_mapping *m, const pa_channel_map *bonus) { if ((m->input_path_names && m->input_element) || (m->output_path_names && m->output_element)) { - pa_log("Mapping %s must have either mixer path or mixer elment, not both.", m->name); + pa_log("Mapping %s must have either mixer path or mixer element, not both.", m->name); return -1; } @@ -3176,10 +3646,52 @@ void pa_alsa_profile_dump(pa_alsa_profile *p) { pa_log_debug("Output %s", m->name); } +static int decibel_fix_verify(pa_alsa_decibel_fix *db_fix) { + pa_assert(db_fix); + + /* Check that the dB mapping has been configured. Since "db-values" is + * currently the only option in the DecibelFix section, and decibel fix + * objects don't get created if a DecibelFix section is empty, this is + * actually a redundant check. Having this may prevent future bugs, + * however. */ + if (!db_fix->db_values) { + pa_log("Decibel fix for element %s lacks the dB values.", db_fix->name); + return -1; + } + + return 0; +} + +void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix *db_fix) { + char *db_values = NULL; + + pa_assert(db_fix); + + if (db_fix->db_values) { + pa_strbuf *buf; + long i; + long max_i = db_fix->max_step - db_fix->min_step; + + buf = pa_strbuf_new(); + pa_strbuf_printf(buf, "[%li]:%0.2f", db_fix->min_step, db_fix->db_values[0] / 100.0); + + for (i = 1; i <= max_i; ++i) + pa_strbuf_printf(buf, " [%li]:%0.2f", i + db_fix->min_step, db_fix->db_values[i] / 100.0); + + db_values = pa_strbuf_tostring_free(buf); + } + + pa_log_debug("Decibel fix %s, min_step=%li, max_step=%li, db_values=%s", + db_fix->name, db_fix->min_step, db_fix->max_step, pa_strnull(db_values)); + + pa_xfree(db_values); +} + pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel_map *bonus) { pa_alsa_profile_set *ps; pa_alsa_profile *p; pa_alsa_mapping *m; + pa_alsa_decibel_fix *db_fix; char *fn; int r; void *state; @@ -3205,12 +3717,16 @@ pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel { "input-mappings", profile_parse_mappings, NULL, NULL }, { "output-mappings", profile_parse_mappings, NULL, NULL }, { "skip-probe", profile_parse_skip_probe, NULL, NULL }, + + /* [DecibelFix ...] */ + { "db-values", decibel_fix_parse_db_values, NULL, NULL }, { NULL, NULL, NULL, NULL } }; ps = pa_xnew0(pa_alsa_profile_set, 1); ps->mappings = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); ps->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + ps->decibel_fixes = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); items[0].data = &ps->auto_profiles; @@ -3240,6 +3756,10 @@ pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel if (profile_verify(p) < 0) goto fail; + PA_HASHMAP_FOREACH(db_fix, ps->decibel_fixes, state) + if (decibel_fix_verify(db_fix) < 0) + goto fail; + return ps; fail: @@ -3420,23 +3940,28 @@ void pa_alsa_profile_set_probe( void pa_alsa_profile_set_dump(pa_alsa_profile_set *ps) { pa_alsa_profile *p; pa_alsa_mapping *m; + pa_alsa_decibel_fix *db_fix; void *state; pa_assert(ps); - pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u", + pa_log_debug("Profile set %p, auto_profiles=%s, probed=%s, n_mappings=%u, n_profiles=%u, n_decibel_fixes=%u", (void*) ps, pa_yes_no(ps->auto_profiles), pa_yes_no(ps->probed), pa_hashmap_size(ps->mappings), - pa_hashmap_size(ps->profiles)); + pa_hashmap_size(ps->profiles), + pa_hashmap_size(ps->decibel_fixes)); PA_HASHMAP_FOREACH(m, ps->mappings, state) pa_alsa_mapping_dump(m); PA_HASHMAP_FOREACH(p, ps->profiles, state) pa_alsa_profile_dump(p); + + PA_HASHMAP_FOREACH(db_fix, ps->decibel_fixes, state) + pa_alsa_decibel_fix_dump(db_fix); } void pa_alsa_add_ports(pa_hashmap **p, pa_alsa_path_set *ps) { diff --git a/src/modules/alsa/alsa-mixer.h b/src/modules/alsa/alsa-mixer.h index 7fb408a6..c24a8965 100644 --- a/src/modules/alsa/alsa-mixer.h +++ b/src/modules/alsa/alsa-mixer.h @@ -46,6 +46,7 @@ typedef struct pa_alsa_path pa_alsa_path; typedef struct pa_alsa_path_set pa_alsa_path_set; typedef struct pa_alsa_mapping pa_alsa_mapping; typedef struct pa_alsa_profile pa_alsa_profile; +typedef struct pa_alsa_decibel_fix pa_alsa_decibel_fix; typedef struct pa_alsa_profile_set pa_alsa_profile_set; typedef struct pa_alsa_port_data pa_alsa_port_data; @@ -112,11 +113,15 @@ struct pa_alsa_option { char *name; char *description; unsigned priority; + + pa_alsa_required_t required; + pa_alsa_required_t required_any; + pa_alsa_required_t required_absent; }; -/* And element wraps one specific ALSA element. A series of elements * -make up a path (see below). If the element is an enumeration or switch -* element it may includes a list of options. */ +/* An element wraps one specific ALSA element. A series of elements + * make up a path (see below). If the element is an enumeration or switch + * element it may include a list of options. */ struct pa_alsa_element { pa_alsa_path *path; PA_LLIST_FIELDS(pa_alsa_element); @@ -129,6 +134,7 @@ struct pa_alsa_element { pa_alsa_enumeration_use_t enumeration_use; pa_alsa_required_t required; + pa_alsa_required_t required_any; pa_alsa_required_t required_absent; pa_bool_t override_map:1; @@ -136,6 +142,7 @@ struct pa_alsa_element { pa_bool_t has_dB:1; long min_volume, max_volume; + long volume_limit; /* -1 for no configured limit */ double min_dB, max_dB; pa_channel_position_mask_t masks[SND_MIXER_SCHN_LAST][2]; @@ -144,6 +151,8 @@ struct pa_alsa_element { pa_channel_position_mask_t merged_mask; PA_LLIST_HEAD(pa_alsa_option, options); + + pa_alsa_decibel_fix *db_fix; }; /* A path wraps a series of elements into a single entity which can be @@ -164,6 +173,9 @@ struct pa_alsa_path { pa_bool_t has_mute:1; pa_bool_t has_volume:1; pa_bool_t has_dB:1; + /* These two are used during probing only */ + pa_bool_t has_req_any:1; + pa_bool_t req_any_present:1; long min_volume, max_volume; double min_dB, max_dB; @@ -258,9 +270,26 @@ struct pa_alsa_profile { pa_idxset *output_mappings; }; +struct pa_alsa_decibel_fix { + pa_alsa_profile_set *profile_set; + + char *name; /* Alsa volume element name. */ + long min_step; + long max_step; + + /* An array that maps alsa volume element steps to decibels. The steps can + * be used as indices to this array, after substracting min_step from the + * real value. + * + * The values are actually stored as integers representing millibels, + * because that's the format the alsa API uses. */ + long *db_values; +}; + struct pa_alsa_profile_set { pa_hashmap *mappings; pa_hashmap *profiles; + pa_hashmap *decibel_fixes; pa_bool_t auto_profiles; pa_bool_t probed:1; @@ -268,6 +297,7 @@ struct pa_alsa_profile_set { void pa_alsa_mapping_dump(pa_alsa_mapping *m); void pa_alsa_profile_dump(pa_alsa_profile *p); +void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix *db_fix); pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel_map *bonus); void pa_alsa_profile_set_probe(pa_alsa_profile_set *ps, const char *dev_id, const pa_sample_spec *ss, unsigned default_n_fragments, unsigned default_fragment_size_msec); diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index 45a7af39..6d18e607 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -796,7 +796,7 @@ static void update_smoother(struct userdata *u) { } static pa_usec_t source_get_latency(struct userdata *u) { - int64_t delay; + int64_t delay; pa_usec_t now1, now2; pa_assert(u); diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c index 786e664d..6435db00 100644 --- a/src/modules/alsa/alsa-util.c +++ b/src/modules/alsa/alsa-util.c @@ -362,7 +362,7 @@ int pa_alsa_set_hw_params( pa_log_debug("Set neither period nor buffer size."); /* Last chance, set nothing */ - if ((ret = snd_pcm_hw_params(pcm_handle, hwparams)) < 0) { + if ((ret = snd_pcm_hw_params(pcm_handle, hwparams)) < 0) { pa_log_info("snd_pcm_hw_params failed: %s", pa_alsa_strerror(ret)); goto finish; } diff --git a/src/modules/alsa/mixer/paths/analog-input-dock-mic.conf b/src/modules/alsa/mixer/paths/analog-input-dock-mic.conf new file mode 100644 index 00000000..74826a96 --- /dev/null +++ b/src/modules/alsa/mixer/paths/analog-input-dock-mic.conf @@ -0,0 +1,81 @@ +# This file is part of PulseAudio. +# +# PulseAudio is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation; either version 2.1 of the +# License, or (at your option) any later version. +# +# PulseAudio 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 Lesser General Public License +# along with PulseAudio; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +; For devices where a 'Dock Mic' or 'Dock Mic Boost' element exists +; +; See analog-output.conf.common for an explanation on the directives + +[General] +priority = 80 +name = analog-input-microphone-dock + +[Element Dock Mic Boost] +required-any = any +switch = select +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + +[Option Dock Mic Boost:on] +name = input-boost-on + +[Option Dock Mic Boost:off] +name = input-boost-off + +[Element Dock Mic] +required-any = any +switch = mute +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + +[Element Capture] +switch = mute +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + +[Element Input Source] +enumeration = select + +[Option Input Source:Dock Mic] +name = analog-input-microphone-dock +required-any = any + +[Element Capture Source] +enumeration = select + +[Option Capture Source:Dock Mic] +name = analog-input-microphone-dock +required-any = any + +[Element Mic] +switch = off +volume = off + +[Element Internal Mic] +switch = off +volume = off + +[Element Front Mic] +switch = off +volume = off + +[Element Rear Mic] +switch = off +volume = off + +.include analog-input-mic.conf.common diff --git a/src/modules/alsa/mixer/paths/analog-input-front-mic.conf b/src/modules/alsa/mixer/paths/analog-input-front-mic.conf new file mode 100644 index 00000000..6c58ece1 --- /dev/null +++ b/src/modules/alsa/mixer/paths/analog-input-front-mic.conf @@ -0,0 +1,81 @@ +# This file is part of PulseAudio. +# +# PulseAudio is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation; either version 2.1 of the +# License, or (at your option) any later version. +# +# PulseAudio 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 Lesser General Public License +# along with PulseAudio; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +; For devices where a 'Front Mic' or 'Front Mic Boost' element exists +; +; See analog-output.conf.common for an explanation on the directives + +[General] +priority = 90 +name = analog-input-microphone-front + +[Element Front Mic Boost] +required-any = any +switch = select +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + +[Option Front Mic Boost:on] +name = input-boost-on + +[Option Front Mic Boost:off] +name = input-boost-off + +[Element Front Mic] +required-any = any +switch = mute +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + +[Element Capture] +switch = mute +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + +[Element Input Source] +enumeration = select + +[Option Input Source:Front Mic] +name = analog-input-microphone-front +required-any = any + +[Element Capture Source] +enumeration = select + +[Option Capture Source:Front Mic] +name = analog-input-microphone-front +required-any = any + +[Element Mic] +switch = off +volume = off + +[Element Internal Mic] +switch = off +volume = off + +[Element Rear Mic] +switch = off +volume = off + +[Element Dock Mic] +switch = off +volume = off + +.include analog-input-mic.conf.common diff --git a/src/modules/alsa/mixer/paths/analog-input-internal-mic.conf b/src/modules/alsa/mixer/paths/analog-input-internal-mic.conf index 70cd5129..70a1cd12 100644 --- a/src/modules/alsa/mixer/paths/analog-input-internal-mic.conf +++ b/src/modules/alsa/mixer/paths/analog-input-internal-mic.conf @@ -14,54 +14,98 @@ # along with PulseAudio; if not, write to the Free Software Foundation, # Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. -; For devices where a 'Internal Mic' element exists +; For devices where a 'Internal Mic' or 'Internal Mic Boost' element exists +; 'Int Mic' and 'Int Mic Boost' are for compatibility with kernels < 2.6.38 ; ; See analog-output.conf.common for an explanation on the directives [General] -priority = 90 -name = analog-input-microphone +priority = 89 +name = analog-input-microphone-internal -[Element Capture] -switch = mute +[Element Internal Mic Boost] +required-any = any +switch = select volume = merge override-map.1 = all override-map.2 = all-left,all-right -[Element Mic] -switch = off -volume = off +[Option Internal Mic Boost:on] +name = input-boost-on + +[Option Internal Mic Boost:off] +name = input-boost-off + +[Element Int Mic Boost] +required-any = any +switch = select +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + +[Option Int Mic Boost:on] +name = input-boost-on + +[Option Int Mic Boost:off] +name = input-boost-off + [Element Internal Mic] -required = any +required-any = any switch = mute volume = merge override-map.1 = all override-map.2 = all-left,all-right -[Element Line] -switch = off -volume = off +[Element Int Mic] +required-any = any +switch = mute +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right -[Element Aux] -switch = off -volume = off +[Element Capture] +switch = mute +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + +[Element Input Source] +enumeration = select + +[Option Input Source:Internal Mic] +name = analog-input-microphone-internal +required-any = any + +[Option Input Source:Int Mic] +name = analog-input-microphone-internal +required-any = any -[Element Video] +[Element Capture Source] +enumeration = select + +[Option Capture Source:Internal Mic] +name = analog-input-microphone-internal +required-any = any + +[Option Capture Source:Int Mic] +name = analog-input-microphone-internal +required-any = any + +[Element Mic] switch = off volume = off -[Element Mic/Line] +[Element Dock Mic] switch = off volume = off -[Element TV Tuner] +[Element Front Mic] switch = off volume = off -[Element FM] +[Element Rear Mic] switch = off volume = off -.include analog-input.conf.common .include analog-input-mic.conf.common diff --git a/src/modules/alsa/mixer/paths/analog-input-linein.conf b/src/modules/alsa/mixer/paths/analog-input-linein.conf index 57568ccd..461cebdb 100644 --- a/src/modules/alsa/mixer/paths/analog-input-linein.conf +++ b/src/modules/alsa/mixer/paths/analog-input-linein.conf @@ -35,13 +35,35 @@ volume = off switch = off volume = off +[Element Line Boost] +required-any = any +switch = mute +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + [Element Line] -required = any +required-any = any switch = mute volume = merge override-map.1 = all override-map.2 = all-left,all-right +[Element Input Source] +enumeration = select + +[Option Input Source:Line] +name = analog-input-linein +required-any = any + +[Element Capture Source] +enumeration = select + +[Option Capture Source:Line] +name = analog-input-linein +required-any = any + + [Element Aux] switch = off volume = off @@ -62,4 +84,10 @@ volume = off switch = off volume = off -.include analog-input.conf.common +[Element Mic Jack Mode] +enumeration = select + +[Option Mic Jack Mode:Line In] +priority = 19 +required-any = any +name = input-linein diff --git a/src/modules/alsa/mixer/paths/analog-input-mic.conf b/src/modules/alsa/mixer/paths/analog-input-mic.conf index 9b8b75a1..d88028bf 100644 --- a/src/modules/alsa/mixer/paths/analog-input-mic.conf +++ b/src/modules/alsa/mixer/paths/analog-input-mic.conf @@ -14,54 +14,91 @@ # along with PulseAudio; if not, write to the Free Software Foundation, # Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. -; For devices where a 'Mic' element exists +; For devices where a 'Mic' or 'Mic Boost' element exists ; ; See analog-output.conf.common for an explanation on the directives [General] -priority = 100 +priority = 89 name = analog-input-microphone -[Element Capture] -switch = mute +[Element Mic Boost] +required-any = any +switch = select volume = merge override-map.1 = all override-map.2 = all-left,all-right +[Option Mic Boost:on] +name = input-boost-on + +[Option Mic Boost:off] +name = input-boost-off + [Element Mic] -required = any +required-any = any switch = mute volume = merge override-map.1 = all override-map.2 = all-left,all-right -[Element Internal Mic] -switch = off -volume = off +[Element Capture] +switch = mute +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right -[Element Line] -switch = off -volume = off +[Element Input Source] +enumeration = select -[Element Aux] -switch = off -volume = off +[Option Input Source:Mic] +name = analog-input-microphone +required-any = any + +[Element Capture Source] +enumeration = select + +[Option Capture Source:Mic] +name = analog-input-microphone +required-any = any + +;;; Some AC'97s have "Mic Select" and "Mic Boost (+20dB)" + +[Element Mic Select] +enumeration = select -[Element Video] +[Option Mic Select:Mic1] +name = input-microphone +priority = 20 + +[Option Mic Select:Mic2] +name = input-microphone +priority = 19 + +[Element Mic Boost (+20dB)] +switch = select +volume = merge + +[Option Mic Boost (+20dB):on] +name = input-boost-on + +[Option Mic Boost (+20dB):off] +name = input-boost-off + +[Element Front Mic] switch = off volume = off -[Element Mic/Line] +[Element Internal Mic] switch = off volume = off -[Element TV Tuner] +[Element Rear Mic] switch = off volume = off -[Element FM] +[Element Dock Mic] switch = off volume = off -.include analog-input.conf.common .include analog-input-mic.conf.common diff --git a/src/modules/alsa/mixer/paths/analog-input-mic.conf.common b/src/modules/alsa/mixer/paths/analog-input-mic.conf.common index 9bddd48c..2e4f0d81 100644 --- a/src/modules/alsa/mixer/paths/analog-input-mic.conf.common +++ b/src/modules/alsa/mixer/paths/analog-input-mic.conf.common @@ -18,64 +18,37 @@ ; ; See analog-output.conf.common for an explanation on the directives -;;; 'Mic Select' +[Element Line] +switch = off +volume = off -[Element Mic Select] -enumeration = select - -[Option Mic Select:Mic1] -name = input-microphone -priority = 20 - -[Option Mic Select:Mic2] -name = input-microphone -priority = 19 - -;;; Various Boosts - -[Element Mic Boost (+20dB)] -switch = select -volume = merge - -[Option Mic Boost (+20dB):on] -name = input-boost-on +[Element Line Boost] +switch = off +volume = off -[Option Mic Boost (+20dB):off] -name = input-boost-off +[Element Aux] +switch = off +volume = off -[Element Mic Boost] -switch = select -volume = merge +[Element Video] +switch = off +volume = off -[Option Mic Boost:on] -name = input-boost-on +[Element Mic/Line] +switch = off +volume = off -[Option Mic Boost:off] -name = input-boost-off +[Element TV Tuner] +switch = off +volume = off -[Element Front Mic Boost] -switch = select +[Element FM] +switch = off +volume = off -[Option Front Mic Boost:on] -name = input-boost-on - -[Option Front Mic Boost:off] -name = input-boost-off - -[Element Rear Mic Boost] -switch = select - -[Option Rear Mic Boost:on] -name = input-boost-on - -[Option Rear Mic Boost:off] -name = input-boost-off - -[Element Int Mic Boost] -switch = select - -[Option Int Mic Boost:on] -name = input-boost-on +[Element Mic Jack Mode] +enumeration = select -[Option Int Mic Boost:off] -name = input-boost-off +[Option Mic Jack Mode:Mic In] +priority = 19 +name = input-microphone diff --git a/src/modules/alsa/mixer/paths/analog-input-rear-mic.conf b/src/modules/alsa/mixer/paths/analog-input-rear-mic.conf new file mode 100644 index 00000000..75ed61b0 --- /dev/null +++ b/src/modules/alsa/mixer/paths/analog-input-rear-mic.conf @@ -0,0 +1,81 @@ +# This file is part of PulseAudio. +# +# PulseAudio is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation; either version 2.1 of the +# License, or (at your option) any later version. +# +# PulseAudio 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 Lesser General Public License +# along with PulseAudio; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +; For devices where a 'Rear Mic' or 'Rear Mic Boost' element exists +; +; See analog-output.conf.common for an explanation on the directives + +[General] +priority = 89 +name = analog-input-microphone-rear + +[Element Rear Mic Boost] +required-any = any +switch = select +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + +[Option Rear Mic Boost:on] +name = input-boost-on + +[Option Rear Mic Boost:off] +name = input-boost-off + +[Element Rear Mic] +required-any = any +switch = mute +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + +[Element Capture] +switch = mute +volume = merge +override-map.1 = all +override-map.2 = all-left,all-right + +[Element Input Source] +enumeration = select + +[Option Input Source:Rear Mic] +name = analog-input-microphone-rear +required-any = any + +[Element Capture Source] +enumeration = select + +[Option Capture Source:Rear Mic] +name = analog-input-microphone-rear +required-any = any + +[Element Mic] +switch = off +volume = off + +[Element Internal Mic] +switch = off +volume = off + +[Element Front Mic] +switch = off +volume = off + +[Element Dock Mic] +switch = off +volume = off + +.include analog-input-mic.conf.common diff --git a/src/modules/alsa/mixer/paths/analog-input.conf b/src/modules/alsa/mixer/paths/analog-input.conf index 30507386..b86c3564 100644 --- a/src/modules/alsa/mixer/paths/analog-input.conf +++ b/src/modules/alsa/mixer/paths/analog-input.conf @@ -32,9 +32,36 @@ override-map.2 = all-left,all-right [Element Mic] required-absent = any +[Element Dock Mic] +required-absent = any + +[Element Dock Mic Boost] +required-absent = any + +[Element Front Mic] +required-absent = any + +[Element Front Mic Boost] +required-absent = any + +[Element Int Mic] +required-absent = any + +[Element Int Mic Boost] +required-absent = any + [Element Internal Mic] required-absent = any +[Element Internal Mic Boost] +required-absent = any + +[Element Rear Mic] +required-absent = any + +[Element Rear Mic Boost] +required-absent = any + [Element Line] required-absent = any @@ -54,4 +81,3 @@ required-absent = any required-absent = any .include analog-input.conf.common -.include analog-input-mic.conf.common diff --git a/src/modules/alsa/mixer/paths/analog-input.conf.common b/src/modules/alsa/mixer/paths/analog-input.conf.common index 0b2cfd94..94165776 100644 --- a/src/modules/alsa/mixer/paths/analog-input.conf.common +++ b/src/modules/alsa/mixer/paths/analog-input.conf.common @@ -66,42 +66,18 @@ enumeration = select name = input-microphone priority = 20 -[Option Input Source:Mic] -name = input-microphone -priority = 20 - [Option Input Source:Microphone] name = input-microphone priority = 20 -[Option Input Source:Front Mic] -name = input-microphone -priority = 19 - [Option Input Source:Front Microphone] name = input-microphone priority = 19 -[Option Input Source:Int Mic] -name = input-microphone -priority = 19 - -[Option Input Source:Internal Mic] -name = input-microphone -priority = 19 - -[Option Input Source:Rear Mic] -name = input-microphone -priority = 19 - [Option Input Source:Internal Mic 1] name = input-microphone priority = 19 -[Option Input Source:Line] -name = input-linein -priority = 18 - [Option Input Source:Line-In] name = input-linein priority = 18 @@ -135,21 +111,12 @@ name = input [Option Capture Source:Line/Mic] name = input -[Option Capture Source:Mic] -name = input-microphone - [Option Capture Source:Microphone] name = input-microphone -[Option Capture Source:Int Mic] -name = input-microphone-internal - [Option Capture Source:Int DMic] name = input-microphone-internal -[Option Capture Source:Internal Mic] -name = input-microphone-internal - [Option Capture Source:iMic] name = input-microphone-internal @@ -159,15 +126,9 @@ name = input-microphone-internal [Option Capture Source:Internal Microphone] name = input-microphone-internal -[Option Capture Source:Front Mic] -name = input-microphone - [Option Capture Source:Front Microphone] name = input-microphone -[Option Capture Source:Rear Mic] -name = input-microphone - [Option Capture Source:Mic1] name = input-microphone @@ -198,9 +159,6 @@ name = input-linein [Option Capture Source:Analog] name = input -[Option Capture Source:Line] -name = input-linein - [Option Capture Source:Line-In] name = input-linein @@ -261,9 +219,6 @@ name = input [Option Capture Source:Docking-Station] name = input-docking -[Option Capture Source:Dock Mic] -name = input-docking-microphone - ;;; 'Mic Jack Mode' [Element Mic Jack Mode] diff --git a/src/modules/alsa/mixer/paths/analog-output.conf.common b/src/modules/alsa/mixer/paths/analog-output.conf.common index 6131da5c..c7c44350 100644 --- a/src/modules/alsa/mixer/paths/analog-output.conf.common +++ b/src/modules/alsa/mixer/paths/analog-output.conf.common @@ -63,10 +63,15 @@ ; # by the option name, resp. on/off if the element is a switch. ; name = ... # Logical name to use in the path identifier ; priority = ... # Priority if this is made into a device port +; required = ignore | enumeration | any # In this element, this option must exist or the path will be invalid. ("any" is an alias for "enumeration".) +; required-any = ignore | enumeration | any # In this element, either this or another option must exist (or an element) +; required-absent = ignore | enumeration | any # In this element, this option must not exist or the path will be invalid ; ; [Element ...] # For each element that we shall control ; required = ignore | switch | volume | enumeration | any # If set, require this element to be of this kind and available, ; # otherwise don't consider this path valid for the card +; required-any = ignore | switch | volume | enumeration | any # If set, at least one of the elements with required-any in this +; # path must be present, otherwise this path is invalid for the card ; required-absent = ignore | switch | volume # If set, require this element to not be of this kind and not ; # available, otherwise don't consider this path valid for the card ; @@ -77,6 +82,7 @@ ; volume = ignore | merge | off | zero # What to do with this volume: ignore it, merge it into the device ; # volume slider, always set it to the lowest value possible, or always ; # set it to 0 dB (for whatever that means) +; volume-limit = <volume step> # Limit the maximum volume by disabling the volume steps above <volume step>. ; enumeration = ignore | select # What to do with this enumeration, ignore it or make it selectable ; # via device ports. If set to 'select' you need to define an Option section ; # for each of the items you want to expose diff --git a/src/modules/alsa/mixer/profile-sets/90-pulseaudio.rules b/src/modules/alsa/mixer/profile-sets/90-pulseaudio.rules index f964b005..03293409 100644 --- a/src/modules/alsa/mixer/profile-sets/90-pulseaudio.rules +++ b/src/modules/alsa/mixer/profile-sets/90-pulseaudio.rules @@ -24,6 +24,8 @@ SUBSYSTEMS=="usb", ATTRS{idVendor}=="17cc", ATTRS{idProduct}=="1978", ENV{PULSE_ SUBSYSTEMS=="usb", ATTRS{idVendor}=="17cc", ATTRS{idProduct}=="0839", ENV{PULSE_PROFILE_SET}="native-instruments-audio4dj.conf" SUBSYSTEMS=="usb", ATTRS{idVendor}=="17cc", ATTRS{idProduct}=="baff", ENV{PULSE_PROFILE_SET}="native-instruments-traktorkontrol-s4.conf" SUBSYSTEMS=="usb", ATTRS{idVendor}=="17cc", ATTRS{idProduct}=="4711", ENV{PULSE_PROFILE_SET}="native-instruments-korecontroller.conf" +SUBSYSTEMS=="usb", ATTRS{idVendor}=="17cc", ATTRS{idProduct}=="1011", ENV{PULSE_PROFILE_SET}="native-instruments-traktor-audio6.conf" +SUBSYSTEMS=="usb", ATTRS{idVendor}=="17cc", ATTRS{idProduct}=="1021", ENV{PULSE_PROFILE_SET}="native-instruments-traktor-audio10.conf" SUBSYSTEMS=="usb", ATTRS{idVendor}=="0763", ATTRS{idProduct}=="2012", ENV{PULSE_PROFILE_SET}="maudio-fasttrack-pro.conf" LABEL="pulseaudio_end" diff --git a/src/modules/alsa/mixer/profile-sets/default.conf b/src/modules/alsa/mixer/profile-sets/default.conf index f470d604..9f7b5f2b 100644 --- a/src/modules/alsa/mixer/profile-sets/default.conf +++ b/src/modules/alsa/mixer/profile-sets/default.conf @@ -16,17 +16,27 @@ ; Default profile definitions for the ALSA backend of PulseAudio. This ; is used as fallback for all cards that have no special mapping -; assigned. (and should be good enough for the vast majority of -; cards). Use the udev property PULSE_PROFILE_SET to assign a -; different profile set than this one to a device. So what is this -; about? Simply, what we do here is map ALSA devices to how they are -; exposed in PA. We say which ALSA device string to use to open a -; device, which channel mapping to use then, and which mixer path to -; use. This is encoded in a 'mapping'. Multiple of these mappings can -; be bound together in a 'profile' which is then directly exposed in -; the UI as a card profile. Each mapping assigned to a profile will -; result in one sink/source to be created if the profile is selected -; for the card. +; assigned (and should be good enough for the vast majority of +; cards). If you want to assign a different profile set than this one +; to a device, either set the udev property PULSE_PROFILE_SET for the +; card, or use the "profile_set" module argument when loading +; module-alsa-card. +; +; So what is this about? Simply, what we do here is map ALSA devices +; to how they are exposed in PA. We say which ALSA device string to +; use to open a device, which channel mapping to use then, and which +; mixer path to use. This is encoded in a 'mapping'. Multiple of these +; mappings can be bound together in a 'profile' which is then directly +; exposed in the UI as a card profile. Each mapping assigned to a +; profile will result in one sink/source to be created if the profile +; is selected for the card. +; +; Additionally, the path set configuration files can describe the +; decibel values assigned to the steps of the volume elements. This +; can be used to work around situations when the alsa driver doesn't +; provide any decibel information, or when the information is +; incorrect. + ; [General] ; auto-profiles = no | yes # Instead of defining all profiles manually, autogenerate @@ -55,6 +65,35 @@ ; skip-probe = no | yes # Skip probing for availability? If this is yes then this profile ; # will be assumed as working without probing. Makes initialization ; # a bit faster but only works if the card is really known well. +; +; [DecibelFix element] # Decibel fixes can be used to work around missing or incorrect dB +; # information from alsa. A decibel fix is a table that maps volume steps +; # to decibel values for one volume element. The "element" part in the +; # section title is the name of the volume element. +; # +; # NOTE: This feature is meant just as a help for figuring out the correct +; # decibel values. Pulseaudio is not the correct place to maintain the +; # decibel mappings! +; # +; # If you need this feature, then you should make sure that when you have +; # the correct values figured out, the alsa driver developers get informed +; # too, so that they can fix the driver. +; +; db-values = ... # The option value consists of pairs of step numbers and decibel values. +; # The pairs are separated with whitespace, and steps are separated from +; # the corresponding decibel values with a colon. The values must be in an +; # increasing order. Here's an example of a valid string: +; # +; # "0:-40.50 1:-38.70 3:-33.00 11:0" +; # +; # The lowest step imposes a lower limit for hardware volume and the +; # highest step correspondingly imposes a higher limit. That means that +; # that the mixer will never be set outside those values - the rest of the +; # volume scale is done using software volume. +; # +; # As can be seen in the example, you don't need to specify a dB value for +; # each step. The dB values for skipped steps will be linearly interpolated +; # using the nearest steps that are given. [General] auto-profiles = yes @@ -63,14 +102,14 @@ auto-profiles = yes device-strings = hw:%f channel-map = mono paths-output = analog-output analog-output-speaker analog-output-desktop-speaker analog-output-headphones analog-output-headphones-2 analog-output-mono analog-output-lfe-on-mono -paths-input = analog-input analog-input-mic analog-input-linein analog-input-aux analog-input-video analog-input-tvtuner analog-input-fm analog-input-mic-line +paths-input = analog-input-front-mic analog-input-rear-mic analog-input-internal-mic analog-input-dock-mic analog-input analog-input-mic analog-input-linein analog-input-aux analog-input-video analog-input-tvtuner analog-input-fm analog-input-mic-line priority = 1 [Mapping analog-stereo] device-strings = front:%f hw:%f channel-map = left,right paths-output = analog-output analog-output-speaker analog-output-desktop-speaker analog-output-headphones analog-output-headphones-2 analog-output-mono analog-output-lfe-on-mono -paths-input = analog-input analog-input-mic analog-input-linein analog-input-aux analog-input-video analog-input-tvtuner analog-input-fm analog-input-mic-line +paths-input = analog-input-front-mic analog-input-rear-mic analog-input-internal-mic analog-input-dock-mic analog-input analog-input-mic analog-input-linein analog-input-aux analog-input-video analog-input-tvtuner analog-input-fm analog-input-mic-line priority = 10 [Mapping analog-surround-40] diff --git a/src/modules/alsa/mixer/profile-sets/native-instruments-traktor-audio10.conf b/src/modules/alsa/mixer/profile-sets/native-instruments-traktor-audio10.conf new file mode 100644 index 00000000..4deb65da --- /dev/null +++ b/src/modules/alsa/mixer/profile-sets/native-instruments-traktor-audio10.conf @@ -0,0 +1,131 @@ +# This file is part of PulseAudio. +# +# PulseAudio is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation; either version 2.1 of the +# License, or (at your option) any later version. +# +# PulseAudio 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 Lesser General Public License +# along with PulseAudio; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +; Native Instruments Audio 10 DJ +; +; This card has five stereo pairs of input and five stereo pairs of +; output +; +; We knowingly only define a subset of the theoretically possible +; mapping combinations as profiles here. +; +; See default.conf for an explanation on the directives used here. + +[General] +auto-profiles = no + +[Mapping analog-stereo-out-main] +description = Analog Stereo Main +device-strings = hw:%f,0,0 +channel-map = left,right + +[Mapping analog-stereo-out-a] +description = Analog Stereo Channel A +device-strings = hw:%f,0,1 +channel-map = left,right +direction = output + +[Mapping analog-stereo-out-b] +description = Analog Stereo Channel B +device-strings = hw:%f,0,1 +channel-map = left,right +direction = output + +[Mapping analog-stereo-out-c] +description = Analog Stereo Channel C +device-strings = hw:%f,0,2 +channel-map = left,right +direction = output + +[Mapping analog-stereo-out-d] +description = Analog Stereo Channel D +device-strings = hw:%f,0,3 +channel-map = left,right +direction = output + +[Mapping analog-stereo-in-main] +description = Analog Stereo Main +device-strings = hw:%f,0,0 +channel-map = left,right + +[Mapping analog-stereo-in-a] +description = Analog Stereo Channel A +device-strings = hw:%f,0,1 +channel-map = left,right +direction = input + +[Mapping analog-stereo-in-b] +description = Analog Stereo Channel B +device-strings = hw:%f,0,1 +channel-map = left,right +direction = input + +[Mapping analog-stereo-in-c] +description = Analog Stereo Channel C +device-strings = hw:%f,0,2 +channel-map = left,right +direction = input + +[Mapping analog-stereo-in-d] +description = Analog Stereo Channel D +device-strings = hw:%f,0,3 +channel-map = left,right +direction = input + + + + +[Profile output:analog-stereo-all+input:analog-stereo-all] +description = Analog Stereo Duplex Channels Main, A, B, C, D +output-mappings = analog-stereo-out-main analog-stereo-out-a analog-stereo-out-b analog-stereo-out-c analog-stereo-out-d +input-mappings = analog-stereo-in-main analog-stereo-in-a analog-stereo-in-b analog-stereo-in-c analog-stereo-in-d +priority = 100 +skip-probe = yes + +[Profile output:analog-stereo-main+input:analog-stereo-main] +description = Analog Stereo Duplex Main +output-mappings = analog-stereo-out-main +input-mappings = analog-stereo-in-main +priority = 50 +skip-probe = yes + +[Profile output:analog-stereo-a+input:analog-stereo-a] +description = Analog Stereo Duplex Channel A +output-mappings = analog-stereo-out-a +input-mappings = analog-stereo-in-a +priority = 40 +skip-probe = yes + +[Profile output:analog-stereo-b+input:analog-stereo-b] +description = Analog Stereo Duplex Channel B +output-mappings = analog-stereo-out-b +input-mappings = analog-stereo-in-b +priority = 30 +skip-probe = yes + +[Profile output:analog-stereo-a+input:analog-stereo-c] +description = Analog Stereo Duplex Channel C +output-mappings = analog-stereo-out-c +input-mappings = analog-stereo-in-c +priority = 20 +skip-probe = yes + +[Profile output:analog-stereo-a+input:analog-stereo-d] +description = Analog Stereo Duplex Channel D +output-mappings = analog-stereo-out-d +input-mappings = analog-stereo-in-d +priority = 10 +skip-probe = yes diff --git a/src/modules/alsa/mixer/profile-sets/native-instruments-traktor-audio6.conf b/src/modules/alsa/mixer/profile-sets/native-instruments-traktor-audio6.conf new file mode 100644 index 00000000..48d9058b --- /dev/null +++ b/src/modules/alsa/mixer/profile-sets/native-instruments-traktor-audio6.conf @@ -0,0 +1,92 @@ +# This file is part of PulseAudio. +# +# PulseAudio is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation; either version 2.1 of the +# License, or (at your option) any later version. +# +# PulseAudio 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 Lesser General Public License +# along with PulseAudio; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +; Native Instruments Audio 6 DJ +; +; This card has three stereo pairs of input and three stereo pairs of +; output +; +; We knowingly only define a subset of the theoretically possible +; mapping combinations as profiles here. +; +; See default.conf for an explanation on the directives used here. + +[General] +auto-profiles = no + +[Mapping analog-stereo-out-main] +description = Analog Stereo Main +device-strings = hw:%f,0,0 +channel-map = left,right + +[Mapping analog-stereo-out-a] +description = Analog Stereo Channel A +device-strings = hw:%f,0,1 +channel-map = left,right +direction = output + +[Mapping analog-stereo-out-b] +description = Analog Stereo Channel B +device-strings = hw:%f,0,1 +channel-map = left,right +direction = output + +[Mapping analog-stereo-in-main] +description = Analog Stereo Main +device-strings = hw:%f,0,0 +channel-map = left,right + +[Mapping analog-stereo-in-a] +description = Analog Stereo Channel A +device-strings = hw:%f,0,1 +channel-map = left,right +direction = input + +[Mapping analog-stereo-in-b] +description = Analog Stereo Channel B +device-strings = hw:%f,0,1 +channel-map = left,right +direction = input + + + +[Profile output:analog-stereo-all+input:analog-stereo-all] +description = Analog Stereo Duplex Channels A, B (Headphones) +output-mappings = analog-stereo-out-main analog-stereo-out-a analog-stereo-out-b +input-mappings = analog-stereo-in-main analog-stereo-in-a analog-stereo-in-b +priority = 100 +skip-probe = yes + +[Profile output:analog-stereo-main+input:analog-stereo-main] +description = Analog Stereo Duplex Channel Main +output-mappings = analog-stereo-out-main +input-mappings = analog-stereo-in-main +priority = 50 +skip-probe = yes + +[Profile output:analog-stereo-a+input:analog-stereo-a] +description = Analog Stereo Duplex Channel A +output-mappings = analog-stereo-out-a +input-mappings = analog-stereo-in-a +priority = 40 +skip-probe = yes + +[Profile output:analog-stereo-b+input:analog-stereo-b] +description = Analog Stereo Duplex Channel B +output-mappings = analog-stereo-out-b +input-mappings = analog-stereo-in-b +priority = 30 +skip-probe = yes diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c index ebd2f8ae..e60aa5ef 100644 --- a/src/modules/alsa/module-alsa-card.c +++ b/src/modules/alsa/module-alsa-card.c @@ -65,7 +65,8 @@ PA_MODULE_USAGE( "tsched_buffer_watermark=<lower fill watermark> " "profile=<profile name> " "ignore_dB=<ignore dB information from the device?> " - "sync_volume=<syncronize sw and hw voluchanges in IO-thread?>"); + "sync_volume=<syncronize sw and hw voluchanges in IO-thread?> " + "profile_set=<profile set configuration file> "); static const char* const valid_modargs[] = { "name", @@ -88,6 +89,7 @@ static const char* const valid_modargs[] = { "profile", "ignore_dB", "sync_volume", + "profile_set", NULL }; @@ -328,6 +330,11 @@ int pa__init(pa_module *m) { fn = pa_udev_get_property(alsa_card_index, "PULSE_PROFILE_SET"); #endif + if (pa_modargs_get_value(ma, "profile_set", NULL)) { + pa_xfree(fn); + fn = pa_xstrdup(pa_modargs_get_value(ma, "profile_set", NULL)); + } + u->profile_set = pa_alsa_profile_set_new(fn, &u->core->default_channel_map); pa_xfree(fn); @@ -335,6 +342,7 @@ int pa__init(pa_module *m) { goto fail; pa_alsa_profile_set_probe(u->profile_set, u->device_id, &m->core->default_sample_spec, m->core->default_n_fragments, m->core->default_fragment_size_msec); + pa_alsa_profile_set_dump(u->profile_set); pa_card_new_data_init(&data); data.driver = __FILE__; @@ -393,6 +401,14 @@ int pa__init(pa_module *m) { if (reserve) pa_reserve_wrapper_unref(reserve); + if (!pa_hashmap_isempty(u->profile_set->decibel_fixes)) + pa_log_warn("Card %s uses decibel fixes (i.e. overrides the decibel information for some alsa volume elements). " + "Please note that this feature is meant just as a help for figuring out the correct decibel values. " + "Pulseaudio is not the correct place to maintain the decibel mappings! The fixed decibel values " + "should be sent to ALSA developers so that they can fix the driver. If it turns out that this feature " + "is abused (i.e. fixes are not pushed to ALSA), the decibel fix feature may be removed in some future " + "Pulseaudio version.", u->card->name); + return 0; fail: diff --git a/src/modules/alsa/module-alsa-sink.c b/src/modules/alsa/module-alsa-sink.c index 697fab45..465c8b9e 100644 --- a/src/modules/alsa/module-alsa-sink.c +++ b/src/modules/alsa/module-alsa-sink.c @@ -40,7 +40,8 @@ PA_MODULE_LOAD_ONCE(FALSE); PA_MODULE_USAGE( "name=<name of the sink, to be prefixed> " "sink_name=<name for the sink> " - "sink_properities=<properties for the sink> " + "sink_properties=<properties for the sink> " + "namereg_fail=<pa_namereg_register() fail parameter value> " "device=<ALSA device> " "device_id=<ALSA card index> " "format=<sample format> " @@ -64,6 +65,7 @@ static const char* const valid_modargs[] = { "name", "sink_name", "sink_properties", + "namereg_fail", "device", "device_id", "format", diff --git a/src/modules/alsa/module-alsa-source.c b/src/modules/alsa/module-alsa-source.c index 23da4185..90ffea57 100644 --- a/src/modules/alsa/module-alsa-source.c +++ b/src/modules/alsa/module-alsa-source.c @@ -65,6 +65,7 @@ PA_MODULE_USAGE( "name=<name for the source, to be prefixed> " "source_name=<name for the source> " "source_properties=<properties for the source> " + "namereg_fail=<pa_namereg_register() fail parameter value> " "device=<ALSA device> " "device_id=<ALSA card index> " "format=<sample format> " @@ -84,6 +85,7 @@ static const char* const valid_modargs[] = { "name", "source_name", "source_properties", + "namereg_fail", "device", "device_id", "format", |