summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlavio Ceolin <flavio.ceolin@gmail.com>2014-09-08 23:50:53 -0300
committerFlavio Ceolin <flavio.ceolin@gmail.com>2014-09-08 23:50:53 -0300
commit25ab39d8830bfb9c60865f3c7f0b368b800c4f28 (patch)
tree6f12875076585e73c568386b58096760e18b6f94
parent0a32e7c29d1b5371b2d546262e85eaa8c78c79eb (diff)
emix: Support volume meter
-rw-r--r--src/lib/backends/alsa/alsa.c3
-rw-r--r--src/lib/backends/pulseaudio/pulse.c109
-rw-r--r--src/lib/emix.c10
-rw-r--r--src/lib/emix.h5
4 files changed, 125 insertions, 2 deletions
diff --git a/src/lib/backends/alsa/alsa.c b/src/lib/backends/alsa/alsa.c
index e26be09..ff9c588 100644
--- a/src/lib/backends/alsa/alsa.c
+++ b/src/lib/backends/alsa/alsa.c
@@ -483,7 +483,8 @@ _alsa_backend = {
483 _alsa_sources_get,/*source*/ 483 _alsa_sources_get,/*source*/
484 _alsa_sources_mute_set,/* source mute set */ 484 _alsa_sources_mute_set,/* source mute set */
485 _alsa_sources_volume_set, /* source volume set */ 485 _alsa_sources_volume_set, /* source volume set */
486 NULL /* advanced options */ 486 NULL, /* advanced options */
487 NULL
487}; 488};
488 489
489EAPI Emix_Backend * 490EAPI Emix_Backend *
diff --git a/src/lib/backends/pulseaudio/pulse.c b/src/lib/backends/pulseaudio/pulse.c
index 1ef0dc4..50b7e8f 100644
--- a/src/lib/backends/pulseaudio/pulse.c
+++ b/src/lib/backends/pulseaudio/pulse.c
@@ -23,22 +23,26 @@ typedef struct _Context {
23 source_event_cb source_cb; 23 source_event_cb source_cb;
24 disconnect_event_cb disconnect_cb; 24 disconnect_event_cb disconnect_cb;
25 ready_event_cb ready_cb; 25 ready_event_cb ready_cb;
26 volume_meter_cb meter_cb;
26 Ecore_Timer *connect; 27 Ecore_Timer *connect;
27 int default_sink; 28 int default_sink;
28 29
29 Eina_List *sinks, *sources, *inputs; 30 Eina_List *sinks, *sources, *inputs;
30 Eina_Bool connected; 31 Eina_Bool connected;
32 const void *meter_data;
31} Context; 33} Context;
32 34
33typedef struct _Sink { 35typedef struct _Sink {
34 Emix_Sink base; 36 Emix_Sink base;
35 int idx; 37 int idx;
38 int source_idx;
36} Sink; 39} Sink;
37 40
38typedef struct _Sink_Input { 41typedef struct _Sink_Input {
39 Emix_Sink_Input base; 42 Emix_Sink_Input base;
40 const char *icon; 43 const char *icon;
41 int idx; 44 int idx;
45 pa_stream *peak;
42} Sink_Input; 46} Sink_Input;
43 47
44typedef struct _Source { 48typedef struct _Source {
@@ -123,6 +127,95 @@ _source_del(Source *source)
123} 127}
124 128
125static void 129static void
130_suspended_cb(pa_stream *s, void *userdata EINA_UNUSED)
131{
132 if (pa_stream_is_suspended(s))
133 ERR("stream suspended");
134}
135
136static void
137_read_cb(pa_stream *s, size_t length, void *userdata)
138{
139 const void *data;
140 double v;
141 Sink_Input *si = userdata;
142
143 if (!ctx->meter_cb)
144 return;
145
146 if (pa_stream_peek(s, &data, &length) < 0)
147 {
148 ERR("Failed to read data from stream");
149 return;
150 }
151
152 EINA_SAFETY_ON_FALSE_RETURN(length > 0);
153 EINA_SAFETY_ON_FALSE_RETURN(length % sizeof(float) == 0);
154
155 v = ((const float*) data)[length / sizeof(float) -1];
156
157 pa_stream_drop(s);
158
159 if (v < 0)
160 v = 0;
161 if (v > 1)
162 v = 1;
163
164 ctx->meter_cb(si->base.sink, v, (void *)ctx->meter_data);
165}
166
167static void
168_create_sink_monitor(Sink_Input *si)
169{
170 pa_stream *s;
171 char t[16];
172 pa_buffer_attr attr;
173 pa_sample_spec ss;
174 pa_stream_flags_t flags;
175 uint32_t source_idx = ((Sink *)si->base.sink)->source_idx;
176 uint32_t stream_idx = si->idx;
177
178 if (si->peak)
179 {
180 pa_stream_disconnect(si->peak);
181 si->peak = NULL;
182 }
183
184 ss.channels = 1;
185 ss.format = PA_SAMPLE_FLOAT32;
186 ss.rate = 25;
187
188 memset(&attr, 0, sizeof(attr));
189 attr.fragsize = sizeof(float);
190 attr.maxlength = (uint32_t) -1;
191
192 snprintf(t, sizeof(t), "%u", source_idx);
193
194 if (!(s = pa_stream_new(ctx->context, "Peak detect", &ss, NULL)))
195 {
196 ERR("Failed to create monitoring stream");
197 return;
198 }
199
200 if (stream_idx != (uint32_t) -1)
201 pa_stream_set_monitor_stream(s, stream_idx);
202
203 pa_stream_set_read_callback(s, _read_cb, si);
204 pa_stream_set_suspended_callback(s, _suspended_cb, si);
205 flags = (pa_stream_flags_t) (PA_STREAM_DONT_MOVE | PA_STREAM_PEAK_DETECT |
206 PA_STREAM_ADJUST_LATENCY | PA_STREAM_NOFLAGS);
207
208 if (pa_stream_connect_record(s, t, &attr, flags) < 0)
209 {
210 ERR("Failed to connect monitoring stream");
211 pa_stream_unref(s);
212 return;
213 }
214
215 si->peak = s;
216}
217
218static void
126_sink_cb(pa_context *c EINA_UNUSED, const pa_sink_info *info, int eol, 219_sink_cb(pa_context *c EINA_UNUSED, const pa_sink_info *info, int eol,
127 void *userdata EINA_UNUSED) 220 void *userdata EINA_UNUSED)
128{ 221{
@@ -147,6 +240,7 @@ _sink_cb(pa_context *c EINA_UNUSED, const pa_sink_info *info, int eol,
147 240
148 sink = calloc(1, sizeof(Sink)); 241 sink = calloc(1, sizeof(Sink));
149 sink->idx = info->index; 242 sink->idx = info->index;
243 sink->source_idx = info->monitor_source;
150 sink->base.name = eina_stringshare_add(info->description); 244 sink->base.name = eina_stringshare_add(info->description);
151 sink->base.volume = _pa_cvolume_convert(info->volume); 245 sink->base.volume = _pa_cvolume_convert(info->volume);
152 sink->base.mute = !!info->mute; 246 sink->base.mute = !!info->mute;
@@ -335,6 +429,10 @@ _sink_input_cb(pa_context *c EINA_UNUSED, const pa_sink_input_info *info,
335 } 429 }
336 input->icon = eina_stringshare_add(_icon_from_properties(info->proplist)); 430 input->icon = eina_stringshare_add(_icon_from_properties(info->proplist));
337 ctx->inputs = eina_list_append(ctx->inputs, input); 431 ctx->inputs = eina_list_append(ctx->inputs, input);
432
433 if (pa_context_get_server_protocol_version(ctx->context) >= 13)
434 _create_sink_monitor(input);
435
338 if (ctx->sink_input_cb) 436 if (ctx->sink_input_cb)
339 ctx->sink_input_cb((Emix_Sink_Input *)input, EMIX_EVENT_ADDED); 437 ctx->sink_input_cb((Emix_Sink_Input *)input, EMIX_EVENT_ADDED);
340} 438}
@@ -1013,6 +1111,14 @@ _sink_change_support(void)
1013 return EINA_TRUE; 1111 return EINA_TRUE;
1014} 1112}
1015 1113
1114static void
1115_volume_meter_callback_set(volume_meter_cb cb, const void *userdata)
1116{
1117 EINA_SAFETY_ON_NULL_RETURN(ctx);
1118 ctx->meter_cb = cb;
1119 ctx->meter_data = userdata;
1120}
1121
1016static Emix_Backend 1122static Emix_Backend
1017_pulseaudio_backend = { 1123_pulseaudio_backend = {
1018 _init, 1124 _init,
@@ -1032,7 +1138,8 @@ _pulseaudio_backend = {
1032 _sources_get, 1138 _sources_get,
1033 _source_mute_set, 1139 _source_mute_set,
1034 _source_volume_set, 1140 _source_volume_set,
1035 NULL 1141 NULL,
1142 _volume_meter_callback_set
1036}; 1143};
1037 1144
1038EAPI Emix_Backend * 1145EAPI Emix_Backend *
diff --git a/src/lib/emix.c b/src/lib/emix.c
index 8f40e1c..d8f6517 100644
--- a/src/lib/emix.c
+++ b/src/lib/emix.c
@@ -437,3 +437,13 @@ emix_advanced_options_add(Evas_Object *parent)
437 437
438 return ctx->loaded->ebackend_advanced_options_add(parent); 438 return ctx->loaded->ebackend_advanced_options_add(parent);
439} 439}
440
441void
442emix_volume_meter_callback_set(volume_meter_cb cb, const void *userdata)
443{
444 EINA_SAFETY_ON_FALSE_RETURN((ctx && ctx->loaded &&
445 ctx->loaded->ebackend_volume_meter_callback_set &&
446 cb));
447
448 ctx->loaded->ebackend_volume_meter_callback_set(cb, userdata);
449}
diff --git a/src/lib/emix.h b/src/lib/emix.h
index bdd4487..222991a 100644
--- a/src/lib/emix.h
+++ b/src/lib/emix.h
@@ -63,6 +63,7 @@ typedef void (*sink_input_event_cb)(Emix_Sink_Input *input,
63typedef void (*source_event_cb)(Emix_Source *source, Emix_Event_Type type); 63typedef void (*source_event_cb)(Emix_Source *source, Emix_Event_Type type);
64typedef void (*disconnect_event_cb)(void); 64typedef void (*disconnect_event_cb)(void);
65typedef void (*ready_event_cb)(void); 65typedef void (*ready_event_cb)(void);
66typedef void (*volume_meter_cb)(Emix_Sink *sink, double value, void *userdata);
66 67
67typedef struct _Emix_Backend { 68typedef struct _Emix_Backend {
68 Eina_Bool (*ebackend_init)(sink_event_cb sink_cb, 69 Eina_Bool (*ebackend_init)(sink_event_cb sink_cb,
@@ -99,6 +100,8 @@ typedef struct _Emix_Backend {
99 Emix_Volume volume); 100 Emix_Volume volume);
100 101
101 Evas_Object* (*ebackend_advanced_options_add)(Evas_Object *parent); 102 Evas_Object* (*ebackend_advanced_options_add)(Evas_Object *parent);
103 void (*ebackend_volume_meter_callback_set)
104 (volume_meter_cb cb, const void *userdata);
102} Emix_Backend; 105} Emix_Backend;
103 106
104 107
@@ -132,5 +135,7 @@ EAPI void emix_source_volume_set(Emix_Source *source,
132 Emix_Volume volume); 135 Emix_Volume volume);
133 136
134EAPI Evas_Object* emix_advanced_options_add(Evas_Object *parent); 137EAPI Evas_Object* emix_advanced_options_add(Evas_Object *parent);
138EAPI void emix_volume_meter_callback_set(volume_meter_cb cb,
139 const void *userdata);
135 140
136#endif /* EMIX_H */ 141#endif /* EMIX_H */