]> code.delx.au - pulseaudio/blobdiff - src/pulsecore/source.c
core: always allow volume setting with single-channel pa_cvolume
[pulseaudio] / src / pulsecore / source.c
index 97a20b91ad62197857e401bb5965e945a39e7265..415c54bc35efe2be9e2798d7246eb7d10eb912b2 100644 (file)
@@ -46,7 +46,7 @@
 #define ABSOLUTE_MAX_LATENCY (10*PA_USEC_PER_SEC)
 #define DEFAULT_FIXED_LATENCY (250*PA_USEC_PER_MSEC)
 
-static PA_DEFINE_CHECK_TYPE(pa_source, pa_msgobject);
+PA_DEFINE_PUBLIC_CLASS(pa_source, pa_msgobject);
 
 static void source_free(pa_object *o);
 
@@ -181,7 +181,7 @@ pa_source* pa_source_new(
         pa_cvolume_reset(&data->volume, data->sample_spec.channels);
 
     pa_return_null_if_fail(pa_cvolume_valid(&data->volume));
-    pa_return_null_if_fail(data->volume.channels == data->sample_spec.channels);
+    pa_return_null_if_fail(pa_cvolume_compatible(&data->volume, &data->sample_spec));
 
     if (!data->muted_is_set)
         data->muted = FALSE;
@@ -205,6 +205,7 @@ pa_source* pa_source_new(
     s->core = core;
     s->state = PA_SOURCE_INIT;
     s->flags = flags;
+    s->priority = 0;
     s->suspend_cause = 0;
     s->name = pa_xstrdup(name);
     s->proplist = pa_proplist_copy(data->proplist);
@@ -212,6 +213,8 @@ pa_source* pa_source_new(
     s->module = data->module;
     s->card = data->card;
 
+    s->priority = pa_device_init_priority(s->proplist);
+
     s->sample_spec = data->sample_spec;
     s->channel_map = data->channel_map;
 
@@ -219,20 +222,17 @@ pa_source* pa_source_new(
     s->n_corked = 0;
     s->monitor_of = NULL;
 
-    s->virtual_volume = data->volume;
+    s->volume = data->volume;
     pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels);
     s->base_volume = PA_VOLUME_NORM;
     s->n_volume_steps = PA_VOLUME_NORM+1;
     s->muted = data->muted;
     s->refresh_volume = s->refresh_muted = FALSE;
 
-    s->fixed_latency = flags & PA_SOURCE_DYNAMIC_LATENCY ? 0 : DEFAULT_FIXED_LATENCY;
-
     reset_callbacks(s);
     s->userdata = NULL;
 
     s->asyncmsgq = NULL;
-    s->rtpoll = NULL;
 
     /* As a minor optimization we just steal the list instead of
      * copying it here */
@@ -265,6 +265,7 @@ pa_source* pa_source_new(
             &s->sample_spec,
             0);
 
+    s->thread_info.rtpoll = NULL;
     s->thread_info.outputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
     s->thread_info.soft_volume = s->soft_volume;
     s->thread_info.soft_muted = s->muted;
@@ -274,6 +275,7 @@ pa_source* pa_source_new(
     s->thread_info.requested_latency = 0;
     s->thread_info.min_latency = ABSOLUTE_MIN_LATENCY;
     s->thread_info.max_latency = ABSOLUTE_MAX_LATENCY;
+    s->thread_info.fixed_latency = flags & PA_SOURCE_DYNAMIC_LATENCY ? 0 : DEFAULT_FIXED_LATENCY;
 
     pa_assert_se(pa_idxset_put(core->sources, s, &s->index) >= 0);
 
@@ -336,15 +338,14 @@ static int source_set_state(pa_source *s, pa_source_state_t state) {
 
         /* We're suspending or resuming, tell everyone about it */
 
-        for (o = PA_SOURCE_OUTPUT(pa_idxset_first(s->outputs, &idx)); o; o = PA_SOURCE_OUTPUT(pa_idxset_next(s->outputs, &idx)))
+        PA_IDXSET_FOREACH(o, s->outputs, idx)
             if (s->state == PA_SOURCE_SUSPENDED &&
-                (o->flags & PA_SOURCE_OUTPUT_FAIL_ON_SUSPEND))
+                (o->flags & PA_SOURCE_OUTPUT_KILL_ON_SUSPEND))
                 pa_source_output_kill(o);
             else if (o->suspend)
                 o->suspend(o, state == PA_SOURCE_SUSPENDED);
     }
 
-
     return 0;
 }
 
@@ -357,7 +358,6 @@ void pa_source_put(pa_source *s) {
 
     /* The following fields must be initialized properly when calling _put() */
     pa_assert(s->asyncmsgq);
-    pa_assert(s->rtpoll);
     pa_assert(s->thread_info.min_latency <= s->thread_info.max_latency);
 
     /* Generally, flags should be initialized via pa_source_new(). As
@@ -372,7 +372,7 @@ void pa_source_put(pa_source *s) {
 
     pa_assert((s->flags & PA_SOURCE_HW_VOLUME_CTRL) || (s->base_volume == PA_VOLUME_NORM && s->flags & PA_SOURCE_DECIBEL_VOLUME));
     pa_assert(!(s->flags & PA_SOURCE_DECIBEL_VOLUME) || s->n_volume_steps == PA_VOLUME_NORM+1);
-    pa_assert(!(s->flags & PA_SOURCE_DYNAMIC_LATENCY) == (s->fixed_latency != 0));
+    pa_assert(!(s->flags & PA_SOURCE_DYNAMIC_LATENCY) == (s->thread_info.fixed_latency != 0));
 
     pa_assert_se(source_set_state(s, PA_SOURCE_IDLE) == 0);
 
@@ -464,20 +464,34 @@ static void source_free(pa_object *o) {
     pa_xfree(s);
 }
 
-/* Called from main context */
+/* Called from main context, and not while the IO thread is active, please */
 void pa_source_set_asyncmsgq(pa_source *s, pa_asyncmsgq *q) {
-    pa_assert_ctl_context();
     pa_source_assert_ref(s);
+    pa_assert_ctl_context();
 
     s->asyncmsgq = q;
 }
 
-/* Called from main context */
-void pa_source_set_rtpoll(pa_source *s, pa_rtpoll *p) {
+/* Called from main context, and not while the IO thread is active, please */
+void pa_source_update_flags(pa_source *s, pa_source_flags_t mask, pa_source_flags_t value) {
+    pa_source_assert_ref(s);
     pa_assert_ctl_context();
+
+    if (mask == 0)
+        return;
+
+    /* For now, allow only a minimal set of flags to be changed. */
+    pa_assert((mask & ~(PA_SOURCE_DYNAMIC_LATENCY|PA_SOURCE_LATENCY)) == 0);
+
+    s->flags = (s->flags & ~mask) | (value & mask);
+}
+
+/* Called from IO context, or before _put() from main context */
+void pa_source_set_rtpoll(pa_source *s, pa_rtpoll *p) {
     pa_source_assert_ref(s);
+    pa_source_assert_io_context(s);
 
-    s->rtpoll = p;
+    s->thread_info.rtpoll = p;
 }
 
 /* Called from main context */
@@ -574,7 +588,7 @@ void pa_source_move_all_finish(pa_source *s, pa_queue *q, pa_bool_t save) {
 
     while ((o = PA_SOURCE_OUTPUT(pa_queue_pop(q)))) {
         if (pa_source_output_finish_move(o, s, save) < 0)
-            pa_source_output_kill(o);
+            pa_source_output_fail_move(o);
 
         pa_source_output_unref(o);
     }
@@ -590,10 +604,8 @@ void pa_source_move_all_fail(pa_queue *q) {
     pa_assert(q);
 
     while ((o = PA_SOURCE_OUTPUT(pa_queue_pop(q)))) {
-        if (pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FAIL], o) == PA_HOOK_OK) {
-            pa_source_output_kill(o);
-            pa_source_output_unref(o);
-        }
+        pa_source_output_fail_move(o);
+        pa_source_output_unref(o);
     }
 
     pa_queue_free(q, NULL, NULL);
@@ -742,31 +754,39 @@ pa_usec_t pa_source_get_latency_within_thread(pa_source *s) {
 }
 
 /* Called from main thread */
-void pa_source_set_volume(pa_source *s, const pa_cvolume *volume, pa_bool_t save) {
-    pa_cvolume old_virtual_volume;
-    pa_bool_t virtual_volume_changed;
+void pa_source_set_volume(
+        pa_source *s,
+        const pa_cvolume *volume,
+        pa_bool_t save) {
+
+    pa_bool_t real_changed;
+    pa_cvolume old_volume;
 
     pa_source_assert_ref(s);
     pa_assert_ctl_context();
     pa_assert(PA_SOURCE_IS_LINKED(s->state));
-    pa_assert(volume);
     pa_assert(pa_cvolume_valid(volume));
-    pa_assert(pa_cvolume_compatible(volume, &s->sample_spec));
+    pa_assert(volume->channels == 1 || pa_cvolume_compatible(volume, &s->sample_spec));
+
+    old_volume = s->volume;
 
-    old_virtual_volume = s->virtual_volume;
-    s->virtual_volume = *volume;
-    virtual_volume_changed = !pa_cvolume_equal(&old_virtual_volume, &s->virtual_volume);
-    s->save_volume = (!virtual_volume_changed && s->save_volume) || save;
+    if (pa_cvolume_compatible(volume, &s->sample_spec))
+        s->volume = *volume;
+    else
+        pa_cvolume_scale(&s->volume, pa_cvolume_max(volume));
+
+    real_changed = !pa_cvolume_equal(&old_volume, &s->volume);
+    s->save_volume = (!real_changed && s->save_volume) || save;
 
     if (s->set_volume) {
         pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels);
         s->set_volume(s);
     } else
-        s->soft_volume = s->virtual_volume;
+        s->soft_volume = s->volume;
 
     pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_VOLUME, NULL, 0, NULL) == 0);
 
-    if (virtual_volume_changed)
+    if (real_changed)
         pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
 }
 
@@ -774,12 +794,16 @@ void pa_source_set_volume(pa_source *s, const pa_cvolume *volume, pa_bool_t save
 void pa_source_set_soft_volume(pa_source *s, const pa_cvolume *volume) {
     pa_source_assert_ref(s);
     pa_assert_ctl_context();
-    pa_assert(volume);
+
+    if (!volume)
+        pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels);
+    else
+        s->soft_volume = *volume;
 
     if (PA_SOURCE_IS_LINKED(s->state))
         pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_VOLUME, NULL, 0, NULL) == 0);
     else
-        s->thread_info.soft_volume = *volume;
+        s->thread_info.soft_volume = s->soft_volume;
 }
 
 /* Called from main thread */
