78344fc472deb5d45943eac14f3d691a6b5bd573
[framework/uifw/edje.git] / src / lib / edje_multisense.c
1 #include "edje_private.h"
2
3 typedef struct _Multisense_Data
4 {
5    Edje_Multisense_Env *msenv;
6 #ifdef HAVE_LIBREMIX
7    RemixDeck *deck;
8    RemixTrack *track;
9    RemixLayer *snd_layer, *player_layer;
10    RemixBase *player;
11    RemixBase *player_snd;
12    int remaining;
13    int offset;
14    Eina_List *snd_src_list;
15
16    MULTISENSE_SOUND_PLAYER_GET_FUNC multisense_sound_player_get;
17 #endif
18 }Multisense_Data;
19
20 #define BUF_LEN 64
21 #define SND_PROCESS_LENGTH 2048
22 #define TIMEOUT_FOR_MM_HANDLER 10
23
24 #ifdef ENABLE_MULTISENSE
25 static Ecore_Thread *player_thread = NULL;
26 static int command_pipe[2];
27 static Eina_Bool pipe_initialized = EINA_FALSE;
28 static Ecore_Timer *idletimer = NULL;
29 #endif
30
31 typedef enum _Edje_Sound_Action_Type
32 {
33    EDJE_PLAY_SAMPLE = 0,
34    EDJE_PLAY_TONE,
35    /*
36    EDJE_PLAY_PATTERN,
37    EDJE_PLAY_INSTRUMENT,
38    EDJE_PLAY_SONG,
39    */
40    EDJE_CLOSE_HANDLE,
41    EDJE_SOUND_LAST
42 } Edje_Sound_Action_Type;
43
44 typedef struct _Edje_Sample_Action Edje_Sample_Action;
45 typedef struct _Edje_Tone_Action Edje_Tone_Action;
46 typedef struct _Edje_Multisense_Sound_Action Edje_Multisense_Sound_Action;
47
48 struct _Edje_Sample_Action
49 {
50    char sample_name[BUF_LEN];
51    double speed;
52 };
53
54 struct _Edje_Tone_Action
55 {
56    char tone_name[BUF_LEN];
57    double duration;
58 };
59
60 struct _Edje_Multisense_Sound_Action
61 {
62    Edje *ed;
63    Edje_Sound_Action_Type action;
64    union {
65       Edje_Sample_Action sample;
66       Edje_Tone_Action tone;
67    } type;
68    Edje_File *snd_file;
69 };
70 #ifdef ENABLE_MULTISENSE
71 static Eina_Module *m = NULL;
72 static Eina_Bool _edje_multisense_Timer_Callback(void *data);
73
74 static Eina_Bool
75 edje_multisense_create_timer(void)
76 {
77    ecore_thread_main_loop_begin();
78    if (idletimer) ecore_timer_del(idletimer);
79    idletimer = ecore_timer_add(TIMEOUT_FOR_MM_HANDLER,
80                                   _edje_multisense_Timer_Callback, NULL);
81
82    ecore_thread_main_loop_end();
83    return EINA_TRUE;
84 }
85
86 static Eina_Bool
87 edje_multisense_kill_timer(void)
88 {
89    ecore_thread_main_loop_begin();
90    if (idletimer) ecore_timer_del(idletimer);
91    idletimer = NULL;
92    ecore_thread_main_loop_end();
93    return EINA_TRUE;
94 }
95
96 static Eina_Bool
97 _edje_multisense_Timer_Callback(void *data)
98 {
99 #if defined(ENABLE_MULTISENSE) && defined(HAVE_LIBREMIX)
100    ssize_t size = 0;
101    Edje_Multisense_Sound_Action command = {0,};
102
103    if ((!pipe_initialized) && (!player_thread)) return ECORE_CALLBACK_CANCEL;
104
105    //post a command to handle mm sound cleanup
106    command.action = EDJE_CLOSE_HANDLE;
107    size = write(command_pipe[1], &command, sizeof(command));
108    idletimer = NULL;
109 #endif
110    return ECORE_CALLBACK_CANCEL;
111 }
112
113 static Multisense_Data *
114 init_multisense_environment(void)
115 {
116    Multisense_Data *msdata;
117    char ms_factory[BUF_LEN];
118    char *ms_factory_env;
119    MULTISENSE_FACTORY_INIT_FUNC multisense_factory_init;
120
121    msdata = calloc(1, sizeof(Multisense_Data));
122    if (!msdata) goto err;
123
124    msdata->msenv = calloc(1, sizeof(Edje_Multisense_Env));
125    if (!msdata->msenv) goto err;
126
127    ms_factory_env = getenv("MULTISENSE_FACTORY");
128    if (ms_factory_env)
129      strncpy(ms_factory, ms_factory_env, BUF_LEN);
130    else
131      strcpy(ms_factory, "multisense_factory");
132
133    m = _edje_module_handle_load(ms_factory);
134    if (!m) goto err;
135 #ifdef HAVE_LIBREMIX
136    msdata->msenv->remixenv = remix_init();
137 #endif
138    multisense_factory_init = 
139      eina_module_symbol_get(m, "multisense_factory_init");
140    if (multisense_factory_init) multisense_factory_init(msdata->msenv);
141 #ifdef HAVE_LIBREMIX
142    msdata->multisense_sound_player_get = 
143      eina_module_symbol_get(m, "multisense_sound_player_get");
144    if (!msdata->multisense_sound_player_get) goto err;
145
146    msdata->deck = remix_deck_new(msdata->msenv->remixenv);
147    msdata->track = remix_track_new(msdata->msenv->remixenv, msdata->deck);
148    msdata->snd_layer = remix_layer_new_ontop(msdata->msenv->remixenv,
149                                              msdata->track,
150                                              REMIX_TIME_SAMPLES);
151    msdata->player_layer = remix_layer_new_ontop(msdata->msenv->remixenv,
152                                                 msdata->track,
153                                                 REMIX_TIME_SAMPLES);
154    msdata->player = msdata->multisense_sound_player_get(msdata->msenv);
155    if (!msdata->player) goto err;
156    msdata->player_snd = remix_sound_new(msdata->msenv->remixenv,
157                                         msdata->player, msdata->player_layer,
158                                         REMIX_SAMPLES(0),
159                                         REMIX_SAMPLES(REMIX_COUNT_INFINITE));
160 #endif
161    return msdata;
162
163 err:
164    if (msdata)
165      {
166 #ifdef HAVE_LIBREMIX
167         if (msdata->deck) remix_destroy(msdata->msenv->remixenv, msdata->deck);
168         if (msdata->msenv->remixenv) remix_purge(msdata->msenv->remixenv);
169 #endif
170         if (msdata->msenv) free(msdata->msenv);
171         free(msdata);
172      }
173    return NULL;
174 }
175 #endif
176
177 #if defined(ENABLE_MULTISENSE) && defined(HAVE_LIBREMIX)
178 static RemixBase *
179 eet_sound_reader_get(Edje_Multisense_Env *msenv, const char *path,
180                      const char *sound_id, const double speed)
181 {
182    RemixPlugin *sf_plugin = NULL;
183    RemixBase * eet_snd_reader = NULL;
184    int sf_path_key = 0;
185    int sf_sound_id_key = 0;
186    int sf_speed_key = 0;
187    CDSet *sf_parms = NULL;
188    RemixEnv *env = msenv->remixenv;
189
190    if (sf_plugin == NULL)
191      {
192         sf_plugin = remix_find_plugin(env, "eet_sndfile_reader");
193         if (sf_plugin == NULL)
194           {
195              ERR ("Multisense EET Sound reader plugin NULL\n");
196              return NULL;
197           }
198
199         sf_path_key = remix_get_init_parameter_key(env, sf_plugin, "path");
200         sf_sound_id_key = remix_get_init_parameter_key(env, sf_plugin, "sound_id");
201         sf_speed_key = remix_get_init_parameter_key(env, sf_plugin, "speed");
202      }
203    sf_parms = cd_set_replace(env, sf_parms, sf_path_key, CD_STRING(path));
204    sf_parms = cd_set_replace(env, sf_parms, sf_sound_id_key, CD_STRING(sound_id));
205    sf_parms = cd_set_replace(env, sf_parms, sf_speed_key, CD_DOUBLE(speed));
206    eet_snd_reader = remix_new(env, sf_plugin, sf_parms);
207
208    //free the sf_parms as it is no more needed.
209    cd_set_free(env, sf_parms);
210
211    return eet_snd_reader;
212 }
213
214 static RemixBase *
215 edje_remix_sample_create(Multisense_Data *msdata, Edje_File *file, Edje_Sample_Action *action)
216 {
217    RemixBase *remix_snd = NULL;
218    Edje_Sound_Sample *sample;
219    int i;
220    char snd_id_str[16];
221
222    if ((!file) || (!file->sound_dir))
223      return NULL;
224
225    for (i = 0; i < (int)file->sound_dir->samples_count; i++)
226      {
227         sample = &file->sound_dir->samples[i];
228         if (sample && !strcmp(sample->name, action->sample_name))
229           {
230              snprintf(snd_id_str, sizeof(snd_id_str), "edje/sounds/%i", sample->id);
231              remix_snd = eet_sound_reader_get(msdata->msenv, file->path,
232                                               snd_id_str, action->speed);
233              break;
234           }
235      }
236    return remix_snd;
237 }
238
239 static RemixBase *
240 edje_remix_tone_create(Multisense_Data *msdata, Edje_File *file, Edje_Tone_Action *action)
241 {
242    Edje_Sound_Tone *tone;
243    RemixSquareTone *square = NULL;
244    unsigned int i;
245
246    if ((!file) || (!file->sound_dir))
247      return NULL;
248
249    for (i = 0; i < file->sound_dir->tones_count; i++)
250      {
251         tone = &file->sound_dir->tones[i];
252         if (tone && !strcmp(tone->name, action->tone_name))
253           {
254              square = remix_squaretone_new (msdata->msenv->remixenv, tone->value);
255              break;
256           }
257      }
258    return square;
259 }
260
261 static void
262 sound_command_handler(Multisense_Data *msdata)
263 {
264    RemixCount length;
265    Edje_Multisense_Sound_Action command = {0,};
266    RemixBase *base = NULL;
267    RemixBase *sound;
268    int read_len = sizeof(command);
269
270    //read and handle all samples to avoid queue effect
271    while(read(command_pipe[0], &command, sizeof(command)) == read_len)
272      {
273         switch (command.action)
274           {
275               case EDJE_PLAY_SAMPLE:
276                  base = edje_remix_sample_create(msdata, command.snd_file,
277                                         &command.type.sample);
278                  length = remix_length(msdata->msenv->remixenv, base);
279                  edje_multisense_create_timer();
280                  break;
281               case EDJE_PLAY_TONE:
282                  base = edje_remix_tone_create(msdata, command.snd_file, &command.type.tone);
283                  length = (command.type.tone.duration *
284                               remix_get_samplerate(msdata->msenv->remixenv));
285                  break;
286               case EDJE_CLOSE_HANDLE:
287                  remix_reset(msdata->msenv->remixenv, msdata->player);
288                  return;
289                  break;
290               case EDJE_SOUND_LAST:
291                  return;
292               default:
293                  ERR("Invalid Sound Play Command\n");
294                  break;
295           }
296         if (base)
297           {
298              sound = remix_sound_new(msdata->msenv->remixenv, base, msdata->snd_layer,
299                                 REMIX_SAMPLES(msdata->offset),
300                                 REMIX_SAMPLES(length));
301              if (msdata->remaining < length) msdata->remaining = length;
302              msdata->snd_src_list = eina_list_append(msdata->snd_src_list, sound);
303              msdata->snd_src_list = eina_list_append(msdata->snd_src_list, base);
304           }
305      }
306 }
307 #endif
308
309 #ifdef ENABLE_MULTISENSE
310 // msdata outside of thread due to thread issues in dlsym etc.
311 static Multisense_Data *msdata = NULL;
312
313 static void
314 _msdata_free(void)
315 {
316    // cleanup msdata outside of thread due to thread issues in dlsym etc.
317    if (!msdata) return;
318    edje_multisense_kill_timer();
319 #ifdef HAVE_LIBREMIX
320    //cleanup Remix stuffs
321    remix_destroy(msdata->msenv->remixenv, msdata->player);
322    remix_destroy(msdata->msenv->remixenv, msdata->deck);
323    remix_purge(msdata->msenv->remixenv);
324 #endif
325    free(msdata->msenv);
326    free(msdata);
327    msdata = NULL;
328 }
329
330 static void
331 _player_job(void *data __UNUSED__, Ecore_Thread *th)
332 {
333    fd_set wait_fds;
334 #ifdef HAVE_LIBREMIX
335    RemixBase *sound;
336    RemixCount process_len;
337 #endif
338 // disable and move outside of thread due to dlsym etc. thread issues
339 //   Multisense_Data * msdata = init_multisense_environment();
340
341    if (!msdata) return;
342
343    fcntl(command_pipe[0], F_SETFL, O_NONBLOCK);
344    FD_ZERO(&wait_fds);
345    FD_SET(command_pipe[0], &wait_fds);
346
347    while (!ecore_thread_check(th))
348      {
349 #ifdef HAVE_LIBREMIX
350         if (!msdata->remaining)
351           {
352              int err;
353              //Cleanup already played sound sources
354              EINA_LIST_FREE(msdata->snd_src_list, sound)
355                {
356                   remix_destroy(msdata->msenv->remixenv, sound);
357                }
358              //wait for new sound
359              err = select(command_pipe[0] + 1, &wait_fds, NULL, NULL, 0);
360           }
361         //read sound command , if any
362         sound_command_handler(msdata);
363         process_len = MIN(msdata->remaining, SND_PROCESS_LENGTH);
364         remix_process(msdata->msenv->remixenv, msdata->deck, process_len,
365                       RemixNone, RemixNone);
366         msdata->offset += process_len;
367         msdata->remaining -= process_len;
368 #endif
369      }
370
371 #ifdef HAVE_LIBREMIX
372    //Cleanup last played sound sources
373    EINA_LIST_FREE(msdata->snd_src_list, sound)
374      {
375         remix_destroy(msdata->msenv->remixenv, sound);
376      }
377 #endif
378    _msdata_free();
379    player_thread = NULL;
380    close(command_pipe[0]);
381    close(command_pipe[1]);
382 }
383 #endif
384
385 Eina_Bool
386 _edje_multisense_internal_sound_sample_play(Edje *ed, const char *sample_name, const double speed)
387 {
388    ssize_t size = 0;
389 #if defined(ENABLE_MULTISENSE) && defined(HAVE_LIBREMIX)
390    Edje_Multisense_Sound_Action command = {0,};
391
392    if ((!pipe_initialized) && (!player_thread)) return EINA_FALSE;
393    if (!sample_name)
394      {
395         ERR("Given Sample Name is NULL\n");
396         return EINA_FALSE;
397      }
398
399    command.action = EDJE_PLAY_SAMPLE;
400    command.snd_file = ed->file;
401    strncpy(command.type.sample.sample_name, sample_name, BUF_LEN);
402    command.type.sample.speed = speed;
403    size = write(command_pipe[1], &command, sizeof(command));
404 #else
405    // warning shh
406    (void) ed;
407    (void) sample_name;
408    (void) speed;
409 #endif
410    return (size == sizeof(Edje_Multisense_Sound_Action));
411 }
412
413 Eina_Bool
414 _edje_multisense_internal_sound_tone_play(Edje *ed, const char *tone_name, const double duration)
415 {
416    ssize_t size = 0;
417 #if defined(ENABLE_MULTISENSE) && defined(HAVE_LIBREMIX)
418    Edje_Multisense_Sound_Action command = {0,};
419
420    if ((!pipe_initialized) && (!player_thread)) return EINA_FALSE;
421    if (!tone_name)
422      {
423         ERR("Given Tone Name is NULL\n");
424         return EINA_FALSE;
425      }
426
427    command.action = EDJE_PLAY_TONE;
428    command.snd_file = ed->file;
429    strncpy(command.type.tone.tone_name, tone_name, BUF_LEN);
430    command.type.tone.duration = duration;
431    size = write(command_pipe[1], &command, sizeof(command));
432 #else
433    // warning shh
434    (void) ed;
435    (void) duration;
436    (void) tone_name;
437 #endif
438    return (size == sizeof(Edje_Multisense_Sound_Action));
439
440 }
441
442 /* Initialize the modules in main thread. to avoid dlopen issue in the Threads */
443 void
444 _edje_multisense_init(void)
445 {
446 #ifdef ENABLE_MULTISENSE
447    if (!pipe_initialized && (pipe(command_pipe) != -1))
448      pipe_initialized = EINA_TRUE;
449    multisense_init = EINA_TRUE;
450
451    // init msdata outside of thread due to thread issues in dlsym etc.
452    if (!msdata) msdata = init_multisense_environment();
453
454    if (!player_thread)
455      player_thread = ecore_thread_feedback_run(_player_job, NULL, NULL, NULL,
456                                                NULL, EINA_TRUE);
457 #endif
458 }
459
460 void
461 _edje_multisense_shutdown(void)
462 {
463 #ifdef ENABLE_MULTISENSE
464    Edje_Multisense_Sound_Action command = {0,};
465    MULTISENSE_FACTORY_SHUTDOWN_FUNC multisense_factory_shutdown;
466
467    if (m) multisense_factory_shutdown
468             = eina_module_symbol_get(m,"multisense_factory_shutdown");
469    if (multisense_factory_shutdown && msdata)
470          multisense_factory_shutdown(msdata->msenv);
471    if (player_thread) ecore_thread_cancel(player_thread);
472    if (pipe_initialized)
473      {
474         //unblock the select() in player worker thread
475         command.action = EDJE_SOUND_LAST;
476         write(command_pipe[1], &command, sizeof(command));
477      }
478 #endif
479 }