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