@@ -789,20 +813,22 @@ const pa_cvolume *pa_source_get_volume(pa_source *s, pa_bool_t force_refresh) {
     pa_assert(PA_SOURCE_IS_LINKED(s->state));
 
     if (s->refresh_volume || force_refresh) {
-        pa_cvolume old_virtual_volume = s->virtual_volume;
+        pa_cvolume old_volume;
+
+        old_volume = s->volume;
 
         if (s->get_volume)
             s->get_volume(s);
 
         pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_VOLUME, NULL, 0, NULL) == 0);
 
-        if (!pa_cvolume_equal(&old_virtual_volume, &s->virtual_volume)) {
+        if (!pa_cvolume_equal(&old_volume, &s->volume)) {
             s->save_volume = TRUE;
             pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
         }
     }
 
-    return &s->virtual_volume;
+    return &s->volume;
 }
 
 /* Called from main thread */
@@ -813,10 +839,10 @@ void pa_source_volume_changed(pa_source *s, const pa_cvolume *new_volume) {
 
     /* The source implementor may call this if the volume changed to make sure everyone is notified */
 
-    if (pa_cvolume_equal(&s->virtual_volume, new_volume))
+    if (pa_cvolume_equal(&s->volume, new_volume))
         return;
 
-    s->virtual_volume = *new_volume;
+    s->volume = *new_volume;
     s->save_volume = TRUE;
 
     pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
@@ -1041,7 +1067,7 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_
             if (pa_hashmap_remove(s->thread_info.outputs, PA_UINT32_TO_PTR(o->index)))
                 pa_source_output_unref(o);
 
-            pa_source_invalidate_requested_latency(s);
+            pa_source_invalidate_requested_latency(s, TRUE);
 
             return 0;
         }
