raster pushed a commit to branch master.

http://git.enlightenment.org/core/enlightenment.git/commit/?id=9444e5d55d783bdcc79e20998876d9b0cf6504c0

commit 9444e5d55d783bdcc79e20998876d9b0cf6504c0
Author: Carsten Haitzler (Rasterman) <ras...@rasterman.com>
Date:   Tue Mar 30 01:09:06 2021 +0100

    e mixer - add ability to monitor streams with vu meters too
    
    it's generic infra where you can get a general waveform (44.1khz
    stereo floats per strea) for playback (sink inputs), output (sinks)
    and inputs (sources). the emix gui will put a vu meter (actually it's
    not a vbu meter - it's a peak sample measure over any frame period)
    with a progress bar there. very useful now.
    
    @feat
---
 src/modules/mixer/backend.c                       |   2 +-
 src/modules/mixer/emixer.c                        | 228 ++++++++++++++-
 src/modules/mixer/lib/backends/alsa/alsa.c        |   5 +-
 src/modules/mixer/lib/backends/pulseaudio/pulse.c | 338 +++++++++++++++++++++-
 src/modules/mixer/lib/emix.c                      |  38 ++-
 src/modules/mixer/lib/emix.h                      |  27 +-
 6 files changed, 621 insertions(+), 17 deletions(-)

diff --git a/src/modules/mixer/backend.c b/src/modules/mixer/backend.c
index 559fb2514..b08693dc8 100644
--- a/src/modules/mixer/backend.c
+++ b/src/modules/mixer/backend.c
@@ -1248,7 +1248,7 @@ backend_shutdown(void)
      }
 
 
-   emix_event_callback_del(_events_cb);
+   emix_event_callback_del(_events_cb, NULL);
    emix_shutdown();
    emix_config_shutdown();
 
diff --git a/src/modules/mixer/emixer.c b/src/modules/mixer/emixer.c
index 2ddf094ff..e9554818e 100644
--- a/src/modules/mixer/emixer.c
+++ b/src/modules/mixer/emixer.c
@@ -22,6 +22,123 @@ Eina_List *source_list = NULL, *sink_input_list = NULL, 
*sink_list = NULL, *card
 
 //////////////////////////////////////////////////////////////////////////////
 
+typedef struct _Mon_Data
+{
+   Emix_Sink *sink;
+   Emix_Sink_Input *input;
+   Emix_Source *source;
+   Evas_Object *fr;
+   Evas_Object *vu;
+   Ecore_Animator *animator;
+   float samp_max;
+   int mon_skips;
+   int mon_update;
+   int mon_samps;
+} Mon_Data;
+
+static Eina_List *_monitor_data_list = NULL;
+
+static Eina_Bool
+_cb_emix_monitor_update(void *data)
+{
+   Mon_Data *md = data;
+
+   if (md->mon_update == 0)
+     {
+        md->mon_skips++;
+        if (md->mon_skips > 5)
+          {
+             elm_progressbar_value_set(md->vu, 0.0);
+             md->animator = NULL;
+             return EINA_FALSE;
+          }
+        return EINA_TRUE;
+     }
+   elm_progressbar_value_set(md->vu, md->samp_max);
+   md->mon_update = 0;
+   md->samp_max = 0;
+   md->mon_skips = 0;
+   md->mon_samps = 0;
+   return EINA_TRUE;
+}
+
+static void
+_cb_emix_sink_monitor_event(void *data, enum Emix_Event event, void 
*event_info)
+{
+   Mon_Data *md = data;
+   Emix_Sink *sink = event_info;
+
+   if (sink != md->sink) return;
+   if (event == EMIX_SINK_MONITOR_EVENT)
+     {
+        unsigned int i, num = sink->mon_num * 2;
+        float samp, max = 0.0;
+
+        for (i = 0; i < num; i++)
+          {
+             samp = fabs(sink->mon_buf[i]);
+             if (samp > max) max = samp;
+          }
+        md->mon_samps += num;
+        if (md->samp_max < max) md->samp_max = max;
+        md->mon_update++;
+        if (!md->animator)
+          md->animator = ecore_animator_add(_cb_emix_monitor_update, md);
+     }
+}
+
+static void
+_cb_emix_sink_input_monitor_event(void *data, enum Emix_Event event, void 
*event_info)
+{
+   Mon_Data *md = data;
+   Emix_Sink_Input *input = event_info;
+
+   if (input != md->input) return;
+   if (event == EMIX_SINK_INPUT_MONITOR_EVENT)
+     {
+        unsigned int i, num = input->mon_num * 2;
+        float samp, max = 0.0;
+
+        for (i = 0; i < num; i++)
+          {
+             samp = fabs(input->mon_buf[i]);
+             if (samp > max) max = samp;
+          }
+        md->mon_samps += num;
+        if (md->samp_max < max) md->samp_max = max;
+        md->mon_update++;
+        if (!md->animator)
+          md->animator = ecore_animator_add(_cb_emix_monitor_update, md);
+     }
+}
+
+static void
+_cb_emix_source_monitor_event(void *data, enum Emix_Event event, void 
*event_info)
+{
+   Mon_Data *md = data;
+   Emix_Source *source = event_info;
+
+   if (source != md->source) return;
+   if (event == EMIX_SOURCE_MONITOR_EVENT)
+     {
+        unsigned int i, num = source->mon_num * 2;
+        float samp, max = 0.0;
+
+        for (i = 0; i < num; i++)
+          {
+             samp = fabs(source->mon_buf[i]);
+             if (samp > max) max = samp;
+          }
+        md->mon_samps += num;
+        if (md->samp_max < max) md->samp_max = max;
+        md->mon_update++;
+        if (!md->animator)
+          md->animator = ecore_animator_add(_cb_emix_monitor_update, md);
+     }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
 static void _emix_sink_volume_fill(Emix_Sink *sink, Evas_Object *bxv, 
Evas_Object *bx, Eina_Bool locked);
 static Evas_Object *_icon(Evas_Object *base, const char *name);
 
@@ -40,6 +157,12 @@ _backend_init(const char *back)
    return EINA_FALSE;
 }
 
