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
This commit is contained in:
Carsten Haitzler 2021-03-30 01:09:06 +01:00
parent 0564846adb
commit 9444e5d55d
6 changed files with 621 additions and 17 deletions

View File

@ -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();

View File

@ -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;
}

View File

@ -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 *

View File

@ -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 *

View File

@ -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);
}

View File

@ -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 */