@@ -1121,6 +1147,16 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_
             return 0;
         }
 
+        case PA_SOURCE_MESSAGE_GET_FIXED_LATENCY:
+
+            *((pa_usec_t*) userdata) = s->thread_info.fixed_latency;
+            return 0;
+
+        case PA_SOURCE_MESSAGE_SET_FIXED_LATENCY:
+
+            pa_source_set_fixed_latency_within_thread(s, (pa_usec_t) offset);
+            return 0;
+
         case PA_SOURCE_MESSAGE_GET_MAX_REWIND:
 
             *((size_t*) userdata) = s->thread_info.max_rewind;
@@ -1227,13 +1263,12 @@ pa_usec_t pa_source_get_requested_latency_within_thread(pa_source *s) {
     pa_source_assert_io_context(s);
 
     if (!(s->flags & PA_SOURCE_DYNAMIC_LATENCY))
-        return PA_CLAMP(s->fixed_latency, s->thread_info.min_latency, s->thread_info.max_latency);
+        return PA_CLAMP(s->thread_info.fixed_latency, s->thread_info.min_latency, s->thread_info.max_latency);
 
     if (s->thread_info.requested_latency_valid)
         return s->thread_info.requested_latency;
 
-    while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
-
+    PA_HASHMAP_FOREACH(o, s->thread_info.outputs, state)
         if (o->thread_info.requested_source_latency != (pa_usec_t) -1 &&
             (result == (pa_usec_t) -1 || result > o->thread_info.requested_source_latency))
             result = o->thread_info.requested_source_latency;
@@ -1296,18 +1331,18 @@ void pa_source_set_max_rewind(pa_source *s, size_t max_rewind) {
 }
 
 /* Called from IO thread */
-void pa_source_invalidate_requested_latency(pa_source *s) {
+void pa_source_invalidate_requested_latency(pa_source *s, pa_bool_t dynamic) {
     pa_source_output *o;
     void *state = NULL;
 
     pa_source_assert_ref(s);
     pa_source_assert_io_context(s);
 
-    if (!(s->flags & PA_SOURCE_DYNAMIC_LATENCY))
+    if ((s->flags & PA_SOURCE_DYNAMIC_LATENCY))
+        s->thread_info.requested_latency_valid = FALSE;
+    else if (dynamic)
         return;
 
-    s->thread_info.requested_latency_valid = FALSE;
-
     if (PA_SOURCE_IS_LINKED(s->thread_info.state)) {
 
         if (s->update_requested_latency)
@@ -1319,7 +1354,7 @@ void pa_source_invalidate_requested_latency(pa_source *s) {
     }
 
     if (s->monitor_of)
-        pa_sink_invalidate_requested_latency(s->monitor_of);
+        pa_sink_invalidate_requested_latency(s->monitor_of, dynamic);
 }
 
 /* Called from main thread */
@@ -1379,8 +1414,6 @@ void pa_source_get_latency_range(pa_source *s, pa_usec_t *min_latency, pa_usec_t
 
 /* Called from IO thread, and from main thread before pa_source_put() is called */
 void pa_source_set_latency_range_within_thread(pa_source *s, pa_usec_t min_latency, pa_usec_t max_latency) {
-    void *state = NULL;
-
     pa_source_assert_ref(s);
     pa_source_assert_io_context(s);
 
@@ -1394,18 +1427,23 @@ void pa_source_set_latency_range_within_thread(pa_source *s, pa_usec_t min_laten
               (s->flags & PA_SOURCE_DYNAMIC_LATENCY) ||
               s->monitor_of);
 
+    if (s->thread_info.min_latency == min_latency &&
+        s->thread_info.max_latency == max_latency)
+        return;
+
     s->thread_info.min_latency = min_latency;
     s->thread_info.max_latency = max_latency;
 
     if (PA_SOURCE_IS_LINKED(s->thread_info.state)) {
         pa_source_output *o;
+        void *state = NULL;
 
-        while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
+        PA_HASHMAP_FOREACH(o, s->thread_info.outputs, state)
             if (o->update_source_latency_range)
                 o->update_source_latency_range(o);
     }
 
-    pa_source_invalidate_requested_latency(s);
+    pa_source_invalidate_requested_latency(s, FALSE);
 }
 
 /* Called from main thread, before the source is put */
@@ -1413,7 +1451,10 @@ void pa_source_set_fixed_latency(pa_source *s, pa_usec_t latency) {
     pa_source_assert_ref(s);
     pa_assert_ctl_context();
 
-    pa_assert(pa_source_get_state(s) == PA_SOURCE_INIT);
+    if (s->flags & PA_SOURCE_DYNAMIC_LATENCY) {
+        pa_assert(latency == 0);
+        return;
+    }
 
     if (latency < ABSOLUTE_MIN_LATENCY)
         latency = ABSOLUTE_MIN_LATENCY;
@@ -1421,7 +1462,58 @@ void pa_source_set_fixed_latency(pa_source *s, pa_usec_t latency) {
     if (latency > ABSOLUTE_MAX_LATENCY)
         latency = ABSOLUTE_MAX_LATENCY;
 
-    s->fixed_latency = latency;
+    if (PA_SOURCE_IS_LINKED(s->state))
+        pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_FIXED_LATENCY, NULL, (int64_t) latency, NULL) == 0);
+    else
+        s->thread_info.fixed_latency = latency;
+}
+
+/* Called from main thread */
+pa_usec_t pa_source_get_fixed_latency(pa_source *s) {
+    pa_usec_t latency;
+
+    pa_source_assert_ref(s);
+    pa_assert_ctl_context();
+
+    if (s->flags & PA_SOURCE_DYNAMIC_LATENCY)
+        return 0;
+
+    if (PA_SOURCE_IS_LINKED(s->state))
+        pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_FIXED_LATENCY, &latency, 0, NULL) == 0);
+    else
+        latency = s->thread_info.fixed_latency;
+
+    return latency;
+}
+
+/* Called from IO thread */
+void pa_source_set_fixed_latency_within_thread(pa_source *s, pa_usec_t latency) {
+    pa_source_assert_ref(s);
+    pa_source_assert_io_context(s);
+
+    if (s->flags & PA_SOURCE_DYNAMIC_LATENCY) {
+        pa_assert(latency == 0);
+        return;
+    }
+
+    pa_assert(latency >= ABSOLUTE_MIN_LATENCY);
+    pa_assert(latency <= ABSOLUTE_MAX_LATENCY);
+
+    if (s->thread_info.fixed_latency == latency)
+        return;
+
+    s->thread_info.fixed_latency = latency;
+
+    if (PA_SOURCE_IS_LINKED(s->thread_info.state)) {
+        pa_source_output *o;
+        void *state = NULL;
+
+        PA_HASHMAP_FOREACH(o, s->thread_info.outputs, state)
+            if (o->update_source_fixed_latency)
+                o->update_source_fixed_latency(o);
+    }
+
+    pa_source_invalidate_requested_latency(s, FALSE);
 }
 
 /* Called from main thread */