forked from enlightenment/enlightenment
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:
parent
0564846adb
commit
9444e5d55d
|
@ -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();
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 *
|
||||
|
|
|
@ -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 *
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
Loading…
Reference in New Issue