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