Imported Upstream version 1.7.1
[platform/upstream/edje.git] / src / modules / eet_snd_reader / eet_snd_reader.c
1 /*
2  * RemixSnd_eetfile: a libsnd EET Virtual file handler
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 <math.h>
10 #include <sndfile.h>
11 #include <remix/remix.h>
12 #include <Eet.h>
13
14 #define PATH_KEY     1
15 #define SOUND_ID_KEY 2
16 #define SPEED_KEY    3
17 #define BLOCK_FRAMES 8192
18
19 static RemixBase *remix_eet_sndfile_optimise(RemixEnv *env, RemixBase *sndfile);
20
21 typedef struct _VIO_DATA VIO_DATA;
22 typedef struct _SndInstanceData SndInstanceData;
23
24 struct _VIO_DATA
25 {
26    sf_count_t  offset, length;
27    const char *data;
28 };
29
30 struct _SndInstanceData
31 {
32    /* plugin parameters */
33    char       *path;
34    char       *sound_id;
35    double      speed;
36
37    /* Edj & Sndfile Reader */
38    Eet_File   *efp;
39    SNDFILE    *pcm_fp;
40    SF_INFO    *snd_info;
41    VIO_DATA   *vio_data;
42
43    /* PCM buffers */
44    RemixPCM   *readbuf;
45    RemixPCM   *inbuf;
46    RemixPCM   *outbuf;
47
48    /* Resample stuffs */
49    RemixPCM    prevreadbuf[2];
50    int         enable_resample;
51    double      rs_ratio;
52    RemixCount  resample_len;
53    RemixCount  in_avail;
54    RemixCount  out_generated;
55    RemixCount  required_resamples;
56 };
57
58 static sf_count_t
59 eet_snd_file_get_length(void *user_data)
60 {
61    VIO_DATA *vf = user_data;
62    return vf->length;
63 }
64
65 static sf_count_t
66 eet_snd_file_seek(sf_count_t offset, int whence, void *user_data)
67 {
68    VIO_DATA *vf = user_data;
69    
70    switch (whence)
71      {
72       case SEEK_SET:
73         vf->offset = offset;
74         break;
75       case SEEK_CUR:
76         vf->offset += offset;
77         break;
78       case SEEK_END:
79         vf->offset = vf->length + offset;
80         break;
81       default:
82         break;
83      }
84    return vf->offset;
85 }
86
87 static sf_count_t
88 eet_snd_file_read(void *ptr, sf_count_t count, void *user_data)
89 {
90    VIO_DATA *vf = user_data;
91
92    if ((vf->offset + count) > vf->length)
93      count = vf->length - vf->offset;
94    memcpy(ptr, vf->data + vf->offset, count);
95    vf->offset += count;
96    return count;
97 }
98
99 static sf_count_t
100 eet_snd_file_tell(void *user_data)
101 {
102    VIO_DATA *vf = user_data;
103    return vf->offset;
104 }
105
106 static int
107 remix_init_resampler_data(RemixEnv *env, RemixBase *base)
108 {
109    SndInstanceData *si = remix_base_get_instance_data(env, base);
110
111    si->rs_ratio = remix_get_samplerate(env) / si->snd_info->samplerate;
112    si->rs_ratio /= si->speed;
113    si->resample_len = (si->snd_info->frames * si->rs_ratio);
114
115    si->outbuf = malloc(sizeof(RemixPCM) * BLOCK_FRAMES * 2);
116    if (!si->outbuf) return 0;
117    if ((si->rs_ratio == 1.0)/* && (si->snd_info->channels == 2)*/)
118      {
119         si->enable_resample = 0;
120         return 1;
121      }
122    else
123      si->enable_resample = 1;
124
125    si->in_avail = 0;
126    si->out_generated = 0;
127    si->inbuf = malloc(sizeof(RemixPCM) * BLOCK_FRAMES *
128                       si->snd_info->channels);
129    if (!si->inbuf) return 0;
130    return 1;
131 }
132
133 static RemixBase *
134 remix_eet_sndfile_create(RemixEnv *env, RemixBase *sndfile, const char *path, const char *sound_id, const double speed)
135 {
136    SF_VIRTUAL_IO *eet_vio = NULL;
137    SndInstanceData *si;
138    const void *sound_data;
139    int sound_size;
140
141    if ((!path) || (!sound_id)) return NULL;
142
143    si = calloc(1, sizeof(SndInstanceData));
144    if (!si) goto err;
145    remix_base_set_instance_data(env, sndfile, si);
146
147    si->path = strdup(path);
148    si->sound_id = strdup(sound_id);
149    si->speed = speed;
150
151    si->efp = eet_open(path, EET_FILE_MODE_READ);
152    if (!si->efp) goto err;
153
154    // xxx: eet_read_direct does not work on Threads, using eet_read.
155    sound_data = eet_read(si->efp, sound_id, &(sound_size));
156    eet_close(si->efp);
157    si->efp = NULL;
158    if (sound_data == NULL) goto err;
159
160    eet_vio = calloc(1, sizeof(SF_VIRTUAL_IO));
161    if (!eet_vio) goto err;
162
163    /* Set up func pointers to read snd file directly from EET. */
164    eet_vio->get_filelen = eet_snd_file_get_length;
165    eet_vio->seek = eet_snd_file_seek;
166    eet_vio->read = eet_snd_file_read;
167    eet_vio->tell = eet_snd_file_tell;
168
169    si->vio_data = calloc(1, sizeof(VIO_DATA));
170    if (!si->vio_data) goto err;
171    si->vio_data->offset = 0;
172    si->vio_data->length = sound_size;
173    si->vio_data->data = sound_data;
174
175    si->snd_info = calloc(1, sizeof(SF_INFO));
176    if (!si->snd_info) goto err;
177
178    si->pcm_fp = sf_open_virtual(eet_vio, SFM_READ, si->snd_info, si->vio_data);
179    if (!si->pcm_fp) goto err;
180    free(eet_vio);
181    eet_vio = NULL;
182
183    if (!remix_init_resampler_data(env, sndfile)) goto err;
184    si->out_generated = 0;
185
186    return sndfile;
187
188 err:
189    if (eet_vio) free(eet_vio);
190    remix_set_error(env, REMIX_ERROR_SYSTEM);
191    remix_destroy(env, (RemixBase *)sndfile);
192    return RemixNone;
193 }
194
195 static RemixBase *
196 remix_eet_sndfile_reader_init(RemixEnv *env, RemixBase *base, CDSet *parameters)
197 {
198    char *file_path, *sound_id;
199    double speed;
200
201    file_path = (cd_set_find(env, parameters, PATH_KEY)).s_string;
202    sound_id = (cd_set_find(env, parameters, SOUND_ID_KEY)).s_string;
203    speed = (cd_set_find(env, parameters, SPEED_KEY)).s_double;
204
205    if (!remix_eet_sndfile_create(env, base, file_path, sound_id, speed))
206      return RemixNone;
207    remix_eet_sndfile_optimise (env, base);
208    return base;
209 }
210
211 static RemixBase *
212 remix_eet_sndfile_clone(RemixEnv *env, RemixBase *base)
213 {
214    SndInstanceData *si = remix_base_get_instance_data(env, base);
215    RemixBase *new_sndfile = remix_base_new(env);
216
217    remix_eet_sndfile_create(env, new_sndfile, si->path, si->sound_id, si->speed);
218    remix_eet_sndfile_optimise(env, new_sndfile);
219    return new_sndfile;
220 }
221
222 static int
223 remix_eet_sndfile_destroy(RemixEnv *env, RemixBase *base)
224 {
225    SndInstanceData *si = remix_base_get_instance_data(env, base);
226    if (si)
227      {
228         sf_close (si->pcm_fp);
229         eet_close(si->efp);
230         if (si->path) free(si->path);
231         if (si->sound_id) free(si->sound_id);
232         if (si->snd_info) free(si->snd_info);
233         if (si->efp) eet_close(si->efp);
234         if (si->inbuf) free(si->inbuf);
235         if (si->outbuf) free(si->outbuf);
236         if (si->vio_data) free(si->vio_data);
237         free(si);
238       }
239    if (base) free (base);
240    return 0;
241 }
242
243 static int
244 remix_pcm_resample(SndInstanceData *si)
245 {
246    RemixPCM *src, *dst, *srcbase;
247    int i = 0, in_samples, pos, total, chnum, reqsamp, avail;
248    int interp = 1;
249    
250    dst = si->outbuf + (si->out_generated * 2);
251    in_samples = (double)si->required_resamples / si->rs_ratio;
252    chnum = si->snd_info->channels;
253    reqsamp = si->required_resamples;
254    avail = si->in_avail;
255    srcbase = si->readbuf;
256    if ((interp) && (si->rs_ratio >= 1.0))
257      {
258         // linear interpolation of resampling for lower quality samples
259         // so they don't get high requency aliasing effects
260         for (i = 0; i < reqsamp; i++)
261           {
262              float fpos, fpos1;
263              RemixPCM psam[2];
264              
265              fpos = (float)(i * in_samples) / (float)reqsamp;
266              pos = fpos;
267              if (pos >= avail) break;
268              fpos -= pos;
269              fpos1 = 1.0 - fpos;
270              src = srcbase + (pos * chnum);
271              if (chnum == 2)
272                {
273                   if (pos == 0)
274                     {
275                        psam[0] = si->prevreadbuf[0];
276                        psam[1] = si->prevreadbuf[1];
277                     }
278                   else
279                     {
280                        psam[0] = src[0 - 2];
281                        psam[1] = src[1 - 2];
282                     }
283                   *dst++ = (src[0] * fpos) + (psam[0] * fpos1);
284                   *dst++ = (src[1] * fpos) + (psam[1] * fpos1);
285                }
286              else
287                {
288                   if (pos == 0)
289                     psam[0] = si->prevreadbuf[0];
290                   else
291                     psam[0] = src[0 - 1];
292                   *dst++ = (src[0] * fpos) + (psam[0] * fpos1);
293                }
294           }
295      }
296    else
297      {
298         // simple sample-picking/nearest. faster and simpler
299         for (i = 0; i < reqsamp; i++)
300           {
301              pos = (i * in_samples) / reqsamp;
302              if (pos >= avail) break;
303              src = srcbase + (pos * chnum);
304              if (chnum == 2)
305                {
306                   *dst++ = src[0];
307                   *dst++ = src[1];
308                }
309              else
310                *dst++ = src[0];
311           }
312      }
313    si->out_generated += i;
314    total = (i * in_samples) / reqsamp;
315    si->readbuf += total * chnum;
316    si->in_avail -= total;
317    return total;
318 }
319
320 /* An RemixChunkFunc for creating sndfile */
321 static RemixCount
322 remix_eet_sndfile_read_update(RemixEnv *env, RemixBase *sndfile, RemixCount count)
323 {
324    SndInstanceData *si = remix_base_get_instance_data(env, sndfile);
325    
326    si->out_generated = 0;
327    if (si->enable_resample)
328      {
329         RemixCount gen = 0;
330
331         while (gen < count)
332           {
333              if (si->in_avail <= 0)
334                {
335                   si->in_avail = sf_readf_float(si->pcm_fp, si->inbuf, BLOCK_FRAMES);
336                   si->readbuf = si->inbuf;
337                }
338              si->required_resamples = (count - gen);
339              remix_pcm_resample(si);
340              if (si->snd_info->channels == 2)
341                {
342                   si->prevreadbuf[0] = si->readbuf[-2];
343                   si->prevreadbuf[1] = si->readbuf[-1];
344                }
345              else
346                {
347                   si->prevreadbuf[0] = si->readbuf[-1];
348                }
349              gen += si->out_generated;
350           }
351         si->out_generated = gen;
352      }
353    else
354      {
355         si->out_generated = sf_readf_float(si->pcm_fp, si->outbuf, count);
356      }
357    return si->out_generated;
358 }
359
360 static RemixCount
361 remix_eet_sndfile_read_into_chunk(RemixEnv *env, RemixChunk *chunk, RemixCount offset, RemixCount count, int channelname, void *data)
362 {
363    RemixBase *sndfile = data;
364    SndInstanceData *si = remix_base_get_instance_data(env, sndfile);
365    RemixPCM *d, *p;
366    RemixCount remaining = count, written = 0, n, i;
367
368    d = &chunk->data[offset];
369    n = MIN(remaining, BLOCK_FRAMES);
370    // Need parameter support to advance the data reading
371    if (channelname == 0)
372      remix_eet_sndfile_read_update(env, sndfile, n);
373    n = MIN(si->out_generated, remaining);
374    p = si->outbuf;
375    if (si->snd_info->channels > 1) p += channelname;
376    for (i = 0; i < n; i++)
377      {
378         *d++ = *p;
379         p += si->snd_info->channels;
380      }
381    if (n == 0) n = _remix_pcm_set(d, 0.0, remaining);
382    remaining -= n;
383    written += n;
384    return written;
385 }
386
387 static RemixCount
388 remix_eet_sndfile_reader_process(RemixEnv *env, RemixBase *base, RemixCount count, RemixStream *input __UNUSED__, RemixStream *output)
389 {
390    return remix_stream_chunkfuncify(env, output, count, 
391                                     remix_eet_sndfile_read_into_chunk,
392                                     base);
393 }
394
395 static RemixCount
396 remix_eet_sndfile_length(RemixEnv *env, RemixBase *base)
397 {
398    SndInstanceData *si = remix_base_get_instance_data(env, base);
399    return si->resample_len;
400 }
401
402 static RemixCount
403 remix_eet_sndfile_seek(RemixEnv *env, RemixBase *base, RemixCount offset)
404 {
405    SndInstanceData *si = remix_base_get_instance_data(env, base);
406    return sf_seek(si->pcm_fp, offset, SEEK_SET);
407 }
408
409 static struct _RemixMethods _remix_eet_sndfile_reader_methods =
410 {
411    remix_eet_sndfile_clone,
412    remix_eet_sndfile_destroy,
413    NULL, /* ready */
414    NULL, /* prepare */
415    remix_eet_sndfile_reader_process,
416    remix_eet_sndfile_length,
417    remix_eet_sndfile_seek,
418    NULL, /* flush */
419 };
420
421 static RemixBase *
422 remix_eet_sndfile_optimise(RemixEnv *env, RemixBase *sndfile)
423 {
424    remix_base_set_methods(env, sndfile, &_remix_eet_sndfile_reader_methods);
425    return sndfile;
426 }
427
428 static struct _RemixParameterScheme path_scheme =
429 {
430    "path",
431    "Path to sound file",
432    REMIX_TYPE_STRING,
433    REMIX_CONSTRAINT_TYPE_NONE,
434    {NULL},
435    REMIX_HINT_FILENAME,
436 };
437
438 static struct _RemixParameterScheme sound_id_scheme =
439 {
440    "sound_id",
441    "Sound Id (Key) inside EET",
442    REMIX_TYPE_STRING,
443    REMIX_CONSTRAINT_TYPE_NONE,
444    {NULL},
445    REMIX_HINT_DEFAULT,
446 };
447
448 static struct _RemixParameterScheme speed_scheme =
449 {
450    "speed",
451    "Sound Play Speed",
452    REMIX_TYPE_FLOAT,
453    REMIX_CONSTRAINT_TYPE_NONE,
454    {NULL},
455    REMIX_HINT_DEFAULT,
456 };
457
458 static struct _RemixMetaText eet_sndfile_reader_metatext =
459 {
460    "eet_sndfile_reader",
461    "File:: Sound file Reader from EET",
462    "Reads PCM audio files from EET bundle using libsndfile",
463    "Copyright (C) 2011, Samsung Electronics Co., Ltd.",
464    "http://www.samsung.com",
465    REMIX_ONE_AUTHOR ("govi.sm@samsung.com", "prince.dubey@samsung.com"),
466 };
467
468 static struct _RemixPlugin eet_sndfile_reader_plugin =
469 {
470    &eet_sndfile_reader_metatext,
471    REMIX_FLAGS_NONE,
472    CD_EMPTY_SET, /* init scheme */
473    remix_eet_sndfile_reader_init,
474    CD_EMPTY_SET, /* process scheme */
475    NULL, /* suggests */
476    NULL, /* plugin data */
477    NULL  /* destroy */
478 };
479
480 EAPI CDList *
481 remix_load(RemixEnv *env)
482 {
483    CDList *plugins = cd_list_new(env);
484    
485    eet_sndfile_reader_plugin.init_scheme =
486      cd_set_insert(env, eet_sndfile_reader_plugin.init_scheme, PATH_KEY,
487                    CD_POINTER(&path_scheme));
488    eet_sndfile_reader_plugin.init_scheme =
489      cd_set_insert(env, eet_sndfile_reader_plugin.init_scheme, SOUND_ID_KEY,
490                    CD_POINTER(&sound_id_scheme));
491    eet_sndfile_reader_plugin.init_scheme =
492      cd_set_insert(env, eet_sndfile_reader_plugin.init_scheme, SPEED_KEY,
493                    CD_POINTER(&speed_scheme));
494    
495    plugins = cd_list_prepend(env, plugins,
496                              CD_POINTER(&eet_sndfile_reader_plugin));
497    return plugins;
498 }