Initialize Tizen 2.3
[framework/uifw/edje_multisense_plugin.git] / mobile / src / tizen_sound_player / tizen_sound_player.c
1 /*
2  * Remix Stream Player: TIZEN device output
3  *
4  * Govindaraju SM <govi.sm@samsung.com>, August 2011
5  * Prince Kumar Dubey <prince.dubey@samsung.com>, August 2011
6  */
7
8 #include "config.h"
9 #include <mm_sound.h>
10 #include <remix/remix.h>
11 #include <Eina.h>
12 #include <Ecore.h>
13 #ifdef HAVE_LIBSNDFILE
14 #include <sndfile.h>
15 #endif
16 #include <vconf.h>
17
18 int _edje_multisense_default_log_dom = -1;
19
20 #ifdef ERR
21 # undef ERR
22 #endif
23 #define ERR(...) EINA_LOG_DOM_ERR(_edje_multisense_default_log_dom, __VA_ARGS__)
24 #ifdef WRN
25 # undef WRN
26 #endif
27 #define WRN(...) EINA_LOG_DOM_WARN(_edje_multisense_default_log_dom, __VA_ARGS__)
28
29 #define STREAM_PLAYER_BUFFERLEN 2048
30 #define DEFAULT_FORMAT MMSOUND_PCM_S16_LE
31
32 typedef struct _RemixPlayerData RemixPlayerData;
33 typedef short PLAYER_PCM;
34
35 struct _RemixPlayerData {
36    RemixPCM databuffer[STREAM_PLAYER_BUFFERLEN];
37    PLAYER_PCM *playbuffer;
38    MMSoundPcmHandle_t handle;
39    MMSoundPcmChannel_t channel;
40    RemixPCM max_value;
41    RemixEnv *env;
42    RemixBase *base;
43    int snd_on;
44    int tch_snd_on;
45    Eina_Bool vrecord_on;
46    int buffer_size;
47    int old_buffer_size;
48    int stereo;
49    int frequency;
50 };
51
52 /* Optimisation dependencies: none */
53 static RemixBase *remix_player_optimise (RemixEnv *env, RemixBase *base);
54
55 static int
56 remix_player_open_device (RemixEnv *env, RemixBase *base)
57 {
58    RemixPlayerData *player_data = remix_base_get_instance_data(env, base);
59
60    if (!player_data) return -1;
61
62    if (player_data->handle) return 0;
63
64    player_data->old_buffer_size = player_data->buffer_size;
65    player_data->buffer_size = mm_sound_pcm_play_open_no_session(&player_data->handle,
66                                                      player_data->frequency,
67                                                      player_data->channel,
68                                                      DEFAULT_FORMAT,
69                                                      VOLUME_TYPE_SYSTEM);
70    if (mm_sound_pcm_play_start(player_data->handle) < 0)
71      {
72         remix_set_error (env, REMIX_ERROR_SYSTEM);
73         return -1;
74      }
75
76    if (player_data->buffer_size < 0)
77      {
78         remix_set_error (env, REMIX_ERROR_SYSTEM);
79         return -1;
80      }
81
82    if (player_data->old_buffer_size < player_data->buffer_size)
83      {
84         if (player_data->playbuffer) free(player_data->playbuffer);
85         player_data->playbuffer = calloc(sizeof(PLAYER_PCM), player_data->buffer_size);
86         if (!player_data->playbuffer)
87           {
88              remix_set_error (env, REMIX_ERROR_SYSTEM);
89              return -1;
90           }
91    }
92
93    return 0;
94 }
95
96 static void
97 _vconf_noti_key_changed_cb(keynode_t *node, void *data)
98 {
99    RemixPlayerData *player = (RemixPlayerData *)data;
100    char *keyname = vconf_keynode_get_name(node);
101    int sound_status = 0;
102
103    if (strcmp(keyname, VCONFKEY_SETAPPL_SOUND_STATUS_BOOL) == 0)
104      player->snd_on = vconf_keynode_get_bool(node);
105
106    if (strcmp(keyname, VCONFKEY_SETAPPL_TOUCH_SOUNDS_BOOL) == 0)
107      player->tch_snd_on = vconf_keynode_get_bool(node);
108
109    if (strcmp(keyname, VCONFKEY_SOUND_STATUS) == 0)
110      {
111         vconf_get_int(VCONFKEY_SOUND_STATUS, &sound_status);
112         player->vrecord_on = sound_status & VCONFKEY_SOUND_STATUS_AVRECORDING;
113      }
114
115 }
116
117 static RemixBase *
118 remix_player_init (RemixEnv *env, RemixBase *base, CDSet *parameters)
119    {
120    RemixCount nr_channels;
121    CDSet *channels;
122    RemixPlayerData *player_data = calloc(1, sizeof (RemixPlayerData));
123    int sound_status = 0;
124
125    if (!player_data)
126      {
127         remix_set_error(env, REMIX_ERROR_SYSTEM);
128         return RemixNone;
129      }
130
131    remix_base_set_instance_data(env, base, player_data);
132    channels = remix_get_channels (env);
133
134     nr_channels = cd_set_size (env, channels);
135     if (nr_channels == 1)
136       {
137          player_data->stereo = 0;
138          player_data->channel = MMSOUND_PCM_MONO;
139       }
140     else if (nr_channels == 2)
141       {
142          player_data->stereo = 1;
143          player_data->channel = MMSOUND_PCM_STEREO;
144       }
145
146    player_data->frequency = remix_get_samplerate(env);
147    player_data->buffer_size = 0;
148    player_data->max_value = (RemixPCM) SHRT_MAX / 2;
149
150    base = remix_player_optimise (env, base);
151    player_data->env = env;
152    player_data->base = base;
153
154    if (vconf_get_bool(VCONFKEY_SETAPPL_SOUND_STATUS_BOOL,
155                       &player_data->snd_on) < 0)
156      WRN("\nFail to get VCONFKEY_SETAPPL_SOUND_STATUS_BOOL boolean value");
157
158    if (vconf_get_bool(VCONFKEY_SETAPPL_TOUCH_SOUNDS_BOOL,
159                       &player_data->tch_snd_on) < 0)
160      WRN("\nFail to get VCONFKEY_SETAPPL_TOUCH_SOUNDS_BOOL boolean value");
161
162    if (vconf_notify_key_changed(VCONFKEY_SETAPPL_SOUND_STATUS_BOOL,
163                                 _vconf_noti_key_changed_cb, player_data) < 0)
164      WRN("\nFail to register VCONFKEY_SETAPPL_SOUND_STATUS_BOOL key callback");
165
166    if (vconf_notify_key_changed(VCONFKEY_SETAPPL_TOUCH_SOUNDS_BOOL,
167                                 _vconf_noti_key_changed_cb, player_data) < 0)
168      WRN("\nFail to register VCONFKEY_SETAPPL_SOUND_STATUS_BOOL key callback");
169
170    if (vconf_notify_key_changed(VCONFKEY_SYSMAN_EARJACK,
171                                 _vconf_noti_key_changed_cb, player_data) < 0)
172      WRN("\nFail to register VCONFKEY_SYSMAN_EARJACK key callback");
173
174    if (vconf_notify_key_changed(VCONFKEY_SOUND_STATUS,
175                                    _vconf_noti_key_changed_cb, player_data) < 0)
176      WRN("\nFail to get VCONFKEY_SOUND_STATUS int value");
177
178    if (vconf_get_int(VCONFKEY_SOUND_STATUS, &sound_status) < 0)
179      WRN("\nFail to get VCONFKEY_SOUND_STATUS int value");
180    player_data->vrecord_on = sound_status & VCONFKEY_SOUND_STATUS_AVRECORDING;
181
182    return base;
183 }
184
185
186 static RemixBase *
187 remix_player_clone (RemixEnv *env, RemixBase *base)
188 {
189    RemixBase *new_player = remix_base_new (env);
190
191    remix_player_init( env, new_player,  NULL);
192    return new_player;
193 }
194
195 static int
196 remix_player_destroy (RemixEnv *env, RemixBase *base)
197 {
198    RemixPlayerData *player_data = remix_base_get_instance_data(env, base);
199
200    if (vconf_ignore_key_changed(VCONFKEY_SETAPPL_SOUND_STATUS_BOOL,
201                                 _vconf_noti_key_changed_cb) < 0)
202      WRN("\nFail to unregister VCONFKEY_SETAPPL_SOUND_STATUS_BOOL key callback");
203    if (vconf_ignore_key_changed(VCONFKEY_SETAPPL_TOUCH_SOUNDS_BOOL,
204                                 _vconf_noti_key_changed_cb) < 0)
205      WRN("\nFail to unregister VCONFKEY_SETAPPL_TOUCH_SOUNDS_BOOL key callback");
206    if (vconf_ignore_key_changed(VCONFKEY_SYSMAN_EARJACK,
207                                 _vconf_noti_key_changed_cb) < 0)
208      WRN("\nFail to unregister VCONFKEY_SYSMAN_EARJACK key callback");
209    if (vconf_ignore_key_changed(VCONFKEY_SOUND_STATUS,
210                                 _vconf_noti_key_changed_cb) < 0)
211      WRN("\nFail to unregister VCONFKEY_SOUND_STATUS key callback");
212
213    if (player_data->handle)
214      {
215         if (mm_sound_pcm_play_stop(player_data->handle) < 0)
216           remix_set_error (env, REMIX_ERROR_SYSTEM);
217         else
218           mm_sound_pcm_play_close(player_data->handle);
219         player_data->handle = NULL;
220      }
221
222    if (player_data->playbuffer) free(player_data->playbuffer);
223    free (player_data);
224    // TODO: base must be freed, otherwise memory leak.
225    // Need to check why this call create problem in player plugin load.
226 //   if (base) remix_free(base);
227    return 0;
228 }
229
230 static int
231 remix_player_ready (RemixEnv *env, RemixBase *base)
232 {
233    RemixPlayerData *player_data = remix_base_get_instance_data(env, base);
234    RemixCount nr_channels;
235    CDSet *channels;
236    int samplerate;
237
238    channels = remix_get_channels (env);
239    samplerate = (int) remix_get_samplerate (env);
240
241    nr_channels = cd_set_size (env, channels);
242
243    return (samplerate == player_data->frequency &&
244           ((nr_channels == 1 && player_data->stereo == 0) ||
245            (nr_channels > 1 && player_data->stereo == 1)));
246 }
247
248 static RemixBase *
249 remix_player_prepare (RemixEnv *env, RemixBase *base)
250 {
251    return base;
252 }
253
254 static RemixCount
255 remix_player_playbuffer (RemixEnv *env, RemixPlayerData *player, RemixPCM *data,
256           RemixCount count)
257 {
258    RemixCount i;
259    RemixPCM value;
260    int ret = count;
261    size_t length;
262
263    length = count * sizeof(RemixCount);
264
265    for (i = 0; i < count; i++)
266      {
267         value = *data++ * (player->max_value);
268         *(player->playbuffer + i) = (PLAYER_PCM) value;
269      }
270
271    if (player->handle)
272      ret = mm_sound_pcm_play_write(player->handle,
273                                      player->playbuffer,
274                                      length);
275    if (ret < 0)
276      ERR("Write Fail\n");
277
278    return length;
279 }
280
281 /* An RemixChunkFunc for making noise */
282 static RemixCount
283 remix_player_chunk (RemixEnv *env, RemixChunk *chunk, RemixCount offset,
284           RemixCount count, int channelname, void *data)
285 {
286    RemixPlayerData *player = (RemixPlayerData *)data;
287    RemixCount remaining = count, written = 0, n, playcount;
288    RemixPCM *d;
289
290    while (remaining > 0)
291      {
292         playcount = MIN (remaining, player->buffer_size);
293
294         d = &chunk->data[offset];
295         n = remix_player_playbuffer (env, player, d, playcount);
296
297         if (n == -1)
298           return -1;
299         else
300           n /= sizeof (PLAYER_PCM);
301
302         offset += n;
303         written += n;
304         remaining -= n;
305      }
306
307    return written;
308 }
309
310 static RemixCount
311 remix_player_process (RemixEnv *env, RemixBase *base, RemixCount count,
312             RemixStream *input, RemixStream *output)
313 {
314    RemixCount remaining = count, processed = 0, n, nn, nr_channels;
315    RemixPlayerData *player_data = remix_base_get_instance_data(env, base);
316    int sound_status;
317
318    if (!player_data) return 0;
319
320    if (!player_data->handle) remix_player_open_device (env, base);
321
322    if ((!player_data->snd_on) || (!player_data->tch_snd_on)
323          || (player_data->vrecord_on) ) return count;
324
325    nr_channels = remix_stream_nr_channels (env, input);
326
327    if (nr_channels == 1 && player_data->stereo == 0)
328      { /* MONO */
329         return remix_stream_chunkfuncify (env, input, count,
330                   remix_player_chunk, player_data);
331      }
332    else if (nr_channels == 2 && player_data->stereo == 1)
333      { /* STEREO */
334         while (remaining > 0)
335           {
336              n = MIN (remaining, (player_data->buffer_size / 2) );
337              n = remix_stream_interleave_2 (env, input,
338                  REMIX_CHANNEL_LEFT, REMIX_CHANNEL_RIGHT,
339                  player_data->databuffer, n);
340              nn = 2 * n;
341              nn = remix_player_playbuffer (env, player_data,
342                                            player_data->databuffer, nn);
343
344              processed += n;
345              remaining -= n;
346           }
347         return processed;
348      }
349    else
350      {
351         ERR("[remix_player_process] unsupported stream/output channel\n");
352         ERR ("combination %ld / %d\n", nr_channels, player_data->stereo ? 2 : 1);
353         return -1;
354      }
355 }
356
357 static RemixCount
358 remix_player_length (RemixEnv *env, RemixBase *base)
359 {
360    return REMIX_COUNT_INFINITE;
361 }
362
363 static RemixCount
364 remix_player_seek (RemixEnv *env, RemixBase *base, RemixCount count)
365 {
366    return count;
367 }
368
369 static Eina_Bool
370 _remix_mm_handle_close(void *data)
371 {
372    RemixPlayerData *player_data = data;
373
374    if (!player_data) return EINA_FALSE;
375    if (!player_data->handle) return EINA_FALSE;
376
377    if (mm_sound_pcm_play_stop(player_data->handle) < 0)
378      {
379         player_data->handle = NULL;
380         return EINA_FALSE;
381      }
382    else
383      mm_sound_pcm_play_close(player_data->handle);
384    player_data->handle = NULL;
385
386
387    return EINA_TRUE;
388 }
389
390 static int
391 remix_player_flush (RemixEnv *env, RemixBase *base)
392 {
393    return 0;
394 }
395
396 static int
397 remix_player_reset (RemixEnv *env, RemixBase *base)
398 {
399    RemixPlayerData *player_data = remix_base_get_instance_data(env, base);
400
401    if (!player_data) return -1;
402
403    if (_remix_mm_handle_close(player_data)) return 0;
404    return -1;
405 }
406
407 static struct _RemixMethods _remix_player_methods = {
408    remix_player_clone,
409    remix_player_destroy,
410    remix_player_ready,
411    remix_player_prepare,
412    remix_player_process,
413    remix_player_length,
414    remix_player_seek,
415    remix_player_flush,
416    remix_player_reset,
417 };
418
419 static RemixBase *
420 remix_player_optimise (RemixEnv *env, RemixBase *base)
421 {
422    remix_base_set_methods (env, base, &_remix_player_methods);
423    return base;
424 }
425
426 static struct _RemixMetaText tizen_player_metatext = {
427    "tizen_snd_player",
428    "TIZEN Sound Player",
429    "Output the stream into TIZEN System",
430    "Copyright (C) 2011, Samsung Electronics Co., Ltd.",
431    "http://www.samsung.com",
432    REMIX_ONE_AUTHOR ("govi.sm@samsung.com", "prince.dubey@samsung.com"),
433 };
434
435 static struct _RemixPlugin tizen_player_plugin = {
436    &tizen_player_metatext,
437    REMIX_FLAGS_NONE,
438    CD_EMPTY_SET, /* init scheme */
439    remix_player_init,
440    CD_EMPTY_SET, /* process scheme */
441    NULL, /* suggests */
442    NULL, /* plugin data */
443    NULL  /* destroy */
444 };
445
446 EAPI CDList *
447 remix_load (RemixEnv *env)
448 {
449    CDList *plugins = cd_list_new (env);
450    plugins = cd_list_prepend (env, plugins,
451                              CD_POINTER(&tizen_player_plugin));
452    return plugins;
453 }