alsa: when probing for profiles configure buffer/period sizes since some broken drive...
[profile/ivi/pulseaudio-panda.git] / src / modules / alsa / alsa-mixer.c
index 6f21e10..685169b 100644 (file)
@@ -479,6 +479,7 @@ static int element_get_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_chann
     snd_mixer_elem_t *me;
     snd_mixer_selem_channel_id_t c;
     pa_channel_position_mask_t mask = 0;
+    pa_volume_t max_channel_volume = PA_VOLUME_MUTED;
     unsigned k;
 
     pa_assert(m);
@@ -545,6 +546,9 @@ static int element_get_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_chann
             f = from_alsa_volume(value, e->min_volume, e->max_volume);
         }
 
+        if (f > max_channel_volume)
+            max_channel_volume = f;
+
         for (k = 0; k < cm->channels; k++)
             if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k]))
                 if (v->values[k] < f)
@@ -555,7 +559,7 @@ static int element_get_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_chann
 
     for (k = 0; k < cm->channels; k++)
         if (!(mask & PA_CHANNEL_POSITION_MASK(cm->map[k])))
-            v->values[k] = PA_VOLUME_NORM;
+            v->values[k] = max_channel_volume;
 
     return 0;
 }
@@ -677,6 +681,7 @@ static int element_set_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_chann
     snd_mixer_elem_t *me;
     snd_mixer_selem_channel_id_t c;
     pa_channel_position_mask_t mask = 0;
+    pa_volume_t max_channel_volume = PA_VOLUME_MUTED;
     unsigned k;
 
     pa_assert(m);
@@ -696,11 +701,21 @@ static int element_set_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_chann
     for (c = 0; c <= SND_MIXER_SCHN_LAST; c++) {
         int r;
         pa_volume_t f = PA_VOLUME_MUTED;
+        pa_bool_t found = FALSE;
 
         for (k = 0; k < cm->channels; k++)
-            if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k]))
+            if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k])) {
+                found = TRUE;
                 if (v->values[k] > f)
                     f = v->values[k];
+            }
+
+        if (!found) {
+            /* Hmm, so this channel does not exist in the volume
+             * struct, so let's bind it to the overall max of the
+             * volume. */
+            f = pa_cvolume_max(v);
+        }
 
         if (e->has_dB) {
             long value = to_alsa_dB(f);
@@ -756,6 +771,9 @@ static int element_set_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_chann
             f = from_alsa_volume(value, e->min_volume, e->max_volume);
         }
 
+        if (f > max_channel_volume)
+            max_channel_volume = f;
+
         for (k = 0; k < cm->channels; k++)
             if (e->masks[c][e->n_channels-1] & PA_CHANNEL_POSITION_MASK(cm->map[k]))
                 if (rv.values[k] < f)
@@ -766,7 +784,7 @@ static int element_set_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_chann
 
     for (k = 0; k < cm->channels; k++)
         if (!(mask & PA_CHANNEL_POSITION_MASK(cm->map[k])))
-            rv.values[k] = PA_VOLUME_NORM;
+            rv.values[k] = max_channel_volume;
 
     *v = rv;
     return 0;
