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