d052f1ece0c30ff11f458aeea5ee833bc268c3bf
[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 <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <mm_sound.h>
13 #include <remix/remix.h>
14 #include <Eina.h>
15 #ifdef HAVE_LIBSNDFILE
16 #include <sndfile.h>
17 #endif
18 #include <vconf.h>
19
20 int _edje_multisense_default_log_dom = -1;
21
22 #ifdef ERR
23 # undef ERR
24 #endif
25 #define ERR(...) EINA_LOG_DOM_ERR(_edje_multisense_default_log_dom, __VA_ARGS__)
26 #ifdef WRN
27 # undef WRN
28 #endif
29 #define WRN(...) EINA_LOG_DOM_WARN(_edje_multisense_default_log_dom, __VA_ARGS__)
30
31 #define STREAM_PLAYER_BUFFERLEN 2048
32 #define DEFAULT_FORMAT MMSOUND_PCM_S16_LE
33
34 typedef struct _RemixPlayerData RemixPlayerData;
35 typedef short PLAYER_PCM;
36
37 struct _RemixPlayerData {
38    RemixPCM databuffer[STREAM_PLAYER_BUFFERLEN];
39    PLAYER_PCM *playbuffer;
40    MMSoundPcmHandle_t handle;
41    MMSoundPcmChannel_t channel;
42    RemixPCM max_value;
43    RemixEnv *env;
44    RemixBase *base;
45    int snd_on;
46    unsigned int buffer_size;
47    int stereo;
48    int frequency;
49 };
50
51 /* Optimisation dependencies: none */
52 static RemixBase *remix_player_optimise (RemixEnv *env, RemixBase *base);
53
54 static RemixBase *
55 remix_player_reset_device (RemixEnv *env, RemixBase *base)
56 {
57    int old_buffer_size;
58    RemixPlayerData *player_data = remix_base_get_instance_data(env, base);
59
60    if (player_data->handle)
61      {
62         mm_sound_pcm_play_close(player_data->handle);
63         player_data->handle = NULL;
64      }
65    old_buffer_size = player_data->buffer_size;
66    player_data->buffer_size = mm_sound_pcm_play_open(&player_data->handle,
67                                                      player_data->frequency,
68                                                      player_data->channel,
69                                                      DEFAULT_FORMAT,
70                                                      VOLUME_TYPE_SYSTEM);
71    if(player_data->buffer_size < 0)
72      {
73         remix_set_error (env, REMIX_ERROR_SYSTEM);
74         return RemixNone;
75      }
76
77    if( old_buffer_size < player_data->buffer_size)
78      {
79         if( player_data->playbuffer ) free(player_data->playbuffer);
80         player_data->playbuffer = calloc(sizeof(PLAYER_PCM), player_data->buffer_size);
81         if(!player_data->playbuffer)
82           {
83              remix_set_error (env, REMIX_ERROR_SYSTEM);
84              return RemixNone;
85           }
86    }
87    return base;
88 }
89
90 static void
91 _vconf_noti_key_changed_cb(keynode_t *node, void *data)
92 {
93    RemixPlayerData *player = (RemixPlayerData *)data;
94    char *keyname = vconf_keynode_get_name(node);
95
96    if (strcmp(keyname, VCONFKEY_SETAPPL_SOUND_STATUS_BOOL) == 0)
97      player->snd_on = vconf_keynode_get_bool(node);
98
99    else if (strcmp(keyname, VCONFKEY_SYSMAN_EARJACK) == 0)
100      {
101         int ear_jack = vconf_keynode_get_int(node);
102         if (ear_jack == VCONFKEY_SYSMAN_EARJACK_REMOVED)
103           remix_player_reset_device (player->env, player->base);
104      }
105 }
106
107 static RemixBase *
108 remix_player_init (RemixEnv *env, RemixBase *base, CDSet *parameters)
109    {
110    RemixCount nr_channels;
111    CDSet *channels;
112    RemixPlayerData *player_data = calloc(1, sizeof (RemixPlayerData));
113
114    if (!player_data)
115      {
116         remix_set_error(env, REMIX_ERROR_SYSTEM);
117         return RemixNone;
118      }
119
120    remix_base_set_instance_data(env, base, player_data);
121    channels = remix_get_channels (env);
122
123     nr_channels = cd_set_size (env, channels);
124     if (nr_channels == 1)
125       {
126          player_data->stereo = 0;
127          player_data->channel = MMSOUND_PCM_MONO;
128       }
129     else if (nr_channels == 2)
130       {
131          player_data->stereo = 1;
132          player_data->channel = MMSOUND_PCM_STEREO;
133       }
134
135    player_data->frequency = remix_get_samplerate(env);
136    player_data->buffer_size = 0;
137    player_data->max_value = (RemixPCM) SHRT_MAX / 2;
138
139    remix_player_reset_device (env, (RemixBase *)base);
140
141    base = remix_player_optimise (env, base);
142    player_data->env = env;
143    player_data->base = base;
144    if (vconf_get_bool(VCONFKEY_SETAPPL_SOUND_STATUS_BOOL, &player_data->snd_on) < 0)
145      WRN("\nFail to get VCONFKEY_SETAPPL_SOUND_STATUS_BOOL boolean value");
146
147    if (vconf_notify_key_changed(VCONFKEY_SETAPPL_SOUND_STATUS_BOOL,_vconf_noti_key_changed_cb, player_data) < 0)
148      WRN("\nFail to register VCONFKEY_SETAPPL_SOUND_STATUS_BOOL key callback");
149    if (vconf_notify_key_changed(VCONFKEY_SYSMAN_EARJACK, _vconf_noti_key_changed_cb, player_data) < 0)
150      WRN("\nFail to register VCONFKEY_SYSMAN_EARJACK key callback");
151
152    return base;
153 }
154
155
156 static RemixBase *
157 remix_player_clone (RemixEnv *env, RemixBase *base)
158 {
159    RemixBase *new_player = remix_base_new (env);
160
161    remix_player_init( env, new_player,  NULL);
162    return new_player;
163 }
164
165 static int
166 remix_player_destroy (RemixEnv *env, RemixBase *base)
167 {
168    RemixPlayerData *player_data = remix_base_get_instance_data(env, base);
169
170    if (vconf_ignore_key_changed(VCONFKEY_SETAPPL_SOUND_STATUS_BOOL,_vconf_noti_key_changed_cb) < 0)
171      WRN("\nFail to unregister VCONFKEY_SETAPPL_SOUND_STATUS_BOOL key callback");
172    if (vconf_ignore_key_changed(VCONFKEY_SYSMAN_EARJACK, _vconf_noti_key_changed_cb) < 0)
173      WRN("\nFail to unregister VCONFKEY_SYSMAN_EARJACK key callback");
174
175    if(player_data->handle)
176      mm_sound_pcm_play_close(player_data->handle);
177    if(player_data->playbuffer) free(player_data->playbuffer);
178    free (player_data);
179    return 0;
180 }
181
182 static int
183 remix_player_ready (RemixEnv *env, RemixBase *base)
184 {
185    RemixPlayerData *player_data = remix_base_get_instance_data(env, base);
186    RemixCount nr_channels;
187    CDSet *channels;
188    int samplerate;
189
190    channels = remix_get_channels (env);
191    samplerate = (int) remix_get_samplerate (env);
192
193    nr_channels = cd_set_size (env, channels);
194
195    return (samplerate == player_data->frequency &&
196           ((nr_channels == 1 && player_data->stereo == 0) ||
197            (nr_channels > 1 && player_data->stereo == 1)));
198 }
199
200 static RemixBase *
201 remix_player_prepare (RemixEnv *env, RemixBase *base)
202 {
203    remix_player_reset_device (env, base);
204    return base;
205 }
206
207 static RemixCount
208 remix_player_playbuffer (RemixEnv *env, RemixPlayerData *player, RemixPCM *data,
209           RemixCount count)
210 {
211    RemixCount i;
212    RemixPCM value;
213    int ret = count;
214    size_t length;
215
216    length = count * sizeof(RemixCount);
217
218    for (i = 0; i < count; i++)
219      {
220         value = *data++ * (player->max_value);
221         *(player->playbuffer + i) = (PLAYER_PCM) value;
222      }
223
224    ret = mm_sound_pcm_play_write(player->handle,
225                                      player->playbuffer,
226                                      length);
227    if(ret < 0)
228      ERR("Write Fail\n");
229
230    return length;
231 }
232
233 /* An RemixChunkFunc for making noise */
234 static RemixCount
235 remix_player_chunk (RemixEnv *env, RemixChunk *chunk, RemixCount offset,
236           RemixCount count, int channelname, void *data)
237 {
238    RemixPlayerData *player = (RemixPlayerData *)data;
239    RemixCount remaining = count, written = 0, n, playcount;
240    RemixPCM *d;
241
242    while (remaining > 0)
243      {
244         playcount = MIN (remaining, player->buffer_size);
245
246         d = &chunk->data[offset];
247         n = remix_player_playbuffer (env, player, d, playcount);
248
249         if (n == -1)
250           return -1;
251         else
252           n /= sizeof (PLAYER_PCM);
253
254         offset += n;
255         written += n;
256         remaining -= n;
257      }
258
259    return written;
260 }
261
262 static RemixCount
263 remix_player_process (RemixEnv *env, RemixBase *base, RemixCount count,
264             RemixStream *input, RemixStream *output)
265 {
266    RemixCount remaining = count, processed = 0, n, nn, nr_channels;
267    RemixPlayerData *player_data = remix_base_get_instance_data(env, base);
268
269    if (!player_data->snd_on) return count;
270    nr_channels = remix_stream_nr_channels (env, input);
271
272    if (nr_channels == 1 && player_data->stereo == 0)
273      { /* MONO */
274         return remix_stream_chunkfuncify (env, input, count,
275                   remix_player_chunk, player_data);
276      }
277    else if (nr_channels == 2 && player_data->stereo == 1)
278      { /* STEREO */
279         while (remaining > 0)
280           {
281              n = MIN (remaining, (player_data->buffer_size / 2) );
282              n = remix_stream_interleave_2 (env, input,
283                  REMIX_CHANNEL_LEFT, REMIX_CHANNEL_RIGHT,
284                  player_data->databuffer, n);
285              nn = 2 * n;
286              nn = remix_player_playbuffer (env, player_data,
287                                            player_data->databuffer, nn);
288
289              processed += n;
290              remaining -= n;
291           }
292         return processed;
293      }
294    else
295      {
296         ERR("[remix_player_process] unsupported stream/output channel\n");
297         ERR ("combination %ld / %d\n", nr_channels, player_data->stereo ? 2 : 1);
298         return -1;
299      }
300 }
301
302 static RemixCount
303 remix_player_length (RemixEnv *env, RemixBase *base)
304 {
305    return REMIX_COUNT_INFINITE;
306 }
307
308 static RemixCount
309 remix_player_seek (RemixEnv *env, RemixBase *base, RemixCount count)
310 {
311    return count;
312 }
313
314 static int
315 remix_player_flush (RemixEnv *env, RemixBase *base)
316 {
317    RemixPlayerData *player_data = remix_base_get_instance_data(env, base);
318    remix_player_reset_device (env, base);
319     return 0;
320 }
321
322 static struct _RemixMethods _remix_player_methods = {
323    remix_player_clone,
324    remix_player_destroy,
325    remix_player_ready,
326    remix_player_prepare,
327    remix_player_process,
328    remix_player_length,
329    remix_player_seek,
330    remix_player_flush,
331 };
332
333 static RemixBase *
334 remix_player_optimise (RemixEnv *env, RemixBase *base)
335 {
336    remix_base_set_methods (env, base, &_remix_player_methods);
337    return base;
338 }
339
340 static struct _RemixMetaText tizen_player_metatext = {
341    "tizen_snd_player",
342    "TIZEN Sound Player",
343    "Output the stream into TIZEN System",
344    "Copyright (C) 2011, Samsung Electronics Co., Ltd.",
345    "http://www.samsung.com",
346    REMIX_ONE_AUTHOR ("govi.sm@samsung.com", "prince.dubey@samsung.com"),
347 };
348
349 static struct _RemixPlugin tizen_player_plugin = {
350    &tizen_player_metatext,
351    REMIX_FLAGS_NONE,
352    CD_EMPTY_SET, /* init scheme */
353    remix_player_init,
354    CD_EMPTY_SET, /* process scheme */
355    NULL, /* suggests */
356    NULL, /* plugin data */
357    NULL  /* destroy */
358 };
359
360 EAPI CDList *
361 remix_load (RemixEnv *env)
362 {
363    CDList *plugins = cd_list_new (env);
364    plugins = cd_list_prepend (env, plugins,
365                              CD_POINTER(&tizen_player_plugin));
366    return plugins;
367 }