@@ -911,7 +929,7 @@ static int element_zero_volume(pa_alsa_element *e, snd_mixer_t *m) {
 
 int pa_alsa_path_select(pa_alsa_path *p, snd_mixer_t *m) {
     pa_alsa_element *e;
-    int r;
+    int r = 0;
 
     pa_assert(m);
     pa_assert(p);
@@ -922,7 +940,6 @@ int pa_alsa_path_select(pa_alsa_path *p, snd_mixer_t *m) {
     PA_LLIST_FOREACH(e, p->elements) {
 
         switch (e->switch_use) {
-            case PA_ALSA_SWITCH_MUTE:
             case PA_ALSA_SWITCH_OFF:
                 r = element_set_switch(e, m, FALSE);
                 break;
@@ -931,6 +948,7 @@ int pa_alsa_path_select(pa_alsa_path *p, snd_mixer_t *m) {
                 r = element_set_switch(e, m, TRUE);
                 break;
 
+            case PA_ALSA_SWITCH_MUTE:
             case PA_ALSA_SWITCH_IGNORE:
             case PA_ALSA_SWITCH_SELECT:
                 r = 0;
@@ -942,7 +960,6 @@ int pa_alsa_path_select(pa_alsa_path *p, snd_mixer_t *m) {
 
         switch (e->volume_use) {
             case PA_ALSA_VOLUME_OFF:
-            case PA_ALSA_VOLUME_MERGE:
                 r = element_mute_volume(e, m);
                 break;
 
@@ -950,6 +967,7 @@ int pa_alsa_path_select(pa_alsa_path *p, snd_mixer_t *m) {
                 r = element_zero_volume(e, m);
                 break;
 
+            case PA_ALSA_VOLUME_MERGE:
             case PA_ALSA_VOLUME_IGNORE:
                 r = 0;
                 break;
@@ -1831,7 +1849,12 @@ pa_alsa_path* pa_alsa_path_new(const char *fname, pa_alsa_direction_t direction)
     items[1].data = &p->description;
     items[2].data = &p->name;
 
-    fn = pa_maybe_prefix_path(fname, PA_ALSA_PATHS_DIR);
+    fn = pa_maybe_prefix_path(fname,
+#if defined(__linux__) && !defined(__OPTIMIZE__)
+                              pa_run_from_build_tree() ? PA_BUILDDIR "/modules/alsa/mixer/paths/" :
+#endif
+                              PA_ALSA_PATHS_DIR);
+
     r = pa_config_parse(fn, NULL, items, p);
     pa_xfree(fn);
 
@@ -2820,9 +2843,9 @@ static int mapping_verify(pa_alsa_mapping *m, const pa_channel_map *bonus) {
 
     if (bonus) {
         if (pa_channel_map_equal(&m->channel_map, bonus))
-            m->priority += 5000;
+            m->priority += 50;
         else if (m->channel_map.channels == bonus->channels)
-            m->priority += 4000;
+            m->priority += 30;
     }
 
     return 0;
@@ -2866,7 +2889,7 @@ static void profile_set_add_auto_pair(
     else
         name = pa_sprintf_malloc("input:%s", n->name);
 
-    if ((p = pa_hashmap_get(ps->profiles, name))) {
+    if (pa_hashmap_get(ps->profiles, name)) {
         pa_xfree(name);
         return;
     }
@@ -2930,7 +2953,7 @@ static int profile_verify(pa_alsa_profile *p) {
             char **in;
             pa_bool_t duplicate = FALSE;
 
-            for (in = p->output_mapping_names; *in; in++)
+            for (in = name + 1; *in; in++)
                 if (pa_streq(*name, *in)) {
                     duplicate = TRUE;
                     break;
@@ -2945,6 +2968,9 @@ static int profile_verify(pa_alsa_profile *p) {
             }
 
             pa_idxset_put(p->output_mappings, m, NULL);
+
+            if (p->supported)
+                m->supported++;
         }
 
         pa_xstrfreev(p->output_mapping_names);
@@ -2963,7 +2989,7 @@ static int profile_verify(pa_alsa_profile *p) {
             char **in;
             pa_bool_t duplicate = FALSE;
 
-            for (in = p->input_mapping_names; *in; in++)
+            for (in = name + 1; *in; in++)
                 if (pa_streq(*name, *in)) {
                     duplicate = TRUE;
                     break;
@@ -2978,6 +3004,9 @@ static int profile_verify(pa_alsa_profile *p) {
             }
 
             pa_idxset_put(p->input_mappings, m, NULL);
+
+            if (p->supported)
+                m->supported++;
         }
 
         pa_xstrfreev(p->input_mapping_names);
@@ -3086,7 +3115,12 @@ pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel
     if (!fname)
         fname = "default.conf";
 
-    fn = pa_maybe_prefix_path(fname, PA_ALSA_PROFILE_SETS_DIR);
+    fn = pa_maybe_prefix_path(fname,
+#if defined(__linux__) && !defined(__OPTIMIZE__)
+                              pa_run_from_build_tree() ? PA_BUILDDIR "/modules/alsa/mixer/profile-sets/" :
+#endif
+                              PA_ALSA_PROFILE_SETS_DIR);
+
     r = pa_config_parse(fn, NULL, items, ps);
     pa_xfree(fn);
 
@@ -3111,7 +3145,13 @@ fail:
     return NULL;
 }
 
-void pa_alsa_profile_set_probe(pa_alsa_profile_set *ps, const char *dev_id, const pa_sample_spec *ss) {
+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) {
+
     void *state;
     pa_alsa_profile *p, *last = NULL;
     pa_alsa_mapping *m;
@@ -3126,6 +3166,7 @@ void pa_alsa_profile_set_probe(pa_alsa_profile_set *ps, const char *dev_id, cons
     PA_HASHMAP_FOREACH(p, ps->profiles, state) {
         pa_sample_spec try_ss;
         pa_channel_map try_map;
+        snd_pcm_uframes_t try_period_size, try_buffer_size;
         uint32_t idx;
 
         /* Is this already marked that it is supported? (i.e. from the config file) */
@@ -3179,13 +3220,18 @@ void pa_alsa_profile_set_probe(pa_alsa_profile_set *ps, const char *dev_id, cons
                 try_ss = *ss;
                 try_ss.channels = try_map.channels;
 
+                try_period_size =
+                    pa_usec_to_bytes(default_fragment_size_msec * PA_USEC_PER_MSEC, &try_ss) /
+                    pa_frame_size(&try_ss);
+                try_buffer_size = default_n_fragments * try_period_size;
+
                 if (!(m ->output_pcm = pa_alsa_open_by_template(
                               m->device_strings,
                               dev_id,
                               NULL,
                               &try_ss, &try_map,
                               SND_PCM_STREAM_PLAYBACK,
-                              NULL, NULL, 0, NULL, NULL,
+                              &try_period_size, &try_buffer_size, 0, NULL, NULL,
                               TRUE))) {
                     p->supported = FALSE;
                     break;
@@ -3203,13 +3249,18 @@ void pa_alsa_profile_set_probe(pa_alsa_profile_set *ps, const char *dev_id, cons
                 try_ss = *ss;
                 try_ss.channels = try_map.channels;
 
+                try_period_size =
+                    pa_usec_to_bytes(default_fragment_size_msec*PA_USEC_PER_MSEC, &try_ss) /
+                    pa_frame_size(&try_ss);
+                try_buffer_size = default_n_fragments * try_period_size;
+
                 if (!(m ->input_pcm = pa_alsa_open_by_template(
                               m->device_strings,
                               dev_id,
                               NULL,
                               &try_ss, &try_map,
                               SND_PCM_STREAM_CAPTURE,
-                              NULL, NULL, 0, NULL, NULL,
+                              &try_period_size, &try_buffer_size, 0, NULL, NULL,
                               TRUE))) {
                     p->supported = FALSE;
                     break;