+static char *
+_cb_vu_format_cb(double v EINA_UNUSED)
+{
+   return "";
+}
+
 static void
 _cb_sink_port_change(void *data,
                      Evas_Object *obj,
@@ -250,10 +373,11 @@ _emix_sink_volume_fill(Emix_Sink *sink, Evas_Object *fr, 
Evas_Object *bx, Eina_B
 static void
 _emix_sink_add(Emix_Sink *sink)
 {
-   Evas_Object *bxv, *bx, *lb, *hv, *fr, *sep;
+   Evas_Object *bxv, *bx, *lb, *hv, *fr, *sep, *vu;
    const Eina_List *l;
    Emix_Port *port;
    Eina_Bool locked = EINA_TRUE;
+   Mon_Data *md;
    unsigned int i;
 
    fr = elm_frame_add(win);
@@ -323,6 +447,14 @@ _emix_sink_add(Emix_Sink *sink)
    elm_box_pack_end(sink_box, fr);
    evas_object_show(fr);
 
+   vu = elm_progressbar_add(win);
+   elm_progressbar_unit_format_function_set(vu, _cb_vu_format_cb, NULL);
+   evas_object_data_set(fr, "vu", vu);
+   evas_object_size_hint_weight_set(vu, EVAS_HINT_EXPAND, 0.0);
+   evas_object_size_hint_align_set(vu, EVAS_HINT_FILL, 0.0);
+   elm_box_pack_end(sink_box, vu);
+   evas_object_show(vu);
+
    sep = elm_separator_add(win);
    evas_object_data_set(fr, "extra", sep);
    elm_separator_horizontal_set(sep, EINA_TRUE);
@@ -330,19 +462,44 @@ _emix_sink_add(Emix_Sink *sink)
    evas_object_size_hint_align_set(sep, EVAS_HINT_FILL, 0.0);
    elm_box_pack_end(sink_box, sep);
    evas_object_show(sep);
+
+   md = calloc(1, sizeof(Mon_Data));
+   if (md)
+     {
+        md->sink = sink;
+        md->fr = fr;
+        md->vu = vu;
+        emix_event_callback_add(_cb_emix_sink_monitor_event, md);
+        _monitor_data_list = eina_list_append(_monitor_data_list, md);
+     }
+   emix_sink_monitor(sink, EINA_TRUE);
 }
 
 static void
 _emix_sink_del(Emix_Sink *sink)
 {
-   Eina_List *l;
+   Eina_List *l, *ll;
    Evas_Object *fr;
+   Mon_Data *md;
+
+   emix_sink_monitor(sink, EINA_FALSE);
+   EINA_LIST_FOREACH_SAFE(_monitor_data_list, l, ll, md)
+     {
+        if (md->sink == sink)
+          {
+             emix_event_callback_del(_cb_emix_sink_monitor_event, md);
+             _monitor_data_list = eina_list_remove_list(_monitor_data_list, l);
+             if (md->animator) ecore_animator_del(md->animator);
+             free(md);
+          }
+     }
    EINA_LIST_FOREACH(sink_list, l, fr)
      {
         if (evas_object_data_get(fr, "sink") == sink)
           {
              sink_list = eina_list_remove_list(sink_list, l);
              evas_object_del(evas_object_data_get(fr, "extra"));
+             evas_object_del(evas_object_data_get(fr, "vu"));
              evas_object_del(fr);
              return;
           }
@@ -626,10 +783,11 @@ _emix_sink_input_volume_fill(Emix_Sink_Input *input, 
Evas_Object *fr, Evas_Objec
 static void
 _emix_sink_input_add(Emix_Sink_Input *input)
 {
-   Evas_Object *bxv, *bx, *lb, *hv, *ic, *fr, *sep;
+   Evas_Object *bxv, *bx, *lb, *hv, *ic, *fr, *sep, *vu;
    const Eina_List *l;
    Emix_Sink *sink;
    Eina_Bool locked = EINA_TRUE;
+   Mon_Data *md;
    unsigned int i;
 
    if (!input->sink) return;
@@ -717,6 +875,14 @@ _emix_sink_input_add(Emix_Sink_Input *input)
    elm_box_pack_end(sink_input_box, fr);
    evas_object_show(fr);
 
+   vu = elm_progressbar_add(win);
+   elm_progressbar_unit_format_function_set(vu, _cb_vu_format_cb, NULL);
+   evas_object_data_set(fr, "vu", vu);
+   evas_object_size_hint_weight_set(vu, EVAS_HINT_EXPAND, 0.0);
+   evas_object_size_hint_align_set(vu, EVAS_HINT_FILL, 0.0);
+   elm_box_pack_end(sink_input_box, vu);
+   evas_object_show(vu);
+
    sep = elm_separator_add(win);
    evas_object_data_set(fr, "extra", sep);
    elm_separator_horizontal_set(sep, EINA_TRUE);
@@ -724,19 +890,44 @@ _emix_sink_input_add(Emix_Sink_Input *input)
    evas_object_size_hint_align_set(sep, EVAS_HINT_FILL, 0.0);
    elm_box_pack_end(sink_input_box, sep);
    evas_object_show(sep);
+
+   md = calloc(1, sizeof(Mon_Data));
+   if (md)
+     {
+        md->input = input;
+        md->fr = fr;
+        md->vu = vu;
+        emix_event_callback_add(_cb_emix_sink_input_monitor_event, md);
+        _monitor_data_list = eina_list_append(_monitor_data_list, md);
+     }
+   emix_sink_input_monitor(input, EINA_TRUE);
 }
 
 static void
 _emix_sink_input_del(Emix_Sink_Input *input)
 {
-   Eina_List *l;
+   Eina_List *l, *ll;
    Evas_Object *fr;
+   Mon_Data *md;
+
+   emix_sink_input_monitor(input, EINA_FALSE);
+   EINA_LIST_FOREACH_SAFE(_monitor_data_list, l, ll, md)
+     {
+        if (md->input == input)
+          {
+             emix_event_callback_del(_cb_emix_sink_input_monitor_event, md);
+             _monitor_data_list = eina_list_remove_list(_monitor_data_list, l);
+             if (md->animator) ecore_animator_del(md->animator);
+             free(md);
+          }
+     }
    EINA_LIST_FOREACH(sink_input_list, l, fr)
      {
         if (evas_object_data_get(fr, "input") == input)
           {
              sink_input_list = eina_list_remove_list(sink_input_list, l);
              evas_object_del(evas_object_data_get(fr, "extra"));
+             evas_object_del(evas_object_data_get(fr, "vu"));
              evas_object_del(fr);
              return;
           }
@@ -1026,9 +1217,10 @@ _emix_source_volume_fill(Emix_Source *source, 
Evas_Object *fr, Evas_Object *bx,
 static void
 _emix_source_add(Emix_Source *source)
 {
-   Evas_Object *bxv, *bx, *fr, *lb, *sep;
+   Evas_Object *bxv, *bx, *fr, *lb, *sep, *vu;
    unsigned int i;
    Eina_Bool locked = EINA_TRUE;
+   Mon_Data *md;
 
    fr = elm_frame_add(win);
    evas_object_size_hint_weight_set(fr, EVAS_HINT_EXPAND, 1.0);
@@ -1081,6 +1273,14 @@ _emix_source_add(Emix_Source *source)
    elm_box_pack_end(source_box, fr);
    evas_object_show(fr);
 
+   vu = elm_progressbar_add(win);
+   elm_progressbar_unit_format_function_set(vu, _cb_vu_format_cb, NULL);
+   evas_object_data_set(fr, "vu", vu);
+   evas_object_size_hint_weight_set(vu, EVAS_HINT_EXPAND, 0.0);
+   evas_object_size_hint_align_set(vu, EVAS_HINT_FILL, 0.0);
+   elm_box_pack_end(source_box, vu);
+   evas_object_show(vu);
+
    sep = elm_separator_add(win);
    evas_object_data_set(fr, "extra", sep);
    elm_separator_horizontal_set(sep, EINA_TRUE);
@@ -1088,6 +1288,17 @@ _emix_source_add(Emix_Source *source)
    evas_object_size_hint_align_set(sep, EVAS_HINT_FILL, 0.0);
    elm_box_pack_end(source_box, sep);
    evas_object_show(sep);
+
+   md = calloc(1, sizeof(Mon_Data));
+   if (md)
+     {
+        md->source = source;
+        md->fr = fr;
+        md->vu = vu;
+        emix_event_callback_add(_cb_emix_source_monitor_event, md);
+        _monitor_data_list = eina_list_append(_monitor_data_list, md);
+     }
+   emix_source_monitor(source, EINA_TRUE);
 }
 
 static void
@@ -1101,6 +1312,7 @@ _emix_source_del(Emix_Source *source)
           {
              source_list = eina_list_remove_list(source_list, l);
              evas_object_del(evas_object_data_get(fr, "extra"));
+             evas_object_del(evas_object_data_get(fr, "vu"));
              evas_object_del(fr);
              return;
           }
@@ -1322,6 +1534,12 @@ _cb_emix_event(void *data EINA_UNUSED, enum Emix_Event 
event, void *event_info)
       case EMIX_CARD_CHANGED_EVENT:
         _emix_card_change(event_info);
         break;
+      case EMIX_SINK_MONITOR_EVENT:
+        break;
+      case EMIX_SINK_INPUT_MONITOR_EVENT:
+        break;
+      case EMIX_SOURCE_MONITOR_EVENT:
+        break;
       default:
         break;
      }
diff --git a/src/modules/mixer/lib/backends/alsa/alsa.c 
b/src/modules/mixer/lib/backends/alsa/alsa.c
index 894b1115e..465f08094 100644
--- a/src/modules/mixer/lib/backends/alsa/alsa.c
+++ b/src/modules/mixer/lib/backends/alsa/alsa.c
@@ -537,7 +537,10 @@ _alsa_backend =
    _alsa_sources_volume_set, /* source volume set */
    NULL, /* advanced options */
    NULL, /* card list */
-   NULL  /* card profile set */
+   NULL, /* card profile set */
+   NULL, /* sink monitor set */
+   NULL, /* sink input monitor set */
+   NULL /* ssource monitor set */
 };
 
 E_API Emix_Backend *
diff --git a/src/modules/mixer/lib/backends/pulseaudio/pulse.c 
b/src/modules/mixer/lib/backends/pulseaudio/pulse.c
index ac18a74f0..75caff59b 100644
--- a/src/modules/mixer/lib/backends/pulseaudio/pulse.c
+++ b/src/modules/mixer/lib/backends/pulseaudio/pulse.c
@@ -33,14 +33,21 @@ typedef struct _Context
 typedef struct _Sink
 {
    Emix_Sink base;
-   int idx;
+   int idx, monitor_idx;
    const char *pulse_name;
+   const char *monitor_source_name;
+   int mon_count;
+   pa_stream *mon_stream;
+   Eina_Bool running : 1;
 } Sink;
 
 typedef struct _Sink_Input
 {
    Emix_Sink_Input base;
-   int idx;
+   int idx, sink_idx;
+   int mon_count;
+   pa_stream *mon_stream;
+   Eina_Bool running : 1;
 } Sink_Input;
 
 typedef struct _Source
@@ -48,6 +55,8 @@ typedef struct _Source
    Emix_Source base;
    int idx;
    const char *pulse_name;
+   int mon_count;
+   pa_stream *mon_stream;
 } Source;
 
 typedef struct _Profile
@@ -65,6 +74,13 @@ typedef struct _Card
 static Context *ctx = NULL;
 extern pa_mainloop_api functable;
 
+static void _sink_monitor_begin(Sink *sink);
+static void _sink_monitor_end(Sink *sink);
+static void _sink_input_monitor_begin(Sink_Input *i);
+static void _sink_input_monitor_end(Sink_Input *i);
+static void _source_monitor_begin(Source *s);
+static void _source_monitor_end(Source *s);
+
 static pa_cvolume
 _emix_volume_convert(const Emix_Volume *volume)
 {
@@ -116,6 +132,8 @@ _sink_del(Sink *sink)
    free(sink->base.volume.channel_names);
    eina_stringshare_del(sink->base.name);
    eina_stringshare_del(sink->pulse_name);
+   eina_stringshare_del(sink->monitor_source_name);
+   if (sink->mon_stream) pa_stream_disconnect(sink->mon_stream);
    free(sink);
 }
 
@@ -131,6 +149,7 @@ _sink_input_del(Sink_Input *input)
    free(input->base.volume.channel_names);
    eina_stringshare_del(input->base.name);
    eina_stringshare_del(input->base.icon);
+   if (input->mon_stream) pa_stream_disconnect(input->mon_stream);
    free(input);
 }
 
@@ -165,6 +184,30 @@ _card_del(Card *card)
    free(card);
 }
 
+static void
+_sink_state_running_set(Sink *sink, Eina_Bool running)
+{
+   if (running)
+     {
+        if ((!sink->running) && (sink->mon_count > 0))
+          {
+             sink->running = running;
+             _sink_monitor_begin(sink);
+             return;
+          }
+     }
+   else
+     {
+        if ((sink->running) && (sink->mon_count > 0))
+          {
+             sink->running = running;
+             _sink_monitor_end(sink);
+             return;
+          }
+     }
+   sink->running = running;
+}
+
 static void
 _sink_cb(pa_context *c EINA_UNUSED, const pa_sink_info *info, int eol,
          void *userdata EINA_UNUSED)
@@ -190,6 +233,7 @@ _sink_cb(pa_context *c EINA_UNUSED, const pa_sink_info 
*info, int eol,
 
    sink = calloc(1, sizeof(Sink));
    sink->idx = info->index;
+   sink->monitor_idx = info->monitor_source;
    sink->pulse_name = eina_stringshare_add(info->name);
    sink->base.name = eina_stringshare_add(info->description);
    _pa_cvolume_convert(&info->volume, &sink->base.volume);
@@ -197,6 +241,7 @@ _sink_cb(pa_context *c EINA_UNUSED, const pa_sink_info 
*info, int eol,
    for (i = 0; i < sink->base.volume.channel_count; ++i)
      sink->base.volume.channel_names[i] = 
eina_stringshare_add(pa_channel_position_to_pretty_string(info->channel_map.map[i]));
    sink->base.mute = !!info->mute;
+   sink->monitor_source_name = eina_stringshare_add(info->monitor_source_name);
 
    for (i = 0; i < info->n_ports; i++)
      {
@@ -214,7 +259,8 @@ _sink_cb(pa_context *c EINA_UNUSED, const pa_sink_info 
*info, int eol,
         if (info->ports[i]->name == info->active_port->name)
            port->active = EINA_TRUE;
      }
-
+   if (info->state == PA_SINK_RUNNING) _sink_state_running_set(sink, 
EINA_TRUE);
+   else _sink_state_running_set(sink, EINA_FALSE);
    ctx->sinks = eina_list_append(ctx->sinks, sink);
    if (ctx->cb)
       ctx->cb((void *)ctx->userdata, EMIX_SINK_ADDED_EVENT,
@@ -297,6 +343,11 @@ _sink_changed_cb(pa_context *c EINA_UNUSED, const 
pa_sink_info *info, int eol,
            port->active = EINA_TRUE;
      }
 
+   sink->monitor_idx = info->monitor_source;
+
+   if (info->state == PA_SINK_RUNNING) _sink_state_running_set(sink, 
EINA_TRUE);
+   else _sink_state_running_set(sink, EINA_FALSE);
+
    if (ctx->cb)
      ctx->cb((void *)ctx->userdata, EMIX_SINK_CHANGED_EVENT,
                   (Emix_Sink *)sink);
@@ -357,6 +408,30 @@ _icon_from_properties(pa_proplist *l)
    return "audio-card";
 }
 
+static void
+_sink_input_state_running_set(Sink_Input *input, Eina_Bool running)
+{
+   if (running)
+     {
+        if ((!input->running) && (input->mon_count > 0))
+          {
+             input->running = running;
+             _sink_input_monitor_begin(input);
+             return;
+          }
+     }
+   else
+     {
+        if ((input->running) && (input->mon_count > 0))
+          {
+             input->running = running;
+             _sink_input_monitor_end(input);
+             return;
+          }
+     }
+   input->running = running;
+}
+
 static void
 _sink_input_cb(pa_context *c EINA_UNUSED, const pa_sink_input_info *info,
                int eol, void *userdata EINA_UNUSED)
@@ -387,6 +462,7 @@ _sink_input_cb(pa_context *c EINA_UNUSED, const 
pa_sink_input_info *info,
        info->name);
 
    input->idx = info->index;
+   input->sink_idx = info->sink;
 
    Eina_Strbuf *input_name;
 
@@ -421,6 +497,8 @@ _sink_input_cb(pa_context *c EINA_UNUSED, const 
pa_sink_input_info *info,
      {
         input->base.pid = atoi(t);
      }
+   if (!info->corked) _sink_input_state_running_set(input, EINA_TRUE);
+   else _sink_input_state_running_set(input, EINA_FALSE);
 
    if (ctx->cb)
      ctx->cb((void *)ctx->userdata, EMIX_SINK_INPUT_ADDED_EVENT,
@@ -469,6 +547,7 @@ _sink_input_changed_cb(pa_context *c EINA_UNUSED,
         ctx->inputs = eina_list_append(ctx->inputs, input);
      }
    input->idx = info->index;
+   input->sink_idx = info->sink;
    if (input->base.volume.channel_count != info->volume.channels)
      {
         for (i = 0; i < input->base.volume.channel_count; ++i)
@@ -493,6 +572,9 @@ _sink_input_changed_cb(pa_context *c EINA_UNUSED,
         input->base.pid = atoi(t);
      }
 
+   if (!info->corked) _sink_input_state_running_set(input, EINA_TRUE);
+   else _sink_input_state_running_set(input, EINA_FALSE);
+
    if (ctx->cb)
      ctx->cb((void *)ctx->userdata, EMIX_SINK_INPUT_CHANGED_EVENT,
              (Emix_Sink_Input *)input);
@@ -529,6 +611,7 @@ _source_cb(pa_context *c EINA_UNUSED, const pa_source_info 
*info,
 {
    Source *source;
    unsigned int i;
+   size_t len;
    EINA_SAFETY_ON_NULL_RETURN(ctx);
 
    if (eol < 0)
@@ -543,6 +626,13 @@ _source_cb(pa_context *c EINA_UNUSED, const pa_source_info 
*info,
    if (eol > 0)
       return;
 
+   len = strlen(info->name);
+   if (len > 8)
+     {
+        const char *s = info->name + len - 8;
+        if (!strcmp(s, ".monitor")) return;
+     }
+
    source = calloc(1, sizeof(Source));
    EINA_SAFETY_ON_NULL_RETURN(source);
 
@@ -600,7 +690,7 @@ _source_changed_cb(pa_context *c EINA_UNUSED,
         EINA_SAFETY_ON_NULL_RETURN(source);
         ctx->sources = eina_list_append(ctx->sources, source);
      }
-   source->idx= info->index;
+   source->idx = info->index;
    if (source->base.volume.channel_count != info->volume.channels)
      {
         for (i = 0; i < source->base.volume.channel_count; ++i)
@@ -1522,6 +1612,241 @@ _card_profile_set(Emix_Card *card, const Emix_Profile 
*profile)
    return EINA_TRUE;
 }
 
+static void
+_sink_mon_read(pa_stream *stream, size_t bytes EINA_UNUSED, void *data)
+{
+   Sink *s = data;
+   size_t rbytes;
+   const void *buf = NULL;
+
+   if (pa_stream_peek(stream, &buf, &rbytes) == 0)
+     {
+        if ((!buf) && (rbytes))
+          {
+             pa_stream_drop(stream);
+             return;
+          }
+        s->base.mon_num = (unsigned int)((rbytes / sizeof(float)) / 2);
+        s->base.mon_buf = buf;
+        if (ctx->cb)
+          ctx->cb((void *)ctx->userdata, EMIX_SINK_MONITOR_EVENT, s);
+        pa_stream_drop(stream);
+     }
+}
+
+static void
+_sink_monitor_begin(Sink *s)
+{
+   pa_sample_spec samp;
+   pa_buffer_attr attr;
+
+   if (pa_context_get_server_protocol_version(ctx->context) < 13) return;
+
+   pa_sample_spec_init(&samp);
+   samp.format = PA_SAMPLE_FLOAT32;
+   samp.rate = 44100;
+   samp.channels = 2;
+   memset(&attr, 0, sizeof(attr));
+   attr.fragsize = sizeof(float) * 4096;
+   attr.maxlength = (uint32_t) -1;
+
+   s->mon_stream = pa_stream_new(ctx->context, "__e_mon", &samp, NULL);
+   pa_stream_set_read_callback(s->mon_stream, _sink_mon_read, s);
+   pa_stream_connect_record(s->mon_stream, s->monitor_source_name,
+                            &attr,
+                            PA_STREAM_NOFLAGS
+                            | PA_STREAM_DONT_MOVE
+                            | PA_STREAM_ADJUST_LATENCY
+                            | PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND
+                           );
+}
+
+static void
+_sink_monitor_end(Sink *s)
+{
+   if (s->mon_stream)
+     {
+        if (s->mon_stream) pa_stream_disconnect(s->mon_stream);
+        s->mon_stream = NULL;
+     }
+}
+
+static void
+_sink_monitor_set(Emix_Sink *sink, Eina_Bool monitor)
+{
+   Sink *s = (Sink *)sink;
+   EINA_SAFETY_ON_NULL_RETURN(ctx);
+   if (monitor) s->mon_count++;
+   else s->mon_count--;
+   if (s->mon_count < 0) s->mon_count = 0;
+   if (s->mon_count == 1)
+     {
+        if (s->running) _sink_monitor_begin(s);
+     }
+   else if (s->mon_count == 0) _sink_monitor_end(s);
+}
+
+static void
+_sink_input_mon_read(pa_stream *stream, size_t bytes EINA_UNUSED, void *data)
+{
+   Sink_Input *i = data;
+   size_t rbytes;
+   const void *buf = NULL;
+
+   if (pa_stream_peek(stream, &buf, &rbytes) == 0)
+     {
+        if ((!buf) && (rbytes))
+          {
+             pa_stream_drop(stream);
+             return;
+          }
+        i->base.mon_num = (unsigned int)((rbytes / sizeof(float)) / 2);
+        i->base.mon_buf = buf;
+        if (ctx->cb)
+          ctx->cb((void *)ctx->userdata, EMIX_SINK_INPUT_MONITOR_EVENT, i);
+        pa_stream_drop(stream);
+     }
+}
+
+static void
+_sink_input_monitor_begin(Sink_Input *i)
+{
+   pa_sample_spec samp;
+   pa_buffer_attr attr;
+   char buf[16];
+   Eina_List *l;
+   Sink *s;
+   unsigned int mon_idx = 0;
+
+   if (pa_context_get_server_protocol_version(ctx->context) < 13) return;
+
+   pa_sample_spec_init(&samp);
+   samp.format = PA_SAMPLE_FLOAT32;
+   samp.rate = 44100;
+   samp.channels = 2;
+   memset(&attr, 0, sizeof(attr));
+   attr.fragsize = sizeof(float) * 4096;
+   attr.maxlength = (uint32_t) -1;
+
+   i->mon_stream = pa_stream_new(ctx->context, "__e_mon", &samp, NULL);
+   pa_stream_set_monitor_stream(i->mon_stream, i->idx);
+   pa_stream_set_read_callback(i->mon_stream, _sink_input_mon_read, i);
+   EINA_LIST_FOREACH(ctx->sinks, l, s)
+     {
+        if (i->sink_idx == s->idx)
+          {
+             mon_idx = s->monitor_idx;
+             break;
+          }
+     }
+   if (!l) return;
+   snprintf(buf, sizeof(buf), "%i", mon_idx);
+   pa_stream_connect_record(i->mon_stream, buf,
+                            &attr,
+                            PA_STREAM_NOFLAGS
+                            | PA_STREAM_DONT_MOVE
+                            | PA_STREAM_ADJUST_LATENCY
+                            | PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND
+                           );
+}
+
+static void
+_sink_input_monitor_end(Sink_Input *i)
+{
+   if (i->mon_stream)
+     {
+        if (i->mon_stream) pa_stream_disconnect(i->mon_stream);
+        i->mon_stream = NULL;
+     }
+}
+
+static void
+_sink_input_monitor_set(Emix_Sink_Input *input, Eina_Bool monitor)
+{
+   Sink_Input *i = (Sink_Input *)input;
+   EINA_SAFETY_ON_NULL_RETURN(ctx);
+   if (monitor) i->mon_count++;
+   else i->mon_count--;
+   if (i->mon_count < 0) i->mon_count = 0;
+   if (i->mon_count == 1)
+     {
+        if (i->running) _sink_input_monitor_begin(i);
+     }
+   else if (i->mon_count == 0) _sink_input_monitor_end(i);
+}
+
+static void
+_source_mon_read(pa_stream *stream, size_t bytes EINA_UNUSED, void *data)
+{
+   Source *s = data;
+   size_t rbytes;
+   const void *buf = NULL;
+
+   if (pa_stream_peek(stream, &buf, &rbytes) == 0)
+     {
+        if ((!buf) && (rbytes))
+          {
+             pa_stream_drop(stream);
+             return;
+          }
+        s->base.mon_num = (unsigned int)((rbytes / sizeof(float)) / 2);
+        s->base.mon_buf = buf;
+        if (ctx->cb)
+          ctx->cb((void *)ctx->userdata, EMIX_SOURCE_MONITOR_EVENT, s);
+        pa_stream_drop(stream);
+     }
+}
+
+static void
+_source_monitor_begin(Source *s)
+{
+   pa_sample_spec samp;
+   pa_buffer_attr attr;
+   char buf[16];
+
+   if (pa_context_get_server_protocol_version(ctx->context) < 13) return;
+
+   pa_sample_spec_init(&samp);
+   samp.format = PA_SAMPLE_FLOAT32;
+   samp.rate = 44100;
+   samp.channels = 2;
+   memset(&attr, 0, sizeof(attr));
+   attr.fragsize = sizeof(float) * 4096;
+   attr.maxlength = (uint32_t) -1;
+
+   s->mon_stream = pa_stream_new(ctx->context, "__e_mon", &samp, NULL);
+   pa_stream_set_read_callback(s->mon_stream, _source_mon_read, s);
+   snprintf(buf, sizeof(buf), "%i", s->idx);
+   pa_stream_connect_record(s->mon_stream, buf,
+                            &attr,
+                            PA_STREAM_NOFLAGS
+                            | PA_STREAM_DONT_MOVE
+                            | PA_STREAM_ADJUST_LATENCY
+                           );
+}
+
+static void
+_source_monitor_end(Source *s)
+{
+   if (s->mon_stream)
+     {
+        if (s->mon_stream) pa_stream_disconnect(s->mon_stream);
+        s->mon_stream = NULL;
+     }
+}
+
+static void
+_source_monitor_set(Emix_Source *source, Eina_Bool monitor)
+{
+   Source *s = (Source *)source;
+   EINA_SAFETY_ON_NULL_RETURN(ctx);
+   if (monitor) s->mon_count++;
+   else s->mon_count--;
+   if (s->mon_count < 0) s->mon_count = 0;
+   if (s->mon_count == 1) _source_monitor_begin(s);
+   else if (s->mon_count == 0) _source_monitor_end(s);
+}
+
 static Emix_Backend
 _pulseaudio_backend =
 {
@@ -1548,7 +1873,10 @@ _pulseaudio_backend =
    _source_volume_set,
    NULL,
    _cards_get,
-   _card_profile_set
+   _card_profile_set,
+   _sink_monitor_set,
+   _sink_input_monitor_set,
+   _source_monitor_set
 };
 
 E_API Emix_Backend *
diff --git a/src/modules/mixer/lib/emix.c b/src/modules/mixer/lib/emix.c
index 962e7e535..5fb83aa0e 100644
--- a/src/modules/mixer/lib/emix.c
+++ b/src/modules/mixer/lib/emix.c
@@ -419,7 +419,7 @@ emix_event_callback_add(Emix_Event_Cb cb, const void *data)
 }
 
 Eina_Bool
-emix_event_callback_del(Emix_Event_Cb cb)
+emix_event_callback_del(Emix_Event_Cb cb, const void *data)
 {
    struct Callback_Data *callback;
    Eina_List *l;
@@ -427,7 +427,7 @@ emix_event_callback_del(Emix_Event_Cb cb)
 
    EINA_LIST_FOREACH(ctx->callbacks, l, callback)
      {
-        if (callback->cb == cb)
+        if ((callback->cb == cb) && (callback->data == data))
           {
              ctx->callbacks = eina_list_remove_list(ctx->callbacks, l);
              free(callback);
@@ -456,3 +456,37 @@ emix_card_profile_set(Emix_Card *card, Emix_Profile 
*profile)
 
    return ctx->loaded->ebackend_card_profile_set(card, profile);
 }
+
+void
+emix_sink_monitor(Emix_Sink *sink, Eina_Bool monitor)
+{
+   EINA_SAFETY_ON_FALSE_RETURN((ctx && ctx->loaded &&
+                                ctx->loaded->ebackend_sink_mute_set &&
+                                sink));
+   if (!ctx->loaded->ebackend_sink_monitor_set) return;
+   ctx->loaded->ebackend_sink_monitor_set(sink, monitor);
+}
+
+void
+emix_sink_input_monitor(Emix_Sink_Input *input, Eina_Bool monitor)
+{
+   EINA_SAFETY_ON_FALSE_RETURN((ctx && ctx->loaded &&
+                                ctx->loaded->ebackend_sink_mute_set &&
+                                input));
+
+   if (!ctx->loaded->ebackend_sink_input_monitor_set) return;
+   ctx->loaded->ebackend_sink_input_monitor_set(input, monitor);
+}
+
+void
+emix_source_monitor(Emix_Source *source, Eina_Bool monitor)
+{
+   EINA_SAFETY_ON_FALSE_RETURN((ctx && ctx->loaded &&
+                                ctx->loaded->ebackend_sink_mute_set &&
+                                source));
+
+   if (!ctx->loaded->ebackend_source_monitor_set) return;
+   ctx->loaded->ebackend_source_monitor_set(source, monitor);
+}
+
+
diff --git a/src/modules/mixer/lib/emix.h b/src/modules/mixer/lib/emix.h
index 5f2512310..1e1387937 100644
--- a/src/modules/mixer/lib/emix.h
+++ b/src/modules/mixer/lib/emix.h
@@ -39,7 +39,10 @@ enum Emix_Event {
    EMIX_SOURCE_CHANGED_EVENT,
    EMIX_CARD_ADDED_EVENT,
    EMIX_CARD_REMOVED_EVENT,
-   EMIX_CARD_CHANGED_EVENT
+   EMIX_CARD_CHANGED_EVENT,
+   EMIX_SINK_MONITOR_EVENT,
+   EMIX_SINK_INPUT_MONITOR_EVENT,
+   EMIX_SOURCE_MONITOR_EVENT,
 };
 
 typedef const char * Emix_Channel;
@@ -64,6 +67,8 @@ typedef struct _Emix_Sink {
    Eina_Bool mute;
    Eina_Bool default_sink;
    Eina_List *ports;
+   unsigned int mon_num; // number of left + right sample pairs
+   const float *mon_buf; // LRLRLR unsigned char samples
 } Emix_Sink;
 
 typedef struct _Emix_Sink_Input {
@@ -73,6 +78,8 @@ typedef struct _Emix_Sink_Input {
    Emix_Sink *sink;
    pid_t pid;
    const char *icon;
+   unsigned int mon_num; // number of left + right sample pairs
+   const float *mon_buf; // LRLRLR unsigned char samples
 } Emix_Sink_Input;
 
 typedef struct _Emix_Source {
@@ -80,6 +87,8 @@ typedef struct _Emix_Source {
    Emix_Volume volume;
    Eina_Bool mute;
    Eina_Bool default_source;
+   unsigned int mon_num; // number of left + right sample pairs
+   const float *mon_buf; // LRLRLR unsigned char samples
 } Emix_Source;
 
 typedef struct _Emix_Profile {
@@ -135,6 +144,13 @@ typedef struct _Emix_Backend {
    Evas_Object*          (*ebackend_advanced_options_add)(Evas_Object *parent);
    const Eina_List*      (*ebackend_cards_get)(void);
    Eina_Bool             (*ebackend_card_profile_set)(Emix_Card *card, const 
Emix_Profile *profile);
+
+   void                  (*ebackend_sink_monitor_set)(Emix_Sink *sink,
+                                                      Eina_Bool monitor);
+   void                  (*ebackend_sink_input_monitor_set)(Emix_Sink_Input 
*input,
+                                                            Eina_Bool monitor);
+   void                  (*ebackend_source_monitor_set)(Emix_Source *source,
+                                                        Eina_Bool monitor);
 } Emix_Backend;
 
 //////////////////////////////////////////////////////////////////////////////
@@ -160,8 +176,9 @@ E_API const Eina_List*    emix_backends_available(void);
 E_API Eina_Bool           emix_backend_set(const char *backend);
 
 E_API Eina_Bool           emix_event_callback_add(Emix_Event_Cb cb,
-                                                 const void *data);
-E_API Eina_Bool           emix_event_callback_del(Emix_Event_Cb cb);
+                                                  const void *data);
+E_API Eina_Bool           emix_event_callback_del(Emix_Event_Cb cb,
+                                                  const void *data);
 
 E_API int                 emix_max_volume_get(void);
 
@@ -196,4 +213,8 @@ E_API Evas_Object*        
emix_advanced_options_add(Evas_Object *parent);
 E_API const Eina_List*    emix_cards_get(void);
 E_API Eina_Bool           emix_card_profile_set(Emix_Card *card, Emix_Profile 
*profile);
 
+E_API void                emix_sink_monitor(Emix_Sink *sink, Eina_Bool 
monitor);
+E_API void                emix_sink_input_monitor(Emix_Sink_Input *input, 
Eina_Bool monitor);
+E_API void                emix_source_monitor(Emix_Source *source, Eina_Bool 
monitor);
+
 #endif  /* EMIX_H */

-- 


Reply via email to