]> code.delx.au - pulseaudio/blobdiff - src/modules/module-virtual-source.c
bluetooth: Fire DEVICE_CONNECTION_CHANGED in set_device_info_valid()
[pulseaudio] / src / modules / module-virtual-source.c
index 835cf3ce1f92cdec48dbd59c1b2b73de645e33af..6864a5ca9b1750ff24f28d4f9d92205987ff3371 100644 (file)
 #endif
 
 #include <stdio.h>
-#include <math.h>
 
 #include <pulse/xmalloc.h>
-#include <pulse/i18n.h>
 
+#include <pulsecore/i18n.h>
 #include <pulsecore/macro.h>
-#include <pulsecore/core-error.h>
 #include <pulsecore/namereg.h>
 #include <pulsecore/sink.h>
 #include <pulsecore/module.h>
-#include <pulsecore/core-rtclock.h>
 #include <pulsecore/core-util.h>
-#include <pulsecore/core-error.h>
 #include <pulsecore/modargs.h>
 #include <pulsecore/log.h>
-#include <pulsecore/thread.h>
-#include <pulsecore/thread-mq.h>
 #include <pulsecore/rtpoll.h>
 #include <pulsecore/sample-util.h>
 #include <pulsecore/ltdl-helper.h>
+#include <pulsecore/mix.h>
 
 #include "module-virtual-source-symdef.h"
 
 PA_MODULE_AUTHOR("Pierre-Louis Bossart");
 PA_MODULE_DESCRIPTION("Virtual source");
 PA_MODULE_VERSION(PACKAGE_VERSION);
-PA_MODULE_LOAD_ONCE(FALSE);
+PA_MODULE_LOAD_ONCE(false);
 PA_MODULE_USAGE(
         _("source_name=<name for the source> "
           "source_properties=<properties for the source> "
@@ -61,6 +56,8 @@ PA_MODULE_USAGE(
           "rate=<sample rate> "
           "channels=<number of channels> "
           "channel_map=<channel map> "
+          "use_volume_sharing=<yes or no> "
+          "force_flat_volume=<yes or no> "
         ));
 
 #define MEMBLOCKQ_MAXLENGTH (16*1024*1024)
@@ -70,14 +67,14 @@ struct userdata {
     pa_module *module;
 
     /* FIXME: Uncomment this and take "autoloaded" as a modarg if this is a filter */
-    /* pa_bool_t autoloaded; */
+    /* bool autoloaded; */
 
     pa_source *source;
     pa_source_output *source_output;
 
     pa_memblockq *memblockq;
 
-    pa_bool_t auto_desc;
+    bool auto_desc;
     unsigned channels;
 
     /* optional fields for uplink sink */
@@ -96,6 +93,8 @@ static const char* const valid_modargs[] = {
     "rate",
     "channels",
     "channel_map",
+    "use_volume_sharing",
+    "force_flat_volume",
     NULL
 };
 
@@ -128,7 +127,8 @@ static int sink_set_state_cb(pa_sink *s, pa_sink_state_t state) {
 
     if (state == PA_SINK_RUNNING) {
         /* need to wake-up source if it was suspended */
-        pa_source_suspend(u->source, FALSE, PA_SUSPEND_ALL);
+        pa_log_debug("Resuming source %s, because its uplink sink became active.", u->source->name);
+        pa_source_suspend(u->source, false, PA_SUSPEND_ALL);
 
         /* FIXME: if there's no client connected, the source will suspend
            and playback will be stuck. You'd want to prevent the source from
@@ -153,7 +153,6 @@ static void sink_update_requested_latency_cb(pa_sink *s) {
 
 }
 
-
 /* Called from I/O thread context */
 static void sink_request_rewind_cb(pa_sink *s) {
     struct userdata *u;
@@ -241,32 +240,9 @@ static void source_set_volume_cb(pa_source *s) {
         !PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output)))
         return;
 
-    /* FIXME, no volume control in source_output, set volume at the master */
-    pa_source_set_volume(u->source_output->source, &s->volume, TRUE);
-}
-
-static void source_get_volume_cb(pa_source *s) {
-    struct userdata *u;
-
-    pa_source_assert_ref(s);
-    pa_assert_se(u = s->userdata);
-
-    if (!PA_SOURCE_IS_LINKED(pa_source_get_state(s)) ||
-        !PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output)))
-        return;
-
-    /* FIXME, no volume control in source_output, get the info from the master */
-    pa_source_get_volume(u->source_output->source, TRUE);
-
-    if (pa_cvolume_equal(&s->volume,&u->source_output->source->volume))
-        /* no change */
-        return;
-
-    s->volume = u->source_output->source->volume;
-    pa_source_set_soft_volume(s, NULL);
+    pa_source_output_set_volume(u->source_output, &s->real_volume, s->save_volume, true);
 }
 
-
 /* Called from main context */
 static void source_set_mute_cb(pa_source *s) {
     struct userdata *u;
@@ -278,23 +254,7 @@ static void source_set_mute_cb(pa_source *s) {
         !PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output)))
         return;
 
