]> code.delx.au - pulseaudio/blobdiff - src/pulsecore/sink.c
Sending translation for Chinese (Simplified)
[pulseaudio] / src / pulsecore / sink.c
index 1cce8e6b688295e442ea428f068f4fcce49b01c4..24fad34dd049897f4a8dc2866fd35d91c5a5bf6d 100644 (file)
@@ -52,7 +52,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_sink, pa_msgobject);
+PA_DEFINE_PUBLIC_CLASS(pa_sink, pa_msgobject);
 
 static void sink_free(pa_object *s);
 
@@ -236,6 +236,7 @@ pa_sink* pa_sink_new(
     s->core = core;
     s->state = PA_SINK_INIT;
     s->flags = flags;
+    s->priority = 0;
     s->suspend_cause = 0;
     s->name = pa_xstrdup(name);
     s->proplist = pa_proplist_copy(data->proplist);
@@ -243,6 +244,8 @@ pa_sink* pa_sink_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;
 
@@ -775,7 +778,7 @@ static unsigned fill_mix_info(pa_sink *s, size_t *length, pa_mix_info *info, uns
 /* Called from IO thread context */
 static void inputs_drop(pa_sink *s, pa_mix_info *info, unsigned n, pa_memchunk *result) {
     pa_sink_input *i;
-    void *state = NULL;
+    void *state;
     unsigned p = 0;
     unsigned n_unreffed = 0;
 
@@ -787,7 +790,7 @@ static void inputs_drop(pa_sink *s, pa_mix_info *info, unsigned n, pa_memchunk *
 
     /* We optimize for the case where the order of the inputs has not changed */
 
-    while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) {
+    PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state) {
         unsigned j;
         pa_mix_info* m = NULL;
 
@@ -881,8 +884,6 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
     pa_assert(pa_frame_aligned(length, &s->sample_spec));
     pa_assert(result);
 
-    pa_sink_ref(s);
-
     pa_assert(!s->thread_info.rewind_requested);
     pa_assert(s->thread_info.rewind_nbytes == 0);
 
@@ -893,6 +894,8 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
         return;
     }
 
+    pa_sink_ref(s);
+
     if (length <= 0)
         length = pa_frame_align(MIX_BUFFER_LENGTH, &s->sample_spec);
 
@@ -923,18 +926,16 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
 
         pa_sw_cvolume_multiply(&volume, &s->thread_info.soft_volume, &info[0].volume);
 
-        if (s->thread_info.soft_muted || !pa_cvolume_is_norm(&volume)) {
-            if (s->thread_info.soft_muted || pa_cvolume_is_muted(&volume)) {
-                pa_memblock_unref(result->memblock);
-                pa_silence_memchunk_get(&s->core->silence_cache,
-                                        s->core->mempool,
-                                        result,
-                                        &s->sample_spec,
-                                        result->length);
-            } else {
-                pa_memchunk_make_writable(result, 0);
-                pa_volume_memchunk(result, &s->sample_spec, &volume);
-            }
+        if (s->thread_info.soft_muted || pa_cvolume_is_muted(&volume)) {
+            pa_memblock_unref(result->memblock);
+            pa_silence_memchunk_get(&s->core->silence_cache,
+                                    s->core->mempool,
+                                    result,
+                                    &s->sample_spec,
+                                    result->length);
+        } else if (!pa_cvolume_is_norm(&volume)) {
+            pa_memchunk_make_writable(result, 0);
+            pa_volume_memchunk(result, &s->sample_spec, &volume);
         }
     } else {
         void *ptr;
@@ -970,8 +971,6 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) {
     pa_assert(target->length > 0);
     pa_assert(pa_frame_aligned(target->length, &s->sample_spec));
 
-    pa_sink_ref(s);
-
     pa_assert(!s->thread_info.rewind_requested);
     pa_assert(s->thread_info.rewind_nbytes == 0);
 
@@ -980,6 +979,8 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) {
         return;
     }
 
+    pa_sink_ref(s);
+
     length = target->length;
     block_size_max = pa_mempool_block_size_max(s->core->mempool);
     if (length > block_size_max)
@@ -1054,11 +1055,16 @@ void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) {
     pa_assert(target->length > 0);
     pa_assert(pa_frame_aligned(target->length, &s->sample_spec));
 
-    pa_sink_ref(s);
-
     pa_assert(!s->thread_info.rewind_requested);
     pa_assert(s->thread_info.rewind_nbytes == 0);
 
+    if (s->thread_info.state == PA_SINK_SUSPENDED) {
+        pa_silence_memchunk(target, &s->sample_spec);
+        return;
+    }
+
+    pa_sink_ref(s);
+
     l = target->length;
     d = 0;
     while (l > 0) {
@@ -1077,10 +1083,6 @@ void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) {
 
 /* Called from IO thread context */
 void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) {
-    pa_mix_info info[MAX_MIX_CHANNELS];
-    size_t length1st = length;
-    unsigned n;
-
     pa_sink_assert_ref(s);
     pa_sink_assert_io_context(s);
     pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
@@ -1088,81 +1090,24 @@ void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) {
     pa_assert(pa_frame_aligned(length, &s->sample_spec));
     pa_assert(result);
 
-    pa_sink_ref(s);
-
     pa_assert(!s->thread_info.rewind_requested);
     pa_assert(s->thread_info.rewind_nbytes == 0);
 
-    pa_assert(length > 0);
-
-    n = fill_mix_info(s, &length1st, info, MAX_MIX_CHANNELS);
-
-    if (n == 0) {
-        pa_silence_memchunk_get(&s->core->silence_cache,
-                                s->core->mempool,
-                                result,
-                                &s->sample_spec,
-                                length1st);
-    } else if (n == 1) {
-        pa_cvolume volume;
-
-        *result = info[0].chunk;
-        pa_memblock_ref(result->memblock);
-
-        if (result->length > length)
-            result->length = length;
-
-        pa_sw_cvolume_multiply(&volume, &s->thread_info.soft_volume, &info[0].volume);
-
-        if (s->thread_info.soft_muted || !pa_cvolume_is_norm(&volume)) {
-            if (s->thread_info.soft_muted || pa_cvolume_is_muted(&volume)) {
-                pa_memblock_unref(result->memblock);
-                pa_silence_memchunk_get(&s->core->silence_cache,
-                                        s->core->mempool,
-                                        result,
-                                        &s->sample_spec,
-                                        result->length);
-            } else {
-                pa_memchunk_make_writable(result, length);
-                pa_volume_memchunk(result, &s->sample_spec, &volume);
-            }
-        }
-    } else {
-        void *ptr;
-
-        result->index = 0;
-        result->memblock = pa_memblock_new(s->core->mempool, length);
-
-        ptr = pa_memblock_acquire(result->memblock);
-
-        result->length = pa_mix(info, n,
-                                (uint8_t*) ptr + result->index, length1st,
-                                &s->sample_spec,
-                                &s->thread_info.soft_volume,
-                                s->thread_info.soft_muted);
-
-        pa_memblock_release(result->memblock);
-    }
+    pa_sink_ref(s);
 
-    inputs_drop(s, info, n, result);
+    pa_sink_render(s, length, result);
 
     if (result->length < length) {
         pa_memchunk chunk;
-        size_t l, d;
+
         pa_memchunk_make_writable(result, length);
 
-        l = length - result->length;
-        d = result->index + result->length;
-        while (l > 0) {
-            chunk = *result;
-            chunk.index = d;
-            chunk.length = l;
+        chunk.memblock = result->memblock;
+        chunk.index = result->index + result->length;
+        chunk.length = length - result->length;
 
-            pa_sink_render_into(s, &chunk);
+        pa_sink_render_into_full(s, &chunk);
 
-            d += chunk.length;
-            l -= chunk.length;
-        }
         result->length = length;
     }
 
@@ -1380,9 +1325,14 @@ static void propagate_reference_volume(pa_sink *s) {
         pa_cvolume_remap(&remapped, &s->channel_map, &i->channel_map);
         pa_sw_cvolume_multiply(&i->volume, &remapped, &i->reference_ratio);
 
-        /* The reference volume changed, let's tell people so */
-        if (!pa_cvolume_equal(&old_volume, &i->volume))
+        /* The volume changed, let's tell people so */
+        if (!pa_cvolume_equal(&old_volume, &i->volume)) {
+
+            if (i->volume_changed)
+                i->volume_changed(i);
+
             pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
+        }
     }
 }
 
@@ -1390,7 +1340,7 @@ static void propagate_reference_volume(pa_sink *s) {
 void pa_sink_set_volume(
         pa_sink *s,
         const pa_cvolume *volume,
-        pa_bool_t sendmsg,
+        pa_bool_t send_msg,
         pa_bool_t save) {
 
     pa_cvolume old_reference_volume;
@@ -1400,8 +1350,11 @@ void pa_sink_set_volume(
     pa_assert_ctl_context();
     pa_assert(PA_SINK_IS_LINKED(s->state));
     pa_assert(!volume || pa_cvolume_valid(volume));
-    pa_assert(!volume || pa_cvolume_compatible(volume, &s->sample_spec));
     pa_assert(volume || (s->flags & PA_SINK_FLAT_VOLUME));
+    pa_assert(!volume || volume->channels == 1 || pa_cvolume_compatible(volume, &s->sample_spec));
+
+    /* As a special exception we accept mono volumes on all sinks --
+     * even on those with more complex channel maps */
 
     /* If volume is NULL we synchronize the sink's real and reference
      * volumes with the stream volumes. If it is not NULL we update
@@ -1411,7 +1364,10 @@ void pa_sink_set_volume(
 
     if (volume) {
 
-        s->reference_volume = *volume;
+        if (pa_cvolume_compatible(volume, &s->sample_spec))
+            s->reference_volume = *volume;
+        else
+            pa_cvolume_scale(&s->reference_volume, pa_cvolume_max(volume));
 
         if (s->flags & PA_SINK_FLAT_VOLUME) {
             /* OK, propagate this volume change back to the inputs */
@@ -1453,7 +1409,7 @@ void pa_sink_set_volume(
         s->soft_volume = s->real_volume;
 
     /* This tells the sink that soft and/or virtual volume changed */
-    if (sendmsg)
+    if (send_msg)
         pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_VOLUME, NULL, 0, NULL) == 0);
 
     if (reference_changed)
@@ -1522,8 +1478,13 @@ static void propagate_real_volume(pa_sink *s, const pa_cvolume *old_real_volume)
             pa_sw_cvolume_multiply(&i->volume, &remapped, &i->reference_ratio);
 
             /* Notify if something changed */
-            if (!pa_cvolume_equal(&old_volume, &i->volume))
+            if (!pa_cvolume_equal(&old_volume, &i->volume)) {
+
+                if (i->volume_changed)
+                    i->volume_changed(i);
+
                 pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
+            }
         }
     }
 
@@ -1770,10 +1731,14 @@ static void sync_input_volumes_within_thread(pa_sink *s) {
     pa_sink_assert_io_context(s);
 
     PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state) {
+        if (pa_atomic_load(&i->before_ramping_v))
+            i->thread_info.future_soft_volume = i->soft_volume;
+
         if (pa_cvolume_equal(&i->thread_info.soft_volume, &i->soft_volume))
             continue;
 
-        i->thread_info.soft_volume = i->soft_volume;
+        if (!pa_atomic_load(&i->before_ramping_v))
+            i->thread_info.soft_volume = i->soft_volume;
         pa_sink_input_request_rewind(i, 0, TRUE, FALSE, FALSE);
     }
 }
@@ -2685,3 +2650,48 @@ pa_bool_t pa_device_init_intended_roles(pa_proplist *p) {
 
     return FALSE;
 }
+
+unsigned pa_device_init_priority(pa_proplist *p) {
+    const char *s;
+    unsigned priority = 0;
+
+    pa_assert(p);
+
+    if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_CLASS))) {
+
+        if (pa_streq(s, "sound"))
+            priority += 9000;
+        else if (!pa_streq(s, "modem"))
+            priority += 1000;
+    }
+
+    if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_FORM_FACTOR))) {
+
+        if (pa_streq(s, "internal"))
+            priority += 900;
+        else if (pa_streq(s, "speaker"))
+            priority += 500;
+        else if (pa_streq(s, "headphone"))
+            priority += 400;
+    }
+
+    if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_BUS))) {
+
+        if (pa_streq(s, "pci"))
+            priority += 50;
+        else if (pa_streq(s, "usb"))
+            priority += 40;
+        else if (pa_streq(s, "bluetooth"))
+            priority += 30;
+    }
+
+    if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_PROFILE_NAME))) {
+
+        if (pa_startswith(s, "analog-"))
+            priority += 9;
+        else if (pa_startswith(s, "iec958-"))
+            priority += 8;
+    }
+
+    return priority;
+}