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