-    /* FIXME, no volume control in source_output, set mute at the master */
-    pa_source_set_mute(u->source_output->source, TRUE, TRUE);
-}
-
-/* Called from main context */
-static void source_get_mute_cb(pa_source *s) {
-    struct userdata *u;
-
-    pa_source_assert_ref(s);
-    pa_assert_se(u = s->userdata);
-
-    if (!PA_SOURCE_IS_LINKED(pa_source_get_state(s)) ||
-        !PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output)))
-        return;
-
-    /* FIXME, no volume control in source_output, get the info from the master */
-    pa_source_get_mute(u->source_output->source, TRUE);
+    pa_source_output_set_mute(u->source_output, s->muted, s->save_muted);
 }
 
 /* Called from input thread context */
@@ -314,7 +274,7 @@ static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk)
 
     /* if uplink sink exists, pull data from there; simplify by using
        same length as chunk provided by source */
-    if(u->sink && (pa_sink_get_state(u->sink) == PA_SINK_RUNNING)) {
+    if (u->sink && (pa_sink_get_state(u->sink) == PA_SINK_RUNNING)) {
         pa_memchunk tchunk;
         size_t nbytes = chunk->length;
         pa_mix_info streams[2];
@@ -352,8 +312,7 @@ static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk)
         pa_assert( target_chunk.memblock );
 
         /* get target pointer */
-        target = (void*)((uint8_t*)pa_memblock_acquire(target_chunk.memblock)
-                         + target_chunk.index);
+        target = pa_memblock_acquire_chunk(&target_chunk);
 
         /* set-up mixing structure
            volume was taken care of in sink and source already */
@@ -374,7 +333,7 @@ static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk)
                chunk->length,          /* same length as input */
                (const pa_sample_spec *)&o->sample_spec, /* same sample spec for input and output */
                NULL,                   /* no volume information */
-               FALSE);                 /* no mute */
+               false);                 /* no mute */
 
         pa_memblock_release(target_chunk.memblock);
         pa_memblock_unref(tchunk.memblock); /* clean-up */
@@ -389,7 +348,6 @@ static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk)
         pa_source_post(u->source, chunk);
     }
 
-
 }
 
 /* Called from input thread context */
@@ -407,14 +365,6 @@ static void source_output_process_rewind_cb(pa_source_output *o, size_t nbytes)
 #endif
 }
 
-/* Called from output thread context */
-static int source_output_process_msg_cb(pa_msgobject *obj, int code, void *data, int64_t offset, pa_memchunk *chunk) {
-
-    /* FIXME, nothing to do here ? */
-
-    return pa_source_output_process_msg(obj, code, data, offset, chunk);
-}
-
 /* Called from output thread context */
 static void source_output_attach_cb(pa_source_output *o) {
     struct userdata *u;
@@ -484,21 +434,7 @@ static void source_output_kill_cb(pa_source_output *o) {
     pa_source_unref(u->source);
     u->source = NULL;
 
-    pa_module_unload_request(u->module, TRUE);
-}
-
-/* Called from main thread */
-static pa_bool_t source_output_may_move_to_cb(pa_source_output *o, pa_source *dest) {
-    struct userdata *u;
-
-    pa_source_output_assert_ref(o);
-    pa_assert_ctl_context();
-    pa_assert_se(u = o->userdata);
-
-    /* FIXME */
-    //return dest != u->source_input->source->monitor_source;
-
-    return TRUE;
+    pa_module_unload_request(u->module, true);
 }
 
 /* Called from main thread */
@@ -529,7 +465,6 @@ static void source_output_moving_cb(pa_source_output *o, pa_source *dest) {
     }
 }
 
