1 /*
2 SID 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 modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 2 of the License, or
8 (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, see <http://www.gnu.org/licenses/>.
17 */
18 #include <stdlib.h>
19 #include <stdio.h>
20 #include <string.h>
21 #include <ctype.h>
22 #include <sys/stat.h>
23 #ifdef HAVE_CONFIG_H
24 # include <config.h>
25 #endif
26 #include "sidplay/sidplay2.h"
27 #include "sidplay/builders/resid.h"
28 //#include "md5.h"
29 // #include "sidplay/sidendian.h"
30
31 #include "../../deadbeef.h"
32 #include "csid.h"
33
34 extern DB_decoder_t sid_plugin;
35
36 //#define trace(...) { fprintf(stderr, __VA_ARGS__); }
37 #define trace(fmt,...)
38
39 DB_functions_t *deadbeef;
40
41 #define min(x,y) ((x)<(y)?(x):(y))
42 #define max(x,y) ((x)>(y)?(x):(y))
43
44 typedef struct {
45 DB_fileinfo_t info;
46 sidplay2 *sidplay;
47 ReSIDBuilder *resid;
48 SidTune *tune;
49 float duration; // of the current song
50 } sid_info_t;
51
52 static inline void
le_int16(int16_t in,unsigned char * out)53 le_int16 (int16_t in, unsigned char *out) {
54 char *pin = (char *)∈
55 #if !WORDS_BIGENDIAN
56 out[0] = pin[0];
57 out[1] = pin[1];
58 #else
59 out[1] = pin[0];
60 out[0] = pin[1];
61 #endif
62 }
63
64 // SLDB support costs ~1M!!!
65 // current hvsc sldb size is ~35k songs
66 #define SLDB_MAX_SONGS 40000
67 // ~50k subsongs in current sldb
68 #define SLDB_POOL_SIZE 55000
69 typedef struct {
70 uint8_t sldb_digests[SLDB_MAX_SONGS][16];
71 int16_t sldb_pool[SLDB_POOL_SIZE];
72 int sldb_poolmark;
73 int16_t *sldb_lengths[SLDB_MAX_SONGS];
74 int sldb_size;
75 } sldb_t;
76 static int sldb_loaded;
77 static sldb_t *sldb;
78 static int sldb_disable;
79
80 static int chip_voices = 0xff;
81 static int chip_voices_changed = 0;
82
83 static int conf_hvsc_enable = 0;
84
85 static void
sldb_load()86 sldb_load()
87 {
88 if (sldb_disable) {
89 return;
90 }
91 trace ("sldb_load\n");
92 if (sldb_loaded || !conf_hvsc_enable) {
93 sldb_disable = 1;
94 return;
95 }
96 char conf_hvsc_path[1000];
97 deadbeef->conf_get_str ("hvsc_path", "", conf_hvsc_path, sizeof (conf_hvsc_path));
98 if (!conf_hvsc_path[0]) {
99 sldb_disable = 1;
100 return;
101 }
102 sldb_loaded = 1;
103 const char *fname = conf_hvsc_path;
104 FILE *fp = fopen (fname, "r");
105 if (!fp) {
106 trace ("sid: failed to open file %s\n", fname);
107 sldb_disable = 1;
108 return;
109 }
110 char str[1024];
111
112 int line = 1;
113 if (fgets (str, 1024, fp) != str) {
114 goto fail; // eof
115 }
116 if (strncmp (str, "[Database]", 10)) {
117 goto fail; // bad format
118 }
119
120 if (!sldb) {
121 sldb = (sldb_t *)malloc (sizeof (sldb_t));
122 memset (sldb, 0, sizeof (sldb_t));
123 }
124 while (fgets (str, 1024, fp) == str) {
125 if (sldb->sldb_size >= SLDB_MAX_SONGS) {
126 trace ("sldb loader ran out of memory.\n");
127 break;
128 }
129 line++;
130 if (str[0] == ';') {
131 // trace ("reading songlength for %s", str);
132 continue; // comment
133 }
134 // read/validate md5
135 const char *p = str;
136 uint8_t digest[16];
137 int sz = 0;
138 char byte[3];
139 while (*p && sz < 16) {
140 byte[0] = tolower (*p);
141 if (!((byte[0] >= '0' && byte[0] <= '9') || (byte[0] >= 'a' && byte[0] <= 'f'))) {
142 trace ("invalid byte 0 in md5: %c\n", byte[0]);
143 break;
144 }
145 p++;
146 if (!(*p)) {
147 break;
148 }
149 byte[1] = tolower (*p);
150 if (!((byte[1] >= '0' && byte[1] <= '9') || (byte[1] >= 'a' && byte[1] <= 'f'))) {
151 trace ("invalid byte 1 in md5: %c\n", byte[1]);
152 break;
153 }
154 byte[2] = 0;
155 p++;
156
157 // convert from ascii hex to uint8_t
158 if (byte[1] < 'a') {
159 digest[sz] = byte[1] - '0';
160 }
161 else {
162 digest[sz] = byte[1] - 'a' + 10;
163 }
164 if (byte[0] < 'a') {
165 digest[sz] |= (byte[0] - '0') << 4;
166 }
167 else {
168 digest[sz] |= (byte[0] - 'a' + 10) << 4;
169 }
170 sz++;
171 }
172 if (sz < 16) {
173 trace ("bad md5 (sz=%d, line=%d)\n", sz, line);
174 continue; // bad song md5
175 }
176 // else {
177 // trace ("digest: ");
178 // for (int j = 0; j < 16; j++) {
179 // trace ("%02x", (int)digest[j]);
180 // }
181 // trace ("\n");
182 // exit (0);
183 // }
184 memcpy (sldb->sldb_digests[sldb->sldb_size], digest, 16);
185 sldb->sldb_lengths[sldb->sldb_size] = &sldb->sldb_pool[sldb->sldb_poolmark];
186 sldb->sldb_size++;
187 // check '=' sign
188 if (*p != '=') {
189 continue; // no '=' sign
190 }
191 p++;
192 if (!(*p)) {
193 continue; // unexpected eol
194 }
195 int subsong = 0;
196 while (*p >= ' ') {
197 // read subsong lengths until eol
198 char timestamp[7]; // up to MMM:SS
199 sz = 0;
200 while (*p > ' ' && *p != '(' && sz < 7) {
201 timestamp[sz++] = *p;
202 p++;
203 }
204 if (sz < 4 || sz == 6 && *p > ' ' && *p != '(') {
205 break; // bad timestamp
206 }
207 timestamp[sz] = 0;
208 // check for unknown time
209 int16_t time = -1;
210 if (!strcmp (timestamp, "-:--")) {
211 time = -1;
212 }
213 else {
214 // parse timestamp
215 const char *colon = strchr (timestamp, ':');
216 if (!colon) {
217 break; // bad timestamp
218 }
219 // minute
220 char minute[4];
221 strncpy (minute, timestamp, colon-timestamp);
222 minute[colon-timestamp] = 0;
223 // second
224 char second[3];
225 strncpy (second, colon+1, 3);
226 //trace ("subsong %d, time %s:%s\n", subsong, minute, second);
227 time = atoi (minute) * 60 + atoi (second);
228 }
229 if (sldb->sldb_poolmark >= SLDB_POOL_SIZE) {
230 trace ("sldb ran out of memory\n");
231 goto fail;
232 }
233
234 sldb->sldb_lengths[sldb->sldb_size-1][subsong] = time;
235 sldb->sldb_poolmark++;
236 subsong++;
237
238 // prepare for next timestamp
239 if (*p == '(') {
240 // skip until next whitespace
241 while (*p > ' ') {
242 p++;
243 }
244 }
245 if (*p < ' ') {
246 break; // eol
247 }
248 // skip white spaces
249 while (*p == ' ') {
250 p++;
251 }
252 if (*p < ' ') {
253 break; // eol
254 }
255 }
256 }
257
258 fail:
259 sldb_disable = 1;
260 fclose (fp);
261 trace ("HVSC sldb loaded %d songs, %d subsongs total\n", sldb->sldb_size, sldb->sldb_poolmark);
262 }
263
264 static int
sldb_find(const uint8_t * digest)265 sldb_find (const uint8_t *digest) {
266 if (!sldb) {
267 trace ("sldb not loaded\n");
268 return -1;
269 }
270 for (int i = 0; i < sldb->sldb_size; i++) {
271 if (!memcmp (digest, sldb->sldb_digests[i], 16)) {
272 return i;
273 }
274 }
275 return -1;
276 }
277
278 DB_fileinfo_t *
csid_open(uint32_t hints)279 csid_open (uint32_t hints) {
280 DB_fileinfo_t *_info = (DB_fileinfo_t *)malloc (sizeof (sid_info_t));
281 memset (_info, 0, sizeof (sid_info_t));
282 return _info;
283 }
284
285 static void
csid_mute_voices(sid_info_t * info,int chip_voices)286 csid_mute_voices (sid_info_t *info, int chip_voices) {
287 int maxsids = info->sidplay->info ().maxsids;
288 for (int k = 0; k < maxsids; k++) {
289 sidemu *emu = info->resid->getsidemu (k);
290 if (emu) {
291 for (int i = 0; i < 3; i++) {
292 bool mute = chip_voices & (1 << i) ? false : true;
293 emu->voice (i, mute ? 0x00 : 0xff, mute);
294 }
295 }
296 }
297 }
298
299 int
csid_init(DB_fileinfo_t * _info,DB_playItem_t * it)300 csid_init (DB_fileinfo_t *_info, DB_playItem_t *it) {
301 sid_info_t *info = (sid_info_t *)_info;
302
303 // libsidplay crashes if file doesn't exist
304 // so i have to check it here
305 deadbeef->pl_lock ();
306 DB_FILE *fp = deadbeef->fopen (deadbeef->pl_find_meta (it, ":URI"));
307 deadbeef->pl_unlock ();
308 if (!fp ){
309 return -1;
310 }
311 deadbeef->fclose (fp);
312
313 info->sidplay = new sidplay2;
314 info->resid = new ReSIDBuilder ("wtf");
315 info->resid->create (info->sidplay->info ().maxsids);
316 info->resid->filter (true);
317
318 int samplerate = deadbeef->conf_get_int ("sid.samplerate", 44100);
319 int bps = deadbeef->conf_get_int ("sid.bps", 16);
320 if (bps != 16 && bps != 8) {
321 bps = 16;
322 }
323
324 info->resid->sampling (samplerate);
325 info->duration = deadbeef->pl_get_item_duration (it);
326 deadbeef->pl_lock ();
327 info->tune = new SidTune (deadbeef->pl_find_meta (it, ":URI"));
328 deadbeef->pl_unlock ();
329
330 info->tune->selectSong (deadbeef->pl_find_meta_int (it, ":TRACKNUM", 0)+1);
331 sid2_config_t conf;
332 conf = info->sidplay->config ();
333 conf.frequency = samplerate;
334 conf.precision = bps;
335
336 conf.playback = deadbeef->conf_get_int ("sid.mono", 0) ? sid2_mono : sid2_stereo;
337 conf.sidEmulation = info->resid;
338 conf.optimisation = 0;
339 info->sidplay->config (conf);
340 info->sidplay->load (info->tune);
341
342 _info->plugin = &sid_plugin;
343 _info->fmt.channels = conf.playback == sid2_stereo ? 2 : 1;
344 _info->fmt.bps = bps;
345 _info->fmt.samplerate = conf.frequency;
346 _info->fmt.channelmask = _info->fmt.channels == 1 ? DDB_SPEAKER_FRONT_LEFT : (DDB_SPEAKER_FRONT_LEFT | DDB_SPEAKER_FRONT_RIGHT);
347 _info->readpos = 0;
348
349 chip_voices = deadbeef->conf_get_int ("chip.voices", 0xff);
350 csid_mute_voices (info, chip_voices);
351 return 0;
352 }
353
354 void
csid_free(DB_fileinfo_t * _info)355 csid_free (DB_fileinfo_t *_info) {
356 sid_info_t *info = (sid_info_t *)_info;
357 if (info) {
358 delete info->sidplay;
359 delete info->resid;
360 delete info->tune;
361 free (info);
362 }
363 }
364
365 int
csid_read(DB_fileinfo_t * _info,char * bytes,int size)366 csid_read (DB_fileinfo_t *_info, char *bytes, int size) {
367 sid_info_t *info = (sid_info_t *)_info;
368 if (_info->readpos > info->duration) {
369 return 0;
370 }
371
372 if (chip_voices_changed) {
373 chip_voices = deadbeef->conf_get_int ("chip.voices", 0xff);
374 chip_voices_changed = 0;
375 csid_mute_voices (info, chip_voices);
376 }
377
378 int rd = info->sidplay->play (bytes, size);
379
380 int samplesize = (_info->fmt.bps>>3) * _info->fmt.channels;
381
382 _info->readpos += rd / samplesize / (float)_info->fmt.samplerate;
383
384 return size;
385
386 }
387
388 int
csid_seek(DB_fileinfo_t * _info,float time)389 csid_seek (DB_fileinfo_t *_info, float time) {
390 sid_info_t *info = (sid_info_t *)_info;
391 float t = time;
392 if (t < _info->readpos) {
393 // reinit
394 info->sidplay->load (info->tune);
395 csid_mute_voices (info, chip_voices);
396 }
397 else {
398 t -= _info->readpos;
399 }
400 info->resid->filter (false);
401 int samples = t * _info->fmt.samplerate;
402 samples *= (_info->fmt.bps>>3) * _info->fmt.channels;
403 uint16_t buffer[2048 * _info->fmt.channels];
404 while (samples > 0) {
405 int n = min (samples, 2048) * _info->fmt.channels;
406 int done = info->sidplay->play (buffer, n);
407 if (done < n) {
408 trace ("sid seek failure\n");
409 return -1;
410 }
411 samples -= done;
412 }
413 info->resid->filter (true);
414 _info->readpos = time;
415
416 return 0;
417 }
418
419 static const char *
convstr(const char * str,int sz,char * out,int out_sz)420 convstr (const char* str, int sz, char *out, int out_sz) {
421 const char *cs = deadbeef->junk_detect_charset (str);
422 if (!cs) {
423 return str;
424 }
425 else {
426 if (deadbeef->junk_iconv (str, sz, out, out_sz, cs, "utf-8") >= 0) {
427 return out;
428 }
429 }
430 return NULL;
431 }
432
433 static void
find_hvsc_path_from_fname(const char * fname)434 find_hvsc_path_from_fname (const char *fname) {
435 if (conf_hvsc_enable && !sldb_loaded && !sldb_disable) {
436 char conf_hvsc_path[1000];
437 deadbeef->conf_get_str ("hvsc_path", "", conf_hvsc_path, sizeof (conf_hvsc_path));
438 if (!conf_hvsc_path[0]) {
439 strcpy (conf_hvsc_path, fname);
440 char *p;
441 while ((p = strrchr (conf_hvsc_path, '/'))) {
442 strcpy (p, "/DOCUMENTS/Songlengths.txt");
443 struct stat st;
444 int err = stat (conf_hvsc_path, &st);
445 if (!err && (st.st_mode & S_IFREG)) {
446 deadbeef->conf_set_str ("hvsc_path", conf_hvsc_path);
447 deadbeef->conf_save ();
448 break;
449 }
450 *p = 0;
451 }
452 }
453 }
454 }
455
456 extern "C" DB_playItem_t *
csid_insert(ddb_playlist_t * plt,DB_playItem_t * after,const char * fname)457 csid_insert (ddb_playlist_t *plt, DB_playItem_t *after, const char *fname) {
458 trace ("inserting %s\n", fname);
459
460 find_hvsc_path_from_fname (fname);
461
462 sldb_load ();
463 SidTune *tune;
464 trace ("new SidTune\n");
465 tune = new SidTune (fname);
466 int tunes = tune->getInfo ().songs;
467 trace ("subtunes: %d\n", tunes);
468 uint8_t sig[16];
469 unsigned char tmp[2];
470 #if 1
471 trace ("calculating md5\n");
472 DB_md5_t md5;
473 deadbeef->md5_init (&md5);
474 deadbeef->md5_append (&md5, (const uint8_t *)tune->cache.get () + tune->fileOffset, tune->getInfo ().c64dataLen);
475 le_int16 (tune->getInfo ().initAddr, tmp);
476 deadbeef->md5_append (&md5, tmp, 2);
477 le_int16 (tune->getInfo ().playAddr, tmp);
478 deadbeef->md5_append (&md5, tmp, 2);
479 le_int16 (tune->getInfo ().songs, tmp);
480 deadbeef->md5_append (&md5, tmp, 2);
481 for (int s = 1; s <= tunes; s++)
482 {
483 tune->selectSong (s);
484 // songspeed is uint8_t, so no need for byteswap
485 deadbeef->md5_append (&md5, &tune->getInfo ().songSpeed, 1);
486 }
487 if (tune->getInfo ().clockSpeed == SIDTUNE_CLOCK_NTSC) {
488 deadbeef->md5_append (&md5, &tune->getInfo ().clockSpeed, sizeof (tune->getInfo ().clockSpeed));
489 }
490 deadbeef->md5_finish (&md5, sig);
491 #else
492 // md5 calc from libsidplay2
493 MD5 myMD5;
494 myMD5.append ((const char *)tune->cache.get() + tune->fileOffset, tune->getInfo ().c64dataLen);
495 // Include INIT and PLAY address.
496 endian_little16 (tmp,tune->getInfo ().initAddr);
497 myMD5.append (tmp,sizeof(tmp));
498 endian_little16 (tmp,tune->getInfo ().playAddr);
499 myMD5.append (tmp,sizeof(tmp));
500 // Include number of songs.
501 endian_little16 (tmp,tune->getInfo ().songs);
502 myMD5.append (tmp,sizeof(tmp));
503 {
504 // Include song speed for each song.
505 for (uint_least16_t s = 1; s <= tune->getInfo ().songs; s++)
506 {
507 tune->selectSong (s);
508 myMD5.append (&tune->getInfo ().songSpeed,1);
509 }
510 }
511 // Deal with PSID v2NG clock speed flags: Let only NTSC
512 // clock speed change the MD5 fingerprint. That way the
513 // fingerprint of a PAL-speed sidtune in PSID v1, v2, and
514 // PSID v2NG format is the same.
515 if (tune->getInfo ().clockSpeed == SIDTUNE_CLOCK_NTSC) {
516 myMD5.append (&tune->getInfo ().clockSpeed,sizeof(tune->getInfo ().clockSpeed));
517 }
518 myMD5.finish ();
519 memcpy (sig, myMD5.getDigest (), 16);
520 #endif
521
522 int song = -1;
523 if (sldb_loaded) {
524 song = sldb_find (sig);
525 }
526
527 trace ("inserting tunes...\n");
528 for (int s = 0; s < tunes; s++) {
529 trace ("select %d...\n", s);
530 if (tune->selectSong (s+1)) {
531 DB_playItem_t *it = deadbeef->pl_item_alloc_init (fname, sid_plugin.plugin.id);
532 deadbeef->pl_set_meta_int (it, ":TRACKNUM", s);
533 SidTuneInfo sidinfo;
534 tune->getInfo (sidinfo);
535 int i = sidinfo.numberOfInfoStrings;
536 int title_added = 0;
537 trace ("set %d metainfo...\n", s);
538 char temp[2048];
539 if (i >= 1 && sidinfo.infoString[0] && sidinfo.infoString[0][0]) {
540 const char *meta;
541 if (sidinfo.songs > 1) {
542 meta = "album";
543 }
544 else {
545 meta = "title";
546 title_added = 1;
547 }
548 deadbeef->pl_add_meta (it, meta, convstr (sidinfo.infoString[0], strlen (sidinfo.infoString[0]), temp, sizeof (temp)));
549 }
550 if (i >= 2 && sidinfo.infoString[1] && sidinfo.infoString[1][0]) {
551 deadbeef->pl_add_meta (it, "artist", convstr (sidinfo.infoString[1], strlen (sidinfo.infoString[1]), temp, sizeof (temp)));
552 }
553 if (i >= 3 && sidinfo.infoString[2] && sidinfo.infoString[2][0]) {
554 deadbeef->pl_add_meta (it, "copyright", convstr (sidinfo.infoString[2], strlen (sidinfo.infoString[2]), temp, sizeof (temp)));
555 }
556
557 for (int j = 3; j < i; j++)
558 {
559 if (sidinfo.infoString[j] && sidinfo.infoString[j][0]) {
560 deadbeef->pl_add_meta (it, "info", convstr (sidinfo.infoString[j], strlen (sidinfo.infoString[j]), temp, sizeof (temp)));
561 }
562 }
563 char trk[10];
564 snprintf (trk, 10, "%d", s+1);
565 deadbeef->pl_add_meta (it, "track", trk);
566 if (!title_added) {
567 deadbeef->pl_add_meta (it, "title", NULL);
568 }
569
570 float length = deadbeef->conf_get_float ("sid.defaultlength", 180);
571 if (sldb_loaded) {
572 if (song >= 0 && sldb->sldb_lengths[song][s] >= 0) {
573 length = sldb->sldb_lengths[song][s];
574 }
575 // if (song < 0) {
576 // trace ("song %s not found in db, md5: ", fname);
577 // for (int j = 0; j < 16; j++) {
578 // trace ("%02x", (int)sig[j]);
579 // }
580 // trace ("\n");
581 // }
582 }
583 deadbeef->plt_set_item_duration (plt, it, length);
584 deadbeef->pl_add_meta (it, ":FILETYPE", "SID");
585
586 after = deadbeef->plt_insert_item (plt, after, it);
587 deadbeef->pl_item_unref (it);
588 }
589 }
590 trace ("delete sidtune\n");
591 delete tune;
592 return after;
593 }
594
595 static int
sid_configchanged(void)596 sid_configchanged (void) {
597 conf_hvsc_enable = deadbeef->conf_get_int ("hvsc_enable", 0);
598 int disable = !conf_hvsc_enable;
599 if (disable != sldb_disable) {
600 sldb_disable = disable;
601 }
602
603 // pick up new sldb filename in case it was changed
604 if (sldb) {
605 free (sldb);
606 sldb = NULL;
607 sldb_loaded = 0;
608 }
609
610 if (chip_voices != deadbeef->conf_get_int ("chip.voices", 0xff)) {
611 chip_voices_changed = 1;
612 }
613
614 return 0;
615 }
616
617 int
sid_message(uint32_t id,uintptr_t ctx,uint32_t p1,uint32_t p2)618 sid_message (uint32_t id, uintptr_t ctx, uint32_t p1, uint32_t p2) {
619 switch (id) {
620 case DB_EV_CONFIGCHANGED:
621 sid_configchanged ();
622 break;
623 }
624 return 0;
625 }
626
627 int
csid_start(void)628 csid_start (void) {
629 sid_configchanged ();
630 return 0;
631 }
632
633 int
csid_stop(void)634 csid_stop (void) {
635 if (sldb) {
636 free (sldb);
637 sldb = NULL;
638 }
639 sldb_loaded = 0;
640 return 0;
641 }
642
643 extern "C" DB_plugin_t *
sid_load(DB_functions_t * api)644 sid_load (DB_functions_t *api) {
645 deadbeef = api;
646 return DB_PLUGIN (&sid_plugin);
647 }
648
649