1 #include "edje_private.h"
2
3 #ifdef ENABLE_MULTISENSE
4 #include "Ecore_Audio.h"
5
6 static Eo *out = NULL;
7 static int outs = 0;
8 static Eina_List *ins = NULL;
9 static Eina_Bool outfail = EINA_FALSE;
10
11 static void
_play_finished(void * data EINA_UNUSED,const Efl_Event * event)12 _play_finished(void *data EINA_UNUSED, const Efl_Event *event)
13 {
14 ins = eina_list_remove(ins, event->object);
15 efl_unref(event->object);
16 }
17
18 #if defined(_WIN32) || defined(HAVE_PULSE)
19 static void
_out_fail(void * data EINA_UNUSED,const Efl_Event * event)20 _out_fail(void *data EINA_UNUSED, const Efl_Event *event)
21 {
22 outfail = EINA_TRUE;
23 efl_unref(event->object);
24 out = NULL;
25 }
26 #endif
27
28 struct _edje_multisense_eet_data
29 {
30 unsigned int offset, length;
31 Eet_File *ef;
32 const char *data;
33 Ecore_Audio_Vio vio;
34 };
35
36 static int
eet_snd_file_get_length(void * data,Eo * eo_obj EINA_UNUSED)37 eet_snd_file_get_length(void *data, Eo *eo_obj EINA_UNUSED)
38 {
39 struct _edje_multisense_eet_data *vf = data;
40 return vf->length;
41 }
42
43 static int
eet_snd_file_seek(void * data,Eo * eo_obj EINA_UNUSED,int offset,int whence)44 eet_snd_file_seek(void *data, Eo *eo_obj EINA_UNUSED, int offset, int whence)
45 {
46 struct _edje_multisense_eet_data *vf = data;
47
48 switch (whence)
49 {
50 case SEEK_SET:
51 vf->offset = offset;
52 break;
53
54 case SEEK_CUR:
55 vf->offset += offset;
56 break;
57
58 case SEEK_END:
59 vf->offset = vf->length + offset;
60 break;
61
62 default:
63 break;
64 }
65 return vf->offset;
66 }
67
68 static int
eet_snd_file_read(void * data,Eo * eo_obj EINA_UNUSED,void * buffer,int count)69 eet_snd_file_read(void *data, Eo *eo_obj EINA_UNUSED, void *buffer, int count)
70 {
71 struct _edje_multisense_eet_data *vf = data;
72
73 if ((vf->offset + count) > vf->length)
74 count = vf->length - vf->offset;
75 memcpy(buffer, vf->data + vf->offset, count);
76 vf->offset += count;
77 return count;
78 }
79
80 static int
eet_snd_file_tell(void * data,Eo * eo_obj EINA_UNUSED)81 eet_snd_file_tell(void *data, Eo *eo_obj EINA_UNUSED)
82 {
83 struct _edje_multisense_eet_data *vf = data;
84
85 return vf->offset;
86 }
87
88 static void
_free(void * data)89 _free(void *data)
90 {
91 struct _edje_multisense_eet_data *eet_data = data;
92
93 if (eet_data->ef) eet_close(eet_data->ef);
94 // don't free if eet_data->data comes from eet_read_direct
95 // free(eet_data->data);
96 free(data);
97 outs--;
98 }
99
100 static Eina_Bool _channel_mute_states[8] = { 0 };
101
102 static Eina_Bool
_channel_mute(Edje * ed EINA_UNUSED,int channel)103 _channel_mute(Edje *ed EINA_UNUSED, int channel)
104 {
105 // ed lets use set mute per object... but for now no api's for this
106 // if all are muted ... then all!
107 if (_channel_mute_states[7]) return EINA_TRUE;
108 if ((channel < 0) || (channel > 7)) return EINA_FALSE;
109 return _channel_mute_states[channel];
110 return EINA_FALSE;
111 }
112
113 #endif
114
115 EAPI void
edje_audio_channel_mute_set(Edje_Channel channel,Eina_Bool mute)116 edje_audio_channel_mute_set(Edje_Channel channel, Eina_Bool mute)
117 {
118 #ifdef ENABLE_MULTISENSE
119 if ((unsigned)channel > 7) return;
120 _channel_mute_states[channel] = mute;
121 #else
122 (void)channel;
123 (void)mute;
124 #endif
125 }
126
127 EAPI Eina_Bool
edje_audio_channel_mute_get(Edje_Channel channel)128 edje_audio_channel_mute_get(Edje_Channel channel)
129 {
130 #ifdef ENABLE_MULTISENSE
131 if ((unsigned)channel > 7) return EINA_FALSE;
132 return _channel_mute_states[channel];
133 #else
134 (void)channel;
135 return EINA_TRUE;
136 #endif
137 }
138
139 Eina_Bool
_edje_multisense_internal_sound_sample_play(Edje * ed,const char * sample_name,const double speed,int channel)140 _edje_multisense_internal_sound_sample_play(Edje *ed, const char *sample_name, const double speed, int channel)
141 {
142 #ifdef ENABLE_MULTISENSE
143 Eo *in;
144 Edje_Sound_Sample *sample;
145 char snd_id_str[255];
146 int i;
147 Eina_Bool ret = EINA_FALSE;
148
149 if (_channel_mute(ed, channel)) return EINA_FALSE;
150
151 if (outfail) return EINA_FALSE;
152
153 if (!sample_name)
154 {
155 ERR("Given Sample Name is NULL\n");
156 return EINA_FALSE;
157 }
158
159 if ((!ed) || (!ed->file) || (!ed->file->sound_dir))
160 return EINA_FALSE;
161
162 for (i = 0; i < (int)ed->file->sound_dir->samples_count; i++)
163 {
164 sample = &ed->file->sound_dir->samples[i];
165 if (!strcmp(sample->name, sample_name))
166 {
167 struct _edje_multisense_eet_data *eet_data;
168 int len;
169
170 snprintf(snd_id_str, sizeof(snd_id_str), "edje/sounds/%i", sample->id);
171
172 eet_data = calloc(1, sizeof(struct _edje_multisense_eet_data));
173 if (!eet_data)
174 {
175 ERR("Out of memory in allocating multisense sample info");
176 return EINA_FALSE;
177 }
178 // open eet file again to esnure we have reference because we
179 // use eet_read_direct to avoid duplicating/copying into memory
180 // by relying on a direct mmap, but this means we need to close
181 // the eet file handle instead of freeing data
182 eet_data->ef = eet_mmap(ed->file->f);
183 if (!eet_data->ef)
184 {
185 ERR("Cannot open edje file '%s' for samples", ed->path);
186 free(eet_data);
187 return EINA_FALSE;
188 }
189 eet_data->data = eet_read_direct(eet_data->ef, snd_id_str, &len);
190 if (len <= 0)
191 {
192 ERR("Sample form edj file '%s' is 0 length", ed->path);
193 eet_close(eet_data->ef);
194 free(eet_data);
195 return EINA_FALSE;
196 }
197 eet_data->length = len;
198 /* action->speed */
199
200 eet_data->vio.get_length = eet_snd_file_get_length;
201 eet_data->vio.seek = eet_snd_file_seek;
202 eet_data->vio.read = eet_snd_file_read;
203 eet_data->vio.tell = eet_snd_file_tell;
204 eet_data->offset = 0;
205
206 in = efl_add_ref(ECORE_AUDIO_IN_SNDFILE_CLASS, NULL, efl_name_set(efl_added, snd_id_str), ecore_audio_obj_in_speed_set(efl_added, speed), ecore_audio_obj_vio_set(efl_added, &eet_data->vio, eet_data, _free), efl_event_callback_add(efl_added, ECORE_AUDIO_IN_EVENT_IN_STOPPED, _play_finished, NULL));
207 if (!out)
208 {
209
210 # ifdef _WIN32
211 out = efl_add_ref(ECORE_AUDIO_OUT_WASAPI_CLASS, NULL, efl_event_callback_add(efl_added, ECORE_AUDIO_OUT_WASAPI_EVENT_CONTEXT_FAIL, _out_fail, NULL));
212 # else
213 # ifdef HAVE_PULSE
214 out = efl_add_ref(ECORE_AUDIO_OUT_PULSE_CLASS, NULL, efl_event_callback_add(efl_added, ECORE_AUDIO_OUT_PULSE_EVENT_CONTEXT_FAIL, _out_fail, NULL));
215 # endif
216 # endif
217 if (out) outs++;
218 }
219 if (!out)
220 {
221 static Eina_Bool complained = EINA_FALSE;
222
223 if (!complained)
224 {
225 complained = EINA_TRUE;
226 # ifdef _WIN32
227 ERR("Could not create multisense audio out (wasapi)");
228 # else
229 # ifdef HAVE_PULSE
230 ERR("Could not create multisense audio out (pulse)");
231 # endif
232 # endif
233 }
234 efl_unref(in);
235 return EINA_FALSE;
236 }
237 ret = ecore_audio_obj_out_input_attach(out, in);
238 if (!ret)
239 {
240 static Eina_Bool complained = EINA_FALSE;
241
242 if (!complained)
243 {
244 complained = EINA_TRUE;
245 ERR("Could not attach input");
246 }
247 efl_unref(in);
248 return EINA_FALSE;
249 }
250 ins = eina_list_append(ins, in);
251 }
252 }
253 return EINA_TRUE;
254 #else
255 // warning shh
256 (void)ed;
257 (void)sample_name;
258 (void)speed;
259 (void)channel;
260 return EINA_FALSE;
261 #endif
262 }
263
264 Eina_Bool
_edje_multisense_internal_sound_tone_play(Edje * ed,const char * tone_name,const double duration,int channel)265 _edje_multisense_internal_sound_tone_play(Edje *ed, const char *tone_name, const double duration, int channel)
266 {
267 #ifdef ENABLE_MULTISENSE
268 unsigned int i;
269 Edje_Sound_Tone *tone;
270 Eina_Bool ret = EINA_FALSE;
271
272 Eo *in;
273 if (!tone_name)
274 {
275 ERR("Given Tone Name is NULL");
276 return EINA_FALSE;
277 }
278
279 if (_channel_mute(ed, channel)) return EINA_FALSE;
280
281 if (outfail) return EINA_FALSE;
282
283 if ((!ed) || (!ed->file) || (!ed->file->sound_dir))
284 return EINA_FALSE;
285
286 for (i = 0; i < ed->file->sound_dir->tones_count; i++)
287 {
288 tone = &ed->file->sound_dir->tones[i];
289 if (!strcmp(tone->name, tone_name))
290 {
291 in = efl_add_ref(ECORE_AUDIO_IN_TONE_CLASS, NULL);
292 efl_name_set(in, "tone");
293 efl_key_data_set(in, ECORE_AUDIO_ATTR_TONE_FREQ, &tone->value);
294 ecore_audio_obj_in_length_set(in, duration);
295 efl_event_callback_add(in, ECORE_AUDIO_IN_EVENT_IN_STOPPED, _play_finished, NULL);
296
297 if (!out)
298 {
299 # ifdef _WIN32
300 out = efl_add_ref(ECORE_AUDIO_OUT_WASAPI_CLASS, NULL, efl_event_callback_add(efl_added, ECORE_AUDIO_OUT_WASAPI_EVENT_CONTEXT_FAIL, _out_fail, NULL));
301 # else
302 # ifdef HAVE_PULSE
303 out = efl_add_ref(ECORE_AUDIO_OUT_PULSE_CLASS, NULL, efl_event_callback_add(efl_added, ECORE_AUDIO_OUT_PULSE_EVENT_CONTEXT_FAIL, _out_fail, NULL));
304 # endif
305 # endif
306 if (out) outs++;
307 }
308
309 ret = ecore_audio_obj_out_input_attach(out, in);
310 if (!ret)
311 {
312 ERR("Could not attach input");
313 efl_unref(in);
314 return EINA_FALSE;
315 }
316 }
317 }
318 return EINA_TRUE;
319 #else
320 // warning shh
321 (void)ed;
322 (void)duration;
323 (void)tone_name;
324 (void)channel;
325 return EINA_FALSE;
326 #endif
327 }
328
329 Eina_Bool
_edje_multisense_internal_vibration_sample_play(Edje * ed EINA_UNUSED,const char * sample_name EINA_UNUSED,int repeat EINA_UNUSED)330 _edje_multisense_internal_vibration_sample_play(Edje *ed EINA_UNUSED, const char *sample_name EINA_UNUSED, int repeat EINA_UNUSED)
331 {
332 #ifdef ENABLE_MULTISENSE
333 ERR("Vibration is not supported yet, name:%s, repeat:%d", sample_name, repeat);
334 return EINA_FALSE;
335 #else
336 (void)ed;
337 (void)repeat;
338 return EINA_FALSE;
339 #endif
340 }
341
342 void
_edje_multisense_init(void)343 _edje_multisense_init(void)
344 {
345 #ifdef ENABLE_MULTISENSE
346 ecore_audio_init();
347 #endif
348 }
349
350 void
_edje_multisense_shutdown(void)351 _edje_multisense_shutdown(void)
352 {
353 #ifdef ENABLE_MULTISENSE
354 Eo *in;
355 if (outs > 0)
356 {
357 WRN("Shutting down audio while samples still playing");
358 }
359 if (out)
360 {
361 efl_unref(out);
362 out = NULL;
363 outs = 0;
364 }
365 EINA_LIST_FREE(ins, in)
366 efl_unref(in);
367 ecore_audio_shutdown();
368 #endif
369 }
370
371