-
 int pa__init(pa_module*m) {
     struct userdata *u;
     pa_sample_spec ss;
@@ -538,6 +473,8 @@ int pa__init(pa_module*m) {
     pa_source *master=NULL;
     pa_source_output_new_data source_output_data;
     pa_source_new_data source_data;
+    bool use_volume_sharing = true;
+    bool force_flat_volume = false;
 
     /* optional for uplink_sink */
     pa_sink_new_data sink_data;
@@ -565,15 +502,25 @@ int pa__init(pa_module*m) {
         goto fail;
     }
 
+    if (pa_modargs_get_value_boolean(ma, "use_volume_sharing", &use_volume_sharing) < 0) {
+        pa_log("use_volume_sharing= expects a boolean argument");
+        goto fail;
+    }
 
-    u = pa_xnew0(struct userdata, 1);
-    if (!u) {
-        pa_log("Failed to alloc userdata");
+    if (pa_modargs_get_value_boolean(ma, "force_flat_volume", &force_flat_volume) < 0) {
+        pa_log("force_flat_volume= expects a boolean argument");
+        goto fail;
+    }
+
+    if (use_volume_sharing && force_flat_volume) {
+        pa_log("Flat volume can't be forced when using volume sharing.");
         goto fail;
     }
+
+    u = pa_xnew0(struct userdata, 1);
     u->module = m;
     m->userdata = u;
-    u->memblockq = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, 0, pa_frame_size(&ss), 1, 1, 0, NULL);
+    u->memblockq = pa_memblockq_new("module-virtual-source memblockq", 0, MEMBLOCKQ_MAXLENGTH, 0, &ss, 1, 1, 0, NULL);
     if (!u->memblockq) {
         pa_log("Failed to create source memblockq.");
         goto fail;
@@ -605,9 +552,9 @@ int pa__init(pa_module*m) {
         pa_proplist_setf(source_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Virtual Source %s on %s", source_data.name, z ? z : master->name);
     }
 
-    u->source = pa_source_new(m->core, &source_data,
-                          PA_SOURCE_HW_MUTE_CTRL|PA_SOURCE_HW_VOLUME_CTRL|PA_SOURCE_DECIBEL_VOLUME|
-                          (master->flags & (PA_SOURCE_LATENCY|PA_SOURCE_DYNAMIC_LATENCY)));
+    u->source = pa_source_new(m->core, &source_data, (master->flags & (PA_SOURCE_LATENCY|PA_SOURCE_DYNAMIC_LATENCY))
+                                                     | (use_volume_sharing ? PA_SOURCE_SHARE_VOLUME_WITH_MASTER : 0));
+
     pa_source_new_data_done(&source_data);
 
     if (!u->source) {
@@ -618,10 +565,14 @@ int pa__init(pa_module*m) {
     u->source->parent.process_msg = source_process_msg_cb;
     u->source->set_state = source_set_state_cb;
     u->source->update_requested_latency = source_update_requested_latency_cb;
-    u->source->set_volume = source_set_volume_cb;
-    u->source->set_mute = source_set_mute_cb;
-    u->source->get_volume = source_get_volume_cb;
-    u->source->get_mute = source_get_mute_cb;
+    pa_source_set_set_mute_callback(u->source, source_set_mute_cb);
+    if (!use_volume_sharing) {
+        pa_source_set_set_volume_callback(u->source, source_set_volume_cb);
+        pa_source_enable_decibel_volume(u->source, true);
+    }
+    /* Normally this flag would be enabled automatically be we can force it. */
+    if (force_flat_volume)
+        u->source->flags |= PA_SOURCE_FLAT_VOLUME;
     u->source->userdata = u;
 
     pa_source_set_asyncmsgq(u->source, master->asyncmsgq);
@@ -630,10 +581,8 @@ int pa__init(pa_module*m) {
     pa_source_output_new_data_init(&source_output_data);
     source_output_data.driver = __FILE__;
     source_output_data.module = m;
-    source_output_data.source = master;
+    pa_source_output_new_data_set_source(&source_output_data, master, false);
     source_output_data.destination_source = u->source;
-    /* FIXME
-       source_output_data.flags = PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND; */
 
     pa_proplist_setf(source_output_data.proplist, PA_PROP_MEDIA_NAME, "Virtual Source Stream of %s", pa_proplist_gets(u->source->proplist, PA_PROP_DEVICE_DESCRIPTION));
     pa_proplist_sets(source_output_data.proplist, PA_PROP_MEDIA_ROLE, "filter");
@@ -646,14 +595,12 @@ int pa__init(pa_module*m) {
     if (!u->source_output)
         goto fail;
 
-    u->source_output->parent.process_msg = source_output_process_msg_cb;
     u->source_output->push = source_output_push_cb;
     u->source_output->process_rewind = source_output_process_rewind_cb;
     u->source_output->kill = source_output_kill_cb;
     u->source_output->attach = source_output_attach_cb;
     u->source_output->detach = source_output_detach_cb;
     u->source_output->state_change = source_output_state_change_cb;
-    u->source_output->may_move_to = source_output_may_move_to_cb;
     u->source_output->moving = source_output_moving_cb;
     u->source_output->userdata = u;
 
@@ -680,8 +627,9 @@ int pa__init(pa_module*m) {
             pa_proplist_setf(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Uplink Sink %s on %s", sink_data.name, z ? z : master->name);
         }
 
-        u->sink_memblockq = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, 0, pa_frame_size(&ss), 1, 1, 0, NULL);
+        u->sink_memblockq = pa_memblockq_new("module-virtual-source sink_memblockq", 0, MEMBLOCKQ_MAXLENGTH, 0, &ss, 1, 1, 0, NULL);
         if (!u->sink_memblockq) {
+            pa_sink_new_data_done(&sink_data);
             pa_log("Failed to create sink memblockq.");
             goto fail;
         }
@@ -710,6 +658,7 @@ int pa__init(pa_module*m) {
 
         pa_sink_put(u->sink);
     } else {
+        pa_sink_new_data_done(&sink_data);
         /* optional uplink sink not enabled */
         u->sink = NULL;
     }