1 /*
2 libsndfile plugin for DeaDBeeF Player
3 Copyright (C) 2009-2014 Alexey Yakovenko <waker@users.sourceforge.net>
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 */
19 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #endif
22 #ifndef __linux__
23 #define _LARGEFILE64_SOURCE
24 #endif
25 #include <string.h>
26 #include <sndfile.h>
27 #include <math.h>
28 #include <stdlib.h>
29 #include "../../deadbeef.h"
30
31 #define min(x,y) ((x)<(y)?(x):(y))
32 #define max(x,y) ((x)>(y)?(x):(y))
33
34 //#define trace(...) { fprintf(stderr, __VA_ARGS__); }
35 #define trace(fmt,...)
36
37 static DB_decoder_t plugin;
38 static DB_functions_t *deadbeef;
39
40 typedef struct {
41 DB_fileinfo_t info;
42 SNDFILE *ctx;
43 DB_FILE *file;
44 int startsample;
45 int endsample;
46 int currentsample;
47 int bitrate;
48 int sf_format;
49 int read_as_short;
50 int sf_need_endswap;
51 } sndfile_info_t;
52
53 // vfs wrapper for sf
54 static sf_count_t
sf_vfs_get_filelen(void * user_data)55 sf_vfs_get_filelen (void *user_data) {
56 sndfile_info_t *ctx = user_data;
57 return deadbeef->fgetlength (ctx->file);
58 }
59
60 static sf_count_t
sf_vfs_read(void * ptr,sf_count_t count,void * user_data)61 sf_vfs_read (void *ptr, sf_count_t count, void *user_data) {
62 sndfile_info_t *ctx = user_data;
63 return deadbeef->fread (ptr, 1, count, ctx->file);
64 }
65
66 static sf_count_t
sf_vfs_write(const void * ptr,sf_count_t count,void * user_data)67 sf_vfs_write (const void *ptr, sf_count_t count, void *user_data) {
68 return -1;
69 }
70
71 static sf_count_t
sf_vfs_seek(sf_count_t offset,int whence,void * user_data)72 sf_vfs_seek (sf_count_t offset, int whence, void *user_data) {
73 sndfile_info_t *ctx = user_data;
74 int ret = deadbeef->fseek (ctx->file, offset, whence);
75 if (!ret) {
76 return offset;
77 }
78 return -1;
79 }
80
81 static sf_count_t
sf_vfs_tell(void * user_data)82 sf_vfs_tell (void *user_data) {
83 sndfile_info_t *ctx = user_data;
84 return deadbeef->ftell (ctx->file);
85 }
86
87 static SF_VIRTUAL_IO vfs = {
88 .get_filelen = sf_vfs_get_filelen,
89 .seek = sf_vfs_seek,
90 .read = sf_vfs_read,
91 .write = sf_vfs_write,
92 .tell = sf_vfs_tell
93 };
94
95 static DB_fileinfo_t *
sndfile_open(uint32_t hints)96 sndfile_open (uint32_t hints) {
97 DB_fileinfo_t *_info = malloc (sizeof (sndfile_info_t));
98 memset (_info, 0, sizeof (sndfile_info_t));
99 return _info;
100 }
101
102
103 // taken from libsndfile
104 #define ARRAY_LEN(x) ((int) (sizeof (x) / sizeof ((x) [0])))
105 /* This stores which bit in dwChannelMask maps to which channel */
106 static const struct chanmap_s
107 { int id ;
108 const char * name ;
109 } channel_mask_bits [] =
110 { /* WAVEFORMATEXTENSIBLE doesn't distuingish FRONT_LEFT from LEFT */
111 { SF_CHANNEL_MAP_LEFT, "L" },
112 { SF_CHANNEL_MAP_RIGHT, "R" },
113 { SF_CHANNEL_MAP_CENTER, "C" },
114 { SF_CHANNEL_MAP_LFE, "LFE" },
115 { SF_CHANNEL_MAP_REAR_LEFT, "Ls" },
116 { SF_CHANNEL_MAP_REAR_RIGHT, "Rs" },
117 { SF_CHANNEL_MAP_FRONT_LEFT_OF_CENTER, "Lc" },
118 { SF_CHANNEL_MAP_FRONT_RIGHT_OF_CENTER, "Rc" },
119 { SF_CHANNEL_MAP_REAR_CENTER, "Cs" },
120 { SF_CHANNEL_MAP_SIDE_LEFT, "Sl" },
121 { SF_CHANNEL_MAP_SIDE_RIGHT, "Sr" },
122 { SF_CHANNEL_MAP_TOP_CENTER, "Tc" },
123 { SF_CHANNEL_MAP_TOP_FRONT_LEFT, "Tfl" },
124 { SF_CHANNEL_MAP_TOP_FRONT_CENTER, "Tfc" },
125 { SF_CHANNEL_MAP_TOP_FRONT_RIGHT, "Tfr" },
126 { SF_CHANNEL_MAP_TOP_REAR_LEFT, "Trl" },
127 { SF_CHANNEL_MAP_TOP_REAR_CENTER, "Trc" },
128 { SF_CHANNEL_MAP_TOP_REAR_RIGHT, "Trr" },
129 } ;
130
131
132 static int
wavex_gen_channel_mask(const int * chan_map,int channels)133 wavex_gen_channel_mask (const int *chan_map, int channels)
134 { int chan, mask = 0, bit = -1, last_bit = -1 ;
135
136 if (chan_map == NULL)
137 return 0 ;
138
139 for (chan = 0 ; chan < channels ; chan ++)
140 { int k ;
141
142 for (k = bit + 1 ; k < ARRAY_LEN (channel_mask_bits) ; k++)
143 if (chan_map [chan] == channel_mask_bits [k].id)
144 { bit = k ;
145 break ;
146 } ;
147
148 /* Check for bad sequence. */
149 if (bit <= last_bit)
150 return 0 ;
151
152 mask += 1 << bit ;
153 last_bit = bit ;
154 } ;
155
156 return mask ;
157 } /* wavex_gen_channel_mask */
158
159
160 static int
sndfile_init(DB_fileinfo_t * _info,DB_playItem_t * it)161 sndfile_init (DB_fileinfo_t *_info, DB_playItem_t *it) {
162 sndfile_info_t *info = (sndfile_info_t*)_info;
163
164 SF_INFO inf;
165 deadbeef->pl_lock ();
166 DB_FILE *fp = deadbeef->fopen (deadbeef->pl_find_meta (it, ":URI"));
167 deadbeef->pl_unlock ();
168 if (!fp) {
169 trace ("sndfile: failed to open %s\n", deadbeef->pl_find_meta (it, ":URI"));
170 return -1;
171 }
172 int fsize = deadbeef->fgetlength (fp);
173
174 info->file = fp;
175 info->ctx = sf_open_virtual (&vfs, SFM_READ, &inf, info);
176 if (!info->ctx) {
177 trace ("sndfile: %s: unsupported file format\n");
178 return -1;
179 }
180 _info->plugin = &plugin;
181 info->sf_format = inf.format&SF_FORMAT_SUBMASK;
182 info->sf_need_endswap = sf_command (info->ctx, SFC_RAW_DATA_NEEDS_ENDSWAP, NULL, 0);
183
184 switch (inf.format&SF_FORMAT_SUBMASK) {
185 case SF_FORMAT_PCM_S8:
186 case SF_FORMAT_PCM_U8:
187 _info->fmt.bps = 8;
188 break;
189 case SF_FORMAT_PCM_16:
190 _info->fmt.bps = 16;
191 break;
192 case SF_FORMAT_PCM_24:
193 _info->fmt.bps = 24;
194 break;
195 case SF_FORMAT_FLOAT:
196 _info->fmt.is_float = 1;
197 case SF_FORMAT_PCM_32:
198 _info->fmt.bps = 32;
199 break;
200 default:
201 info->read_as_short = 1;
202 _info->fmt.bps = 16;
203 trace ("[sndfile] unidentified input format: 0x%X\n", inf.format&SF_FORMAT_SUBMASK);
204 break;
205 }
206
207 _info->fmt.channels = inf.channels;
208 _info->fmt.samplerate = inf.samplerate;
209
210 // FIXME: streamer and maybe output plugins need to be fixed to support
211 // arbitrary channelmask
212 //
213 // int channel_map [inf.channels];
214 // int cmdres = sf_command (info->ctx, SFC_GET_CHANNEL_MAP_INFO, channel_map, sizeof (channel_map)) ;
215 // if (cmdres != SF_FALSE) {
216 // // channel map found, convert to channel mask
217 // _info->fmt.channelmask = wavex_gen_channel_mask (channel_map, inf.channels);
218 // }
219 // else
220 {
221 // channel map not found, generate from channel number
222 for (int i = 0; i < inf.channels; i++) {
223 _info->fmt.channelmask |= 1 << i;
224 }
225 }
226
227 _info->readpos = 0;
228 if (it->endsample > 0) {
229 info->startsample = it->startsample;
230 info->endsample = it->endsample;
231 if (plugin.seek_sample (_info, 0) < 0) {
232 return -1;
233 }
234 }
235 else {
236 info->startsample = 0;
237 info->endsample = inf.frames-1;
238 }
239 // hack bitrate
240
241 int totalsamples = inf.frames;
242 float sec = (float)totalsamples / inf.samplerate;
243 if (sec > 0) {
244 info->bitrate = fsize / sec * 8 / 1000;
245 }
246 else {
247 info->bitrate = -1;
248 }
249
250 return 0;
251 }
252
253 static void
sndfile_free(DB_fileinfo_t * _info)254 sndfile_free (DB_fileinfo_t *_info) {
255 sndfile_info_t *info = (sndfile_info_t*)_info;
256 if (info->ctx) {
257 sf_close (info->ctx);
258 }
259 if (info->file) {
260 deadbeef->fclose (info->file);
261 }
262 memset (&info, 0, sizeof (info));
263 }
264
265 static int
sndfile_read(DB_fileinfo_t * _info,char * bytes,int size)266 sndfile_read (DB_fileinfo_t *_info, char *bytes, int size) {
267 sndfile_info_t *info = (sndfile_info_t*)_info;
268 int samplesize = _info->fmt.channels * _info->fmt.bps / 8;
269 if (size / samplesize + info->currentsample > info->endsample) {
270 size = (info->endsample - info->currentsample + 1) * samplesize;
271 trace ("sndfile: size truncated to %d bytes, cursample=%d, endsample=%d\n", size, info->currentsample, info->endsample);
272 if (size <= 0) {
273 return 0;
274 }
275 }
276
277 int n = 0;
278 if (info->read_as_short) {
279 n = sf_readf_short(info->ctx, (short *)bytes, size/samplesize);
280 }
281 else {
282 n = sf_read_raw (info->ctx, (short *)bytes, size);
283
284 if (info->sf_format == SF_FORMAT_PCM_U8) {
285 for (int i = 0; i < n; i++) {
286 int sample = ((uint8_t *)bytes)[i];
287 ((int8_t *)bytes)[i] = sample-0x80;
288 }
289 }
290 else if (info->sf_need_endswap) {
291 switch (info->info.fmt.bps) {
292 case 16:
293 {
294 uint16_t *data = (uint16_t *)bytes;
295 for (int i = 0; i < n/2; i++, data++) {
296 *data = ((*data & 0xff) << 8) | ((*data & 0xff00) >> 8);
297 }
298 }
299 break;
300 case 24:
301 {
302 uint8_t *data = bytes;
303 for (int i = 0; i < n/3; i++, data += 3) {
304 uint8_t temp = data[0];
305 data[0] = data[2];
306 data[2] = temp;
307 }
308 }
309 break;
310 case 32:
311 {
312 uint32_t *data = (uint32_t *)bytes;
313 for (int i = 0; i < n/4; i++, data++) {
314 *data = ((*data & 0xff) << 24) | ((*data & 0xff00) << 8) | ((*data & 0xff0000) >> 8) | ((*data & 0xff0000) >> 24);
315 }
316 }
317 break;
318 }
319 }
320 n /= samplesize;
321 }
322
323 info->currentsample += n;
324
325 size = n * samplesize;
326 _info->readpos = (float)(info->currentsample-info->startsample)/_info->fmt.samplerate;
327 if (info->bitrate > 0) {
328 deadbeef->streamer_set_bitrate (info->bitrate);
329 }
330 return size;
331 }
332
333 static int
sndfile_seek_sample(DB_fileinfo_t * _info,int sample)334 sndfile_seek_sample (DB_fileinfo_t *_info, int sample) {
335 sndfile_info_t *info = (sndfile_info_t*)_info;
336 int ret = sf_seek (info->ctx, sample + info->startsample, SEEK_SET);
337 if (ret < 0) {
338 return -1;
339 }
340 info->currentsample = ret;
341 _info->readpos = (float)(info->currentsample - info->startsample) / _info->fmt.samplerate;
342 return 0;
343 }
344
345 static int
sndfile_seek(DB_fileinfo_t * _info,float sec)346 sndfile_seek (DB_fileinfo_t *_info, float sec) {
347 return sndfile_seek_sample (_info, sec * _info->fmt.samplerate);
348 }
349
350 static DB_playItem_t *
sndfile_insert(ddb_playlist_t * plt,DB_playItem_t * after,const char * fname)351 sndfile_insert (ddb_playlist_t *plt, DB_playItem_t *after, const char *fname) {
352 trace ("adding file %s\n", fname);
353 SF_INFO inf;
354 sndfile_info_t info;
355 memset (&info, 0, sizeof (info));
356 info.file = deadbeef->fopen (fname);
357 if (!info.file) {
358 trace ("sndfile: failed to open %s\n", fname);
359 return NULL;
360 }
361 int64_t fsize = deadbeef->fgetlength (info.file);
362 trace ("file: %p, size: %lld\n", info.file, deadbeef->fgetlength (info.file));
363 trace ("calling sf_open_virtual\n");
364 info.ctx = sf_open_virtual (&vfs, SFM_READ, &inf, &info);
365 if (!info.ctx) {
366 trace ("sndfile: sf_open failed\n");
367 deadbeef->fclose (info.file);
368 return NULL;
369 }
370 trace ("calling sf_open_virtual ok\n");
371 int totalsamples = inf.frames;
372 int samplerate = inf.samplerate;
373 sf_close (info.ctx);
374 deadbeef->fclose (info.file);
375
376 float duration = (float)totalsamples / samplerate;
377 DB_playItem_t *it = deadbeef->pl_item_alloc_init (fname, plugin.plugin.id);
378 deadbeef->pl_add_meta (it, ":FILETYPE", "wav");
379 deadbeef->plt_set_item_duration (plt, it, duration);
380
381 trace ("sndfile: totalsamples=%d, samplerate=%d, duration=%f\n", totalsamples, samplerate, duration);
382
383 char s[100];
384 snprintf (s, sizeof (s), "%lld", fsize);
385 deadbeef->pl_add_meta (it, ":FILE_SIZE", s);
386
387 int bps = -1;
388 switch (inf.format&SF_FORMAT_SUBMASK) {
389 case SF_FORMAT_IMA_ADPCM:
390 case SF_FORMAT_MS_ADPCM:
391 bps = 4;
392 break;
393 case SF_FORMAT_ALAW:
394 case SF_FORMAT_ULAW:
395 case SF_FORMAT_PCM_S8:
396 case SF_FORMAT_PCM_U8:
397 bps = 8;
398 break;
399 case SF_FORMAT_PCM_16:
400 bps = 16;
401 break;
402 case SF_FORMAT_PCM_24:
403 bps = 24;
404 break;
405 case SF_FORMAT_FLOAT:
406 case SF_FORMAT_PCM_32:
407 bps = 32;
408 break;
409 }
410
411 if (bps == -1) {
412 snprintf (s, sizeof (s), "unknown");
413 }
414 else {
415 snprintf (s, sizeof (s), "%d", bps);
416 }
417 deadbeef->pl_add_meta (it, ":BPS", s);
418 snprintf (s, sizeof (s), "%d", inf.channels);
419 deadbeef->pl_add_meta (it, ":CHANNELS", s);
420 snprintf (s, sizeof (s), "%d", samplerate);
421 deadbeef->pl_add_meta (it, ":SAMPLERATE", s);
422 int br = (int)roundf(fsize / duration * 8 / 1000);
423 snprintf (s, sizeof (s), "%d", br);
424 deadbeef->pl_add_meta (it, ":BITRATE", s);
425
426 // sndfile subformats
427 const char *subformats[] = {
428 "",
429 "PCM_S8",
430 "PCM_16",
431 "PCM_24",
432 "PCM_32",
433 "PCM_U8",
434 "FLOAT",
435 "DOUBLE",
436 "",
437 "",
438 "ULAW",
439 "ALAW",
440 "IMA_ADPCM",
441 "MS_ADPCM",
442 "",
443 "",
444 "",
445 "",
446 "",
447 "",
448 "",
449 "GSM610",
450 "VOX_ADPCM",
451 "",
452 "",
453 "",
454 "",
455 "",
456 "",
457 "",
458 "",
459 "G721_32",
460 "G723_24",
461 "G723_40",
462 "",
463 "",
464 "",
465 "",
466 "",
467 "",
468 "",
469 "DWVW_12",
470 "DWVW_16",
471 "DWVW_24",
472 "DWVW_N",
473 "",
474 "",
475 "",
476 "",
477 "",
478 "",
479 "DPCM_8",
480 "DPCM_16",
481 "",
482 "",
483 "",
484 "",
485 "",
486 "",
487 "",
488 "",
489 "VORBIS",
490 };
491
492 if (inf.format&SF_FORMAT_SUBMASK <= SF_FORMAT_VORBIS) {
493 deadbeef->pl_add_meta (it, ":SF_FORMAT", subformats[inf.format&SF_FORMAT_SUBMASK]);
494 }
495
496 DB_playItem_t *cue_after = deadbeef->plt_insert_cue (plt, after, it, totalsamples, samplerate);
497 if (cue_after) {
498 deadbeef->pl_item_unref (it);
499 deadbeef->pl_item_unref (cue_after);
500 return cue_after;
501 }
502
503 deadbeef->pl_add_meta (it, "title", NULL);
504 after = deadbeef->plt_insert_item (plt, after, it);
505 deadbeef->pl_item_unref (it);
506
507 return after;
508 }
509
510 #define DEFAULT_EXTS "wav;aif;aiff;snd;au;paf;svx;nist;voc;ircam;w64;mat4;mat5;pvf;xi;htk;sds;avr;wavex;sd2;caf;wve"
511
512 #define EXT_MAX 100
513
514 static char *exts[EXT_MAX] = {NULL};
515
516 static void
sndfile_init_exts(void)517 sndfile_init_exts (void) {
518 for (int i = 0; exts[i]; i++) {
519 free (exts[i]);
520 }
521 exts[0] = NULL;
522
523 int n = 0;
524 deadbeef->conf_lock ();
525 const char *new_exts = deadbeef->conf_get_str_fast ("sndfile.extensions", DEFAULT_EXTS);
526 while (*new_exts) {
527 if (n >= EXT_MAX) {
528 fprintf (stderr, "sndfile: too many extensions, max is %d\n", EXT_MAX);
529 break;
530 }
531 const char *e = new_exts;
532 while (*e && *e != ';') {
533 e++;
534 }
535 if (e != new_exts) {
536 char *ext = malloc (e-new_exts+1);
537 memcpy (ext, new_exts, e-new_exts);
538 ext[e-new_exts] = 0;
539 exts[n++] = ext;
540 }
541 if (*e == 0) {
542 break;
543 }
544 new_exts = e+1;
545 }
546 deadbeef->conf_unlock ();
547 exts[n] = NULL;
548 }
549
550 static int
sndfile_message(uint32_t id,uintptr_t ctx,uint32_t p1,uint32_t p2)551 sndfile_message (uint32_t id, uintptr_t ctx, uint32_t p1, uint32_t p2) {
552 switch (id) {
553 case DB_EV_CONFIGCHANGED:
554 sndfile_init_exts ();
555 break;
556 }
557 return 0;
558 }
559
560 static int
sndfile_start(void)561 sndfile_start (void) {
562 sndfile_init_exts ();
563 return 0;
564 }
565
566 static int
sndfile_stop(void)567 sndfile_stop (void) {
568 for (int i = 0; exts[i]; i++) {
569 free (exts[i]);
570 }
571 exts[0] = NULL;
572 return 0;
573 }
574
575 static const char settings_dlg[] =
576 "property \"File Extensions (separate with ';')\" entry sndfile.extensions \"" DEFAULT_EXTS "\";\n"
577 ;
578
579
580 // define plugin interface
581 static DB_decoder_t plugin = {
582 .plugin.api_vmajor = 1,
583 .plugin.api_vminor = 0,
584 .plugin.version_major = 1,
585 .plugin.version_minor = 0,
586 .plugin.type = DB_PLUGIN_DECODER,
587 .plugin.id = "sndfile",
588 .plugin.name = "WAV/PCM player",
589 .plugin.descr = "wav/aiff player using libsndfile",
590 .plugin.copyright =
591 "libsndfile plugin for DeaDBeeF Player\n"
592 "Copyright (C) 2009-2014 Alexey Yakovenko <waker@users.sourceforge.net>\n"
593 "\n"
594 "This program is free software; you can redistribute it and/or\n"
595 "modify it under the terms of the GNU General Public License\n"
596 "as published by the Free Software Foundation; either version 2\n"
597 "of the License, or (at your option) any later version.\n"
598 "\n"
599 "This program is distributed in the hope that it will be useful,\n"
600 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
601 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
602 "GNU General Public License for more details.\n"
603 "\n"
604 "You should have received a copy of the GNU General Public License\n"
605 "along with this program; if not, write to the Free Software\n"
606 "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n"
607 ,
608 .plugin.website = "http://deadbeef.sf.net",
609 .open = sndfile_open,
610 .init = sndfile_init,
611 .free = sndfile_free,
612 .read = sndfile_read,
613 .seek = sndfile_seek,
614 .seek_sample = sndfile_seek_sample,
615 .insert = sndfile_insert,
616 .exts = (const char **)exts,
617 .plugin.start = sndfile_start,
618 .plugin.stop = sndfile_stop,
619 .plugin.configdialog = settings_dlg,
620 .plugin.message = sndfile_message,
621 };
622
623 DB_plugin_t *
sndfile_load(DB_functions_t * api)624 sndfile_load (DB_functions_t *api) {
625 deadbeef = api;
626 return DB_PLUGIN (&plugin);
627 }
628