1 /*
2     Converter for DeaDBeeF Player
3     Copyright (C) 2009-2015 Alexey Yakovenko and other contributors
4 
5     This software is provided 'as-is', without any express or implied
6     warranty.  In no event will the authors be held liable for any damages
7     arising from the use of this software.
8 
9     Permission is granted to anyone to use this software for any purpose,
10     including commercial applications, and to alter it and redistribute it
11     freely, subject to the following restrictions:
12 
13     1. The origin of this software must not be misrepresented; you must not
14      claim that you wrote the original software. If you use this software
15      in a product, an acknowledgment in the product documentation would be
16      appreciated but is not required.
17 
18     2. Altered source versions must be plainly marked as such, and must not be
19      misrepresented as being the original software.
20 
21     3. This notice may not be removed or altered from any source distribution.
22 */
23 
24 #ifdef HAVE_CONFIG_H
25 #  include "../../config.h"
26 #endif
27 #if HAVE_SYS_CDEFS_H
28 #include <sys/cdefs.h>
29 #endif
30 #include <stdlib.h>
31 #include <limits.h>
32 #include <string.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <dirent.h>
36 #include <fcntl.h>
37 #include <unistd.h>
38 #include <inttypes.h>
39 #include "converter.h"
40 #include "../../deadbeef.h"
41 #include "../../strdupa.h"
42 
43 #ifndef __linux__
44 #define O_LARGEFILE 0
45 #endif
46 
47 #define min(x,y) ((x)<(y)?(x):(y))
48 
49 //#define trace(...) { fprintf(stderr, __VA_ARGS__); }
50 #define trace(fmt,...)
51 
52 static ddb_converter_t plugin;
53 static DB_functions_t *deadbeef;
54 
55 static ddb_encoder_preset_t *encoder_presets;
56 static ddb_dsp_preset_t *dsp_presets;
57 
58 ddb_encoder_preset_t *
encoder_preset_alloc(void)59 encoder_preset_alloc (void) {
60     ddb_encoder_preset_t *p = malloc (sizeof (ddb_encoder_preset_t));
61     if (!p) {
62         fprintf (stderr, "failed to alloc ddb_encoder_preset_t\n");
63         return NULL;
64     }
65     memset (p, 0, sizeof (ddb_encoder_preset_t));
66     return p;
67 }
68 
69 void
encoder_preset_free(ddb_encoder_preset_t * p)70 encoder_preset_free (ddb_encoder_preset_t *p) {
71     if (p) {
72         if (p->title) {
73             free (p->title);
74         }
75         if (p->ext) {
76             free (p->ext);
77         }
78         if (p->encoder) {
79             free (p->encoder);
80         }
81         free (p);
82     }
83 }
84 
85 ddb_encoder_preset_t *
encoder_preset_load(const char * fname)86 encoder_preset_load (const char *fname) {
87     int err = 1;
88     FILE *fp = fopen (fname, "rt");
89     if (!fp) {
90         return NULL;
91     }
92     ddb_encoder_preset_t *p = encoder_preset_alloc ();
93 
94     char str[1024];
95     while (fgets (str, sizeof (str), fp)) {
96         // chomp
97         char *cr = str + strlen (str) - 1;
98         while (*cr == '\n') {
99             cr--;
100         }
101         cr++;
102         *cr = 0;
103 
104         char *sp = strchr (str, ' ');
105         if (!sp) {
106             continue;
107         }
108 
109         *sp = 0;
110         char *item = sp + 1;
111 
112         if (!strcmp (str, "title")) {
113             p->title = strdup (item);
114         }
115         else if (!strcmp (str, "ext")) {
116             p->ext = strdup (item);
117         }
118         else if (!strcmp (str, "encoder")) {
119             p->encoder = strdup (item);
120         }
121         else if (!strcmp (str, "method")) {
122             p->method = atoi (item);
123         }
124         else if (!strcmp (str, "id3v2_version")) {
125             p->id3v2_version = atoi (item);
126         }
127         else if (!strcmp (str, "tag_id3v2")) {
128             p->tag_id3v2 = atoi (item);
129         }
130         else if (!strcmp (str, "tag_id3v1")) {
131             p->tag_id3v1 = atoi (item);
132         }
133         else if (!strcmp (str, "tag_apev2")) {
134             p->tag_apev2 = atoi (item);
135         }
136         else if (!strcmp (str, "tag_flac")) {
137             p->tag_flac = atoi (item);
138         }
139         else if (!strcmp (str, "tag_oggvorbis")) {
140             p->tag_oggvorbis = atoi (item);
141         }
142     }
143 
144     if (!p->title) {
145         p->title = strdup ("Untitled");
146     }
147     if (!p->ext) {
148         p->ext = strdup ("");
149     }
150     if (!p->encoder) {
151         p->encoder = strdup ("");
152     }
153 
154     err = 0;
155 
156     if (err) {
157         encoder_preset_free (p);
158         p = NULL;
159     }
160     if (fp) {
161         fclose (fp);
162     }
163     return p;
164 }
165 
166 // @return -1 on path/write error, -2 if file already exists
167 int
encoder_preset_save(ddb_encoder_preset_t * p,int overwrite)168 encoder_preset_save (ddb_encoder_preset_t *p, int overwrite) {
169     if (!p->title || !p->title[0]) {
170         fprintf (stderr, "encoder_preset_save: empty title\n");
171         return -1;
172     }
173     const char *confdir = deadbeef->get_config_dir ();
174     char path[PATH_MAX];
175     if (snprintf (path, sizeof (path), "%s/presets", confdir) < 0) {
176         return -1;
177     }
178     mkdir (path, 0755);
179     if (snprintf (path, sizeof (path), "%s/presets/encoders", confdir) < 0) {
180         return -1;
181     }
182     mkdir (path, 0755);
183     if (snprintf (path, sizeof (path), "%s/presets/encoders/%s.txt", confdir, p->title) < 0) {
184         return -1;
185     }
186 
187     if (!overwrite) {
188         FILE *fp = fopen (path, "rb");
189         if (fp) {
190             fclose (fp);
191             return -2;
192         }
193     }
194 
195     FILE *fp = fopen (path, "w+b");
196     if (!fp) {
197         return -1;
198     }
199 
200     fprintf (fp, "title %s\n", p->title);
201     fprintf (fp, "ext %s\n", p->ext);
202     fprintf (fp, "encoder %s\n", p->encoder);
203     fprintf (fp, "method %d\n", p->method);
204     fprintf (fp, "id3v2_version %d\n", p->id3v2_version);
205     fprintf (fp, "tag_id3v2 %d\n", p->tag_id3v2);
206     fprintf (fp, "tag_id3v1 %d\n", p->tag_id3v1);
207     fprintf (fp, "tag_apev2 %d\n", p->tag_apev2);
208     fprintf (fp, "tag_flac %d\n", p->tag_flac);
209     fprintf (fp, "tag_oggvorbis %d\n", p->tag_oggvorbis);
210 
211     fclose (fp);
212     return 0;
213 }
214 
215 void
encoder_preset_copy(ddb_encoder_preset_t * to,ddb_encoder_preset_t * from)216 encoder_preset_copy (ddb_encoder_preset_t *to, ddb_encoder_preset_t *from) {
217     to->title = strdup (from->title);
218     to->ext = strdup (from->ext);
219     to->encoder = strdup (from->encoder);
220     to->method = from->method;
221     to->tag_id3v2 = from->tag_id3v2;
222     to->tag_id3v1 = from->tag_id3v1;
223     to->tag_apev2 = from->tag_apev2;
224     to->tag_flac = from->tag_flac;
225     to->tag_oggvorbis = from->tag_oggvorbis;
226     to->tag_mp3xing = from->tag_mp3xing;
227     to->id3v2_version = from->id3v2_version;
228 }
229 
230 ddb_encoder_preset_t *
encoder_preset_get_list(void)231 encoder_preset_get_list (void) {
232     return encoder_presets;
233 }
234 
235 ddb_encoder_preset_t *
encoder_preset_get_for_idx(int idx)236 encoder_preset_get_for_idx (int idx) {
237     ddb_encoder_preset_t *p = encoder_presets;
238     while (p && idx--) {
239         p = p->next;
240     }
241     return p;
242 }
243 
244 void
encoder_preset_append(ddb_encoder_preset_t * p)245 encoder_preset_append (ddb_encoder_preset_t *p) {
246     // append
247     ddb_encoder_preset_t *tail = encoder_presets;
248     while (tail && tail->next) {
249         tail = tail->next;
250     }
251     if (tail) {
252         tail->next = p;
253     }
254     else {
255         encoder_presets = p;
256     }
257 }
258 
259 void
encoder_preset_remove(ddb_encoder_preset_t * p)260 encoder_preset_remove (ddb_encoder_preset_t *p) {
261     ddb_encoder_preset_t *prev = encoder_presets;
262     while (prev && prev->next != p) {
263         prev = prev->next;
264     }
265     if (prev) {
266         prev->next = p->next;
267     }
268     else {
269         encoder_presets = p->next;
270     }
271 }
272 
273 void
encoder_preset_replace(ddb_encoder_preset_t * from,ddb_encoder_preset_t * to)274 encoder_preset_replace (ddb_encoder_preset_t *from, ddb_encoder_preset_t *to) {
275     ddb_encoder_preset_t *prev = encoder_presets;
276     while (prev && prev->next != from) {
277         prev = prev->next;
278     }
279     if (prev) {
280         prev->next = to;
281     }
282     else {
283         encoder_presets = to;
284     }
285     to->next = from->next;
286 }
287 
288 ddb_dsp_preset_t *
dsp_preset_alloc(void)289 dsp_preset_alloc (void) {
290     ddb_dsp_preset_t *p = malloc (sizeof (ddb_dsp_preset_t));
291     if (!p) {
292         fprintf (stderr, "failed to alloc ddb_dsp_preset_t\n");
293         return NULL;
294     }
295     memset (p, 0, sizeof (ddb_dsp_preset_t));
296     return p;
297 }
298 
299 void
dsp_preset_free(ddb_dsp_preset_t * p)300 dsp_preset_free (ddb_dsp_preset_t *p) {
301     if (p) {
302         if (p->title) {
303             free (p->title);
304         }
305         deadbeef->dsp_preset_free (p->chain);
306         free (p);
307     }
308 }
309 
310 void
dsp_preset_copy(ddb_dsp_preset_t * to,ddb_dsp_preset_t * from)311 dsp_preset_copy (ddb_dsp_preset_t *to, ddb_dsp_preset_t *from) {
312     to->title = strdup (from->title);
313     ddb_dsp_context_t *tail = NULL;
314     ddb_dsp_context_t *dsp = from->chain;
315     while (dsp) {
316         ddb_dsp_context_t *i = dsp->plugin->open ();
317         if (dsp->plugin->num_params) {
318             int n = dsp->plugin->num_params ();
319             for (int j = 0; j < n; j++) {
320                 char s[1000] = "";
321                 dsp->plugin->get_param (dsp, j, s, sizeof (s));
322                 i->plugin->set_param (i, j, s);
323             }
324         }
325         if (tail) {
326             tail->next = i;
327             tail = i;
328         }
329         else {
330             to->chain = tail = i;
331         }
332         dsp = dsp->next;
333     }
334 }
335 
336 ddb_dsp_preset_t *
dsp_preset_get_list(void)337 dsp_preset_get_list (void) {
338     return dsp_presets;
339 }
340 
341 ddb_dsp_preset_t *
dsp_preset_load(const char * fname)342 dsp_preset_load (const char *fname) {
343     ddb_dsp_preset_t *p = dsp_preset_alloc ();
344     if (!p) {
345         return NULL;
346     }
347     memset (p, 0, sizeof (ddb_dsp_preset_t));
348     const char *end = strrchr (fname, '.');
349     if (!end) {
350         end = fname + strlen (fname);
351     }
352     const char *start = strrchr (fname, '/');
353     if (!start) {
354         start = fname;
355     }
356     else {
357         start++;
358     }
359 
360     p->title = malloc (end-start+1);
361     memcpy (p->title, start, end-start);
362     p->title[end-start] = 0;
363     int err = deadbeef->dsp_preset_load (fname, &p->chain);
364     if (err != 0) {
365         dsp_preset_free (p);
366         return NULL;
367     }
368     return p;
369 }
370 
371 int
dsp_preset_save(ddb_dsp_preset_t * p,int overwrite)372 dsp_preset_save (ddb_dsp_preset_t *p, int overwrite) {
373     if (!p->title || !p->title[0]) {
374         fprintf (stderr, "dsp_preset_save: empty title\n");
375         return -1;
376     }
377     const char *confdir = deadbeef->get_config_dir ();
378     char path[PATH_MAX];
379     if (snprintf (path, sizeof (path), "%s/presets", confdir) < 0) {
380         return -1;
381     }
382     mkdir (path, 0755);
383     if (snprintf (path, sizeof (path), "%s/presets/dsp", confdir) < 0) {
384         return -1;
385     }
386     mkdir (path, 0755);
387     if (snprintf (path, sizeof (path), "%s/presets/dsp/%s.txt", confdir, p->title) < 0) {
388         return -1;
389     }
390 
391     if (!overwrite) {
392         FILE *fp = fopen (path, "rb");
393         if (fp) {
394             fclose (fp);
395             return -2;
396         }
397     }
398 
399     return deadbeef->dsp_preset_save (path, p->chain);
400 }
401 
dirent_alphasort(const struct dirent ** a,const struct dirent ** b)402 static int dirent_alphasort (const struct dirent **a, const struct dirent **b) {
403     return strcmp ((*a)->d_name, (*b)->d_name);
404 }
405 
406 int
scandir_preset_filter(const struct dirent * ent)407 scandir_preset_filter (const struct dirent *ent) {
408     char *ext = strrchr (ent->d_name, '.');
409     if (ext && !strcasecmp (ext, ".txt")) {
410         return 1;
411     }
412     return 0;
413 }
414 
415 static int
copy_file(const char * in,const char * out)416 copy_file (const char *in, const char *out) {
417     int BUFFER_SIZE = 1000;
418     FILE *fin = fopen (in, "rb");
419     if (!fin) {
420         fprintf (stderr, "converter: failed to open file %s for reading\n", in);
421         return -1;
422     }
423     FILE *fout = fopen (out, "w+b");
424     if (!fout) {
425         fclose (fin);
426         fprintf (stderr, "converter: failed to open file %s for writing\n", out);
427         return -1;
428     }
429     char *buf = malloc (BUFFER_SIZE);
430     if (!buf) {
431         fprintf (stderr, "converter: failed to alloc %d bytes\n", BUFFER_SIZE);
432         fclose (fin);
433         fclose (fout);
434         return -1;
435     }
436 
437     fseek (fin, 0, SEEK_END);
438     size_t sz = ftell (fin);
439     rewind (fin);
440 
441     while (sz > 0) {
442         int rs = min (sz, BUFFER_SIZE);
443         if (fread (buf, rs, 1, fin) != 1) {
444             fprintf (stderr, "converter: failed to read file %s\n", in);
445             break;
446         }
447         if (fwrite (buf, rs, 1, fout) != 1) {
448             fprintf (stderr, "converter: failed to write file %s\n", out);
449             break;
450         }
451         sz -= rs;
452     }
453     free (buf);
454     fclose (fin);
455     fclose (fout);
456     if (sz > 0) {
457         unlink (out);
458     }
459     return 0;
460 }
461 
462 int
load_encoder_presets(void)463 load_encoder_presets (void) {
464     // check if we need to install presets
465     char ppath[PATH_MAX];
466     char epath[PATH_MAX];
467     snprintf (ppath, sizeof (ppath), "%s/presets", deadbeef->get_config_dir ());
468     snprintf (epath, sizeof (epath), "%s/encoders", ppath);
469 
470     char path[PATH_MAX];
471     if (snprintf (path, sizeof (path), "%s/presets/encoders", deadbeef->get_config_dir ()) < 0) {
472         return -1;
473     }
474 
475     char syspath[PATH_MAX];
476     if (snprintf (syspath, sizeof (syspath), "%s/convpresets", deadbeef->get_plugin_dir ()) < 0) {
477         return -1;
478     }
479 
480     const char *preset_dirs[] = {
481         syspath, path, NULL
482     };
483 
484     ddb_encoder_preset_t *tail = NULL;
485 
486     for (int di = 0; preset_dirs[di]; di++) {
487         const char *path = preset_dirs[di];
488         struct dirent **namelist = NULL;
489         int n = scandir (path, &namelist, scandir_preset_filter, dirent_alphasort);
490         int i;
491         for (i = 0; i < n; i++) {
492             char s[PATH_MAX];
493             if (snprintf (s, sizeof (s), "%s/%s", path, namelist[i]->d_name) > 0){
494                 ddb_encoder_preset_t *p = encoder_preset_load (s);
495                 if (p) {
496                     if (path == syspath) {
497                         // don't allow editing stock presets
498                         p->readonly = 1;
499                     }
500                     else {
501                         // check if the same RO preset exists
502                         for (ddb_encoder_preset_t *pr = encoder_presets; pr; pr = pr->next) {
503                             if (pr->readonly && !strcmp (pr->title, p->title)) {
504                                 encoder_preset_free (p);
505                                 p = NULL;
506                                 break;
507                             }
508                         }
509                         if (!p) {
510                             // NOTE: we don't delete duplicate presets in $HOME
511                             // for compat with <=0.6.1
512                             encoder_preset_free (p);
513                             p = NULL;
514                             continue;
515                         }
516                     }
517                     if (tail) {
518                         tail->next = p;
519                         tail = p;
520                     }
521                     else {
522                         encoder_presets = tail = p;
523                     }
524                 }
525             }
526         }
527         for (i = 0; i < n; i++) {
528             free (namelist[i]);
529         }
530         free (namelist);
531         namelist = NULL;
532     }
533     return 0;
534 }
535 
536 void
free_encoder_presets(void)537 free_encoder_presets (void) {
538     ddb_encoder_preset_t *p = encoder_presets;
539     while (p) {
540         ddb_encoder_preset_t *next = p->next;
541         if (p->title) {
542             free (p->title);
543         }
544         if (p->ext) {
545             free (p->ext);
546         }
547         if (p->encoder) {
548             free (p->encoder);
549         }
550         free (p);
551         p = next;
552     }
553     encoder_presets = NULL;
554 }
555 
556 int
load_dsp_presets(void)557 load_dsp_presets (void) {
558     ddb_dsp_preset_t *tail = NULL;
559     char path[PATH_MAX];
560     if (snprintf (path, sizeof (path), "%s/presets/dsp", deadbeef->get_config_dir ()) < 0) {
561         return -1;
562     }
563     struct dirent **namelist = NULL;
564     int n = scandir (path, &namelist, scandir_preset_filter, dirent_alphasort);
565     int i;
566     for (i = 0; i < n; i++) {
567         char s[PATH_MAX];
568         if (snprintf (s, sizeof (s), "%s/%s", path, namelist[i]->d_name) > 0){
569             ddb_dsp_preset_t *p = dsp_preset_load (s);
570             if (p) {
571                 if (tail) {
572                     tail->next = p;
573                     tail = p;
574                 }
575                 else {
576                     dsp_presets = tail = p;
577                 }
578             }
579         }
580         free (namelist[i]);
581     }
582     free (namelist);
583     return 0;
584 }
585 
586 void
free_dsp_presets(void)587 free_dsp_presets (void) {
588     ddb_dsp_preset_t *p = dsp_presets;
589     while (p) {
590         ddb_dsp_preset_t *next = p->next;
591         if (p->title) {
592             free (p->title);
593         }
594         if (p->chain) {
595             deadbeef->dsp_preset_free (p->chain);
596         }
597         free (p);
598         p = next;
599     }
600     dsp_presets = NULL;
601 }
602 
603 ddb_dsp_preset_t *
dsp_preset_get_for_idx(int idx)604 dsp_preset_get_for_idx (int idx) {
605     ddb_dsp_preset_t *p = dsp_presets;
606     while (p && idx--) {
607         p = p->next;
608     }
609     return p;
610 }
611 
612 void
dsp_preset_append(ddb_dsp_preset_t * p)613 dsp_preset_append (ddb_dsp_preset_t *p) {
614     // append
615     ddb_dsp_preset_t *tail = dsp_presets;
616     while (tail && tail->next) {
617         tail = tail->next;
618     }
619     if (tail) {
620         tail->next = p;
621     }
622     else {
623         dsp_presets = p;
624     }
625 }
626 
627 void
dsp_preset_remove(ddb_dsp_preset_t * p)628 dsp_preset_remove (ddb_dsp_preset_t *p) {
629     ddb_dsp_preset_t *prev = dsp_presets;
630     while (prev && prev->next != p) {
631         prev = prev->next;
632     }
633     if (prev) {
634         prev->next = p->next;
635     }
636     else {
637         dsp_presets = p->next;
638     }
639 }
640 
641 void
dsp_preset_replace(ddb_dsp_preset_t * from,ddb_dsp_preset_t * to)642 dsp_preset_replace (ddb_dsp_preset_t *from, ddb_dsp_preset_t *to) {
643     ddb_dsp_preset_t *prev = dsp_presets;
644     while (prev && prev->next != from) {
645         prev = prev->next;
646     }
647     if (prev) {
648         prev->next = to;
649     }
650     else {
651         dsp_presets = to;
652     }
653     to->next = from->next;
654 }
655 
656 static void
escape_filepath(const char * path,char * out,int sz)657 escape_filepath (const char *path, char *out, int sz) {
658     // escape special chars
659     char invalid[] = "$\"`\\";
660     char *p = out;
661     const char *t = path;
662     int n = sz;
663     while (*t && n > 1) {
664         if (strchr (invalid, *t)) {
665             *p++ = '\\';
666             n--;
667             *p++ = *t;
668             n--;
669         }
670         else {
671             *p++ = *t;
672             n--;
673         }
674         t++;
675     }
676     *p = 0;
677 }
678 
679 static void
get_output_field(DB_playItem_t * it,const char * field,char * out,int sz)680 get_output_field (DB_playItem_t *it, const char *field, char *out, int sz)
681 {
682     int idx = deadbeef->pl_get_idx_of (it);
683     char temp[PATH_MAX];
684     char fmt[strlen(field)+3];
685     snprintf (fmt, sizeof (fmt), "%%/%s", field);
686     deadbeef->pl_format_title (it, idx, temp, sizeof (temp), -1, fmt);
687 
688     strncpy (out, temp, sz);
689     out[sz-1] = 0;
690     trace ("field '%s' expanded to '%s'\n", field, out);
691 }
692 
693 static void
get_output_field2(DB_playItem_t * it,ddb_playlist_t * plt,const char * field,char * out,int sz)694 get_output_field2 (DB_playItem_t *it, ddb_playlist_t *plt, const char *field, char *out, int sz)
695 {
696     int idx = deadbeef->pl_get_idx_of (it);
697     char temp[PATH_MAX];
698 
699     char *tf = deadbeef->tf_compile (field);
700     if (!tf) {
701         *out = 0;
702         return;
703     }
704 
705     ddb_tf_context_t ctx = {
706         ._size = sizeof (ddb_tf_context_t),
707         .flags = DDB_TF_CONTEXT_HAS_INDEX,
708         .it = it,
709         .idx = idx,
710         .iter = PL_MAIN,
711         .plt = plt,
712     };
713 
714     deadbeef->tf_eval (&ctx, tf, temp, sizeof (temp));
715     deadbeef->tf_free (tf);
716 
717     char *o = out;
718     for (char *p = temp; *p && sz > 0; p++) {
719         if (*p == '/') {
720             *o++ = '-';
721         }
722         else
723         {
724             *o++ = *p;
725         }
726         sz--;
727     }
728 
729     *o = 0;
730 
731     trace ("field '%s' expanded to '%s'\n", field, out);
732 }
733 
734 
735 static void
get_output_path_int(DB_playItem_t * it,ddb_playlist_t * plt,const char * outfolder_user,const char * outfile,ddb_encoder_preset_t * encoder_preset,int preserve_folder_structure,const char * root_folder,int write_to_source_folder,char * out,int sz,int use_new_tf)736 get_output_path_int (DB_playItem_t *it, ddb_playlist_t *plt, const char *outfolder_user, const char *outfile, ddb_encoder_preset_t *encoder_preset, int preserve_folder_structure, const char *root_folder, int write_to_source_folder, char *out, int sz, int use_new_tf) {
737     trace ("get_output_path: %s %s %s\n", outfolder_user, outfile, root_folder);
738     deadbeef->pl_lock ();
739     const char *uri = strdupa (deadbeef->pl_find_meta (it, ":URI"));
740     deadbeef->pl_unlock ();
741     char outfolder_preserve[2000];
742     if (preserve_folder_structure) {
743         // generate new outfolder
744         size_t rootlen = strlen (root_folder);
745         const char *e = strrchr (uri, '/');
746         if (e) {
747             const char *s = uri + rootlen;
748             char subpath[e-s+1];
749             memcpy (subpath, s, e-s);
750             subpath[e-s] = 0;
751             snprintf (outfolder_preserve, sizeof (outfolder_preserve), "%s%s", outfolder_user[0] ? outfolder_user : getenv("HOME"), subpath);
752         }
753     }
754 
755     const char *outfolder;
756 
757     if (write_to_source_folder) {
758         char *path = strdupa (uri);
759         char *sep = strrchr (path, '/');
760         if (sep) {
761             *sep = 0;
762         }
763         outfolder = path;
764     }
765     else {
766         outfolder = preserve_folder_structure ? outfolder_preserve : outfolder_user;
767     }
768 
769     int l;
770     char fname[PATH_MAX];
771     size_t pathl = strlen(outfolder)*2+1;
772     char path[pathl];
773     char *pattern = strdupa (outfile);
774 
775     snprintf (out, sz, "%s/", outfolder);
776 
777     // split path, and expand each path component using get_output_field
778     char *field = pattern;
779     char *s = pattern;
780     while (*s) {
781         if ((*s == '/') || (*s == '\\')) {
782             *s = '\0';
783             if (use_new_tf) {
784                 get_output_field2(it, plt, field, fname, sizeof (fname));
785             }
786             else {
787                 get_output_field (it, field, fname, sizeof (fname));
788             }
789 
790             l = strlen (out);
791             snprintf (out+l, sz-l, "%s/", fname);
792 
793             field = s+1;
794         }
795         s++;
796     }
797 
798     // last part of outfile is the filename
799     if (use_new_tf) {
800         get_output_field2(it, plt, field, fname, sizeof (fname));
801     }
802     else {
803         get_output_field (it, field, fname, sizeof(fname));
804     }
805 
806     l = strlen (out);
807     snprintf (out+l, sz-l, "%s.%s", fname, encoder_preset->ext);
808     trace ("converter output file is '%s'\n", out);
809 }
810 
811 void
get_output_path(DB_playItem_t * it,const char * outfolder_user,const char * outfile,ddb_encoder_preset_t * encoder_preset,int preserve_folder_structure,const char * root_folder,int write_to_source_folder,char * out,int sz)812 get_output_path (DB_playItem_t *it, const char *outfolder_user, const char *outfile, ddb_encoder_preset_t *encoder_preset, int preserve_folder_structure, const char *root_folder, int write_to_source_folder, char *out, int sz) {
813     get_output_path_int (it, NULL, outfolder_user, outfile, encoder_preset, preserve_folder_structure, root_folder, write_to_source_folder, out, sz, 0);
814 }
815 
816 void
get_output_path2(DB_playItem_t * it,ddb_playlist_t * plt,const char * outfolder_user,const char * outfile,ddb_encoder_preset_t * encoder_preset,int preserve_folder_structure,const char * root_folder,int write_to_source_folder,char * out,int sz)817 get_output_path2 (DB_playItem_t *it, ddb_playlist_t *plt, const char *outfolder_user, const char *outfile, ddb_encoder_preset_t *encoder_preset, int preserve_folder_structure, const char *root_folder, int write_to_source_folder, char *out, int sz) {
818     get_output_path_int (it, plt, outfolder_user, outfile, encoder_preset, preserve_folder_structure, root_folder, write_to_source_folder, out, sz, 1);
819 }
820 
821 static void
get_output_path_1_0(DB_playItem_t * it,const char * outfolder,const char * outfile,ddb_encoder_preset_t * encoder_preset,char * out,int sz)822 get_output_path_1_0 (DB_playItem_t *it, const char *outfolder, const char *outfile, ddb_encoder_preset_t *encoder_preset, char *out, int sz) {
823     fprintf (stderr, "converter: warning: old version of \"get_output_path\" has been called, please update your plugins which depend on converter 1.1\n");
824     *out = 0;
825     sz = 0;
826 }
827 
828 static int
check_dir(const char * dir,mode_t mode)829 check_dir (const char *dir, mode_t mode)
830 {
831     char *tmp = strdup (dir);
832     char *slash = tmp;
833     struct stat stat_buf;
834     do
835     {
836         slash = strstr (slash+1, "/");
837         if (slash)
838             *slash = 0;
839         if (-1 == stat (tmp, &stat_buf))
840         {
841             trace ("creating dir %s\n", tmp);
842             if (0 != mkdir (tmp, mode))
843             {
844                 trace ("Failed to create %s\n", tmp);
845                 free (tmp);
846                 return 0;
847             }
848         }
849         if (slash)
850             *slash = '/';
851     } while (slash);
852     free (tmp);
853     return 1;
854 }
855 
856 static inline void
write_int32_le(char * p,uint32_t value)857 write_int32_le (char *p, uint32_t value) {
858     p[0] = value & 0xff;
859     p[1] = (value >> 8) & 0xff;
860     p[2] = (value >> 16) & 0xff;
861     p[3] = (value >> 24) & 0xff;
862 }
863 
864 static inline void
write_int16_le(char * p,uint16_t value)865 write_int16_le (char *p, uint16_t value) {
866     p[0] = value & 0xff;
867     p[1] = (value >> 8) & 0xff;
868 }
869 
870 
871 int
convert(DB_playItem_t * it,const char * out,int output_bps,int output_is_float,ddb_encoder_preset_t * encoder_preset,ddb_dsp_preset_t * dsp_preset,int * abort)872 convert (DB_playItem_t *it, const char *out, int output_bps, int output_is_float, ddb_encoder_preset_t *encoder_preset, ddb_dsp_preset_t *dsp_preset, int *abort) {
873     char *buffer = NULL;
874     char *dspbuffer = NULL;
875     if (deadbeef->pl_get_item_duration (it) <= 0) {
876         deadbeef->pl_lock ();
877         fprintf (stderr, "converter: stream %s doesn't have finite length, skipped\n", deadbeef->pl_find_meta (it, ":URI"));
878         deadbeef->pl_unlock ();
879         return -1;
880     }
881 
882     int err = -1;
883     FILE *enc_pipe = NULL;
884     int temp_file = -1;
885     DB_decoder_t *dec = NULL;
886     DB_fileinfo_t *fileinfo = NULL;
887     char input_file_name[PATH_MAX] = "";
888     deadbeef->pl_lock ();
889     dec = (DB_decoder_t *)deadbeef->plug_get_for_id (deadbeef->pl_find_meta (it, ":DECODER"));
890     deadbeef->pl_unlock ();
891 
892     if (dec) {
893         fileinfo = dec->open (0);
894         if (fileinfo && dec->init (fileinfo, DB_PLAYITEM (it)) != 0) {
895             deadbeef->pl_lock ();
896             fprintf (stderr, "converter: failed to decode file %s\n", deadbeef->pl_find_meta (it, ":URI"));
897             deadbeef->pl_unlock ();
898             goto error;
899         }
900         if (fileinfo) {
901             if (output_bps == -1) {
902                 output_bps = fileinfo->fmt.bps;
903                 output_is_float = fileinfo->fmt.is_float;
904             }
905 
906             char *final_path = strdupa (out);
907             char *sep = strrchr (final_path, '/');
908             if (sep) {
909                 *sep = 0;
910                 if (!check_dir (final_path, 0755)) {
911                     fprintf (stderr, "converter: failed to create output folder: %s\n", final_path);
912                     goto error;
913                 }
914             }
915 
916             if (encoder_preset->method == DDB_ENCODER_METHOD_FILE) {
917                 const char *tmp = getenv ("TMPDIR");
918                 if (!tmp) {
919                     tmp = "/tmp";
920                 }
921                 snprintf (input_file_name, sizeof (input_file_name), "%s/ddbconvXXXXXX", tmp);
922                 char *res = mktemp (input_file_name);
923                 strcat (input_file_name, ".wav");
924             }
925             else {
926                 strcpy (input_file_name, "-");
927             }
928 
929             char enc[2000];
930             memset (enc, 0, sizeof (enc));
931 
932             char escaped_out[PATH_MAX];
933             escape_filepath (out, escaped_out, sizeof (escaped_out));
934 
935             // formatting: %o = outfile, %i = infile
936             char *e = encoder_preset->encoder;
937             char *o = enc;
938             *o = 0;
939 
940 #ifdef __APPLE__
941             strcpy (o, "/usr/local/bin/");
942             o += 15;
943 #endif
944 
945             int len = sizeof (enc);
946             while (e && *e) {
947                 if (len <= 0) {
948                     fprintf (stderr, "converter: failed to assemble encoder command line - buffer is not big enough, try to shorten your parameters. max allowed length is %u characters\n", (unsigned)sizeof (enc));
949                     goto error;
950                 }
951                 if (e[0] == '%' && e[1]) {
952                     if (e[1] == 'o') {
953                         int l = snprintf (o, len, "\"%s\"", escaped_out);
954                         o += l;
955                         len -= l;
956                     }
957                     else if (e[1] == 'i') {
958                         int l = snprintf (o, len, "\"%s\"", input_file_name);
959                         o += l;
960                         len -= l;
961                     }
962                     else {
963                         strncpy (o, e, 2);
964                         o += 2;
965                         len -= 2;
966                     }
967                     e += 2;
968                 }
969                 else {
970                     *o++ = *e++;
971                     *o = 0;
972                     len--;
973                 }
974             }
975 
976             fprintf (stderr, "converter: will encode using: %s\n", enc[0] ? enc : "internal RIFF WAVE writer");
977 
978             mode_t wrmode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
979 
980             if (!encoder_preset->encoder[0]) {
981                 // write to wave file
982                 trace ("opening %s\n", out);
983                 temp_file = open (out, O_LARGEFILE | O_WRONLY | O_CREAT | O_TRUNC, wrmode);
984                 if (temp_file == -1) {
985                     fprintf (stderr, "converter: failed to open output wave file %s\n", out);
986                     goto error;
987                 }
988             }
989             else if (encoder_preset->method == DDB_ENCODER_METHOD_FILE) {
990                 temp_file = open (input_file_name, O_LARGEFILE | O_WRONLY | O_CREAT | O_TRUNC, wrmode);
991                 if (temp_file == -1) {
992                     fprintf (stderr, "converter: failed to open temp file %s\n", input_file_name);
993                     goto error;
994                 }
995             }
996             else {
997                 enc_pipe = popen (enc, "w");
998                 if (!enc_pipe) {
999                     fprintf (stderr, "converter: failed to open encoder\n");
1000                     goto error;
1001                 }
1002             }
1003 
1004             if (temp_file == -1 && enc_pipe) {
1005                 temp_file = fileno (enc_pipe);
1006             }
1007 
1008             // write wave header
1009             char wavehdr_int[] = {
1010                 0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56, 0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04, 0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61
1011             };
1012             char wavehdr_float[] = {
1013                 0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56, 0x45, 0x66, 0x6d, 0x74, 0x20, 0x28, 0x00, 0x00, 0x00, 0xfe, 0xff, 0x02, 0x00, 0x40, 0x1f, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x00, 0x08, 0x00, 0x20, 0x00, 0x16, 0x00, 0x20, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71, 0x66, 0x61, 0x63, 0x74, 0x04, 0x00, 0x00, 0x00, 0xc5, 0x5b, 0x00, 0x00, 0x64, 0x61, 0x74, 0x61
1014             };
1015             char *wavehdr = output_is_float ? wavehdr_float : wavehdr_int;
1016             int wavehdr_size = output_is_float ? sizeof (wavehdr_float) : sizeof (wavehdr_int);
1017             int header_written = 0;
1018             uint32_t outsize = 0;
1019             uint32_t outsr = fileinfo->fmt.samplerate;
1020             uint16_t outch = fileinfo->fmt.channels;
1021 
1022             int samplesize = fileinfo->fmt.channels * fileinfo->fmt.bps / 8;
1023 
1024             // block size
1025             int bs = 2000 * samplesize;
1026             // expected buffer size after worst-case dsp
1027             int dspsize = bs/samplesize*sizeof(float)*8*48;
1028             buffer = malloc (dspsize);
1029             // account for up to float32 7.1 resampled to 48x ratio
1030             dspbuffer = malloc (dspsize);
1031             int eof = 0;
1032             for (;;) {
1033                 if (eof) {
1034                     break;
1035                 }
1036                 if (abort && *abort) {
1037                     break;
1038                 }
1039                 int sz = dec->read (fileinfo, buffer, bs);
1040 
1041                 if (sz != bs) {
1042                     eof = 1;
1043                 }
1044                 if (dsp_preset) {
1045                     ddb_waveformat_t fmt;
1046                     ddb_waveformat_t outfmt;
1047                     memcpy (&fmt, &fileinfo->fmt, sizeof (fmt));
1048                     memcpy (&outfmt, &fileinfo->fmt, sizeof (fmt));
1049                     fmt.bps = 32;
1050                     fmt.is_float = 1;
1051                     deadbeef->pcm_convert (&fileinfo->fmt, buffer, &fmt, dspbuffer, sz);
1052 
1053                     ddb_dsp_context_t *dsp = dsp_preset->chain;
1054                     int frames = sz / samplesize;
1055                     while (dsp) {
1056                         frames = dsp->plugin->process (dsp, (float *)dspbuffer, frames, dspsize / (fmt.channels * 4), &fmt, NULL);
1057                         if (frames <= 0) {
1058                             break;
1059                         }
1060                         dsp = dsp->next;
1061                     }
1062                     if (frames <= 0) {
1063                         fprintf (stderr, "converter: dsp error, please check you dsp preset\n");
1064                         goto error;
1065                     }
1066 
1067                     outsr = fmt.samplerate;
1068                     outch = fmt.channels;
1069 
1070                     outfmt.bps = output_bps;
1071                     outfmt.is_float = output_is_float;
1072                     outfmt.channels = outch;
1073                     outfmt.samplerate = outsr;
1074 
1075                     int n = deadbeef->pcm_convert (&fmt, dspbuffer, &outfmt, buffer, frames * sizeof (float) * fmt.channels);
1076                     sz = n;
1077                 }
1078                 else if (fileinfo->fmt.bps != output_bps || fileinfo->fmt.is_float != output_is_float) {
1079                     ddb_waveformat_t outfmt;
1080                     memcpy (&outfmt, &fileinfo->fmt, sizeof (outfmt));
1081                     outfmt.bps = output_bps;
1082                     outfmt.is_float = output_is_float;
1083                     outfmt.channels = outch;
1084                     outfmt.samplerate = outsr;
1085 
1086                     int frames = sz / samplesize;
1087                     int n = deadbeef->pcm_convert (&fileinfo->fmt, buffer, &outfmt, dspbuffer, frames * samplesize);
1088                     memcpy (buffer, dspbuffer, n);
1089                     sz = n;
1090                 }
1091                 outsize += sz;
1092 
1093                 if (!header_written) {
1094                     uint64_t size = (int64_t)(it->endsample-it->startsample) * outch * output_bps / 8;
1095                     if (!size) {
1096                         size = (double)deadbeef->pl_get_item_duration (it) * fileinfo->fmt.samplerate * outch * output_bps / 8;
1097 
1098                     }
1099 
1100                     if (outsr != fileinfo->fmt.samplerate) {
1101                         uint64_t temp = size;
1102                         temp *= outsr;
1103                         temp /= fileinfo->fmt.samplerate;
1104                         size  = temp;
1105                     }
1106 
1107                     uint64_t chunksize;
1108                     chunksize = size + 40;
1109 
1110                     // for float, add 36 more
1111                     if (output_is_float) {
1112                         chunksize += 36;
1113                     }
1114 
1115                     uint32_t size32 = 0xffffffff;
1116                     if (chunksize <= 0xffffffff) {
1117                         size32 = chunksize;
1118                     }
1119                     write_int32_le (wavehdr+4, size32);
1120                     write_int16_le (wavehdr+22, outch);
1121                     write_int32_le (wavehdr+24, outsr);
1122                     uint16_t blockalign = outch * output_bps / 8;
1123                     write_int16_le (wavehdr+32, blockalign);
1124                     write_int16_le (wavehdr+34, output_bps);
1125 
1126                     size32 = 0xffffffff;
1127                     if (size <= 0xffffffff) {
1128                         size32 = size;
1129                     }
1130 
1131                     if (wavehdr_size != write (temp_file, wavehdr, wavehdr_size)) {
1132                         fprintf (stderr, "converter: wave header write error\n");
1133                         goto error;
1134                     }
1135                     if (encoder_preset->method == DDB_ENCODER_METHOD_PIPE) {
1136                         size32 = 0;
1137                     }
1138                     if (write (temp_file, &size32, sizeof (size32)) != sizeof (size32)) {
1139                         fprintf (stderr, "converter: wave header size write error\n");
1140                         goto error;
1141                     }
1142                     header_written = 1;
1143                 }
1144 
1145                 int64_t res = write (temp_file, buffer, sz);
1146                 if (sz != res) {
1147                     fprintf (stderr, "converter: write error (%"PRId64" bytes written out of %d)\n", res, sz);
1148                     goto error;
1149                 }
1150             }
1151             if (abort && *abort) {
1152                 goto error;
1153             }
1154             if (temp_file != -1 && (!enc_pipe || temp_file != fileno (enc_pipe))) {
1155                 lseek (temp_file, wavehdr_size, SEEK_SET);
1156                 if (4 != write (temp_file, &outsize, 4)) {
1157                     fprintf (stderr, "converter: data size write error\n");
1158                     goto error;
1159                 }
1160 
1161                 if (temp_file != -1 && (!enc_pipe || temp_file != fileno (enc_pipe))) {
1162                     close (temp_file);
1163                     temp_file = -1;
1164                 }
1165             }
1166 
1167             if (encoder_preset->encoder[0] && encoder_preset->method == DDB_ENCODER_METHOD_FILE) {
1168                 enc_pipe = popen (enc, "w");
1169             }
1170         }
1171     }
1172     err = 0;
1173 error:
1174     if (buffer) {
1175         free (buffer);
1176         buffer = NULL;
1177     }
1178     if (dspbuffer) {
1179         free (dspbuffer);
1180         dspbuffer = NULL;
1181     }
1182     if (temp_file != -1 && (!enc_pipe || temp_file != fileno (enc_pipe))) {
1183         close (temp_file);
1184         temp_file = -1;
1185     }
1186     if (enc_pipe) {
1187         pclose (enc_pipe);
1188         enc_pipe = NULL;
1189     }
1190     if (dec && fileinfo) {
1191         dec->free (fileinfo);
1192         fileinfo = NULL;
1193     }
1194     if (abort && *abort && out[0]) {
1195         unlink (out);
1196     }
1197     if (input_file_name[0] && strcmp (input_file_name, "-")) {
1198         unlink (input_file_name);
1199     }
1200     if (err != 0) {
1201         return err;
1202     }
1203 
1204     // write junklib tags
1205 
1206     DB_playItem_t *out_it = NULL;
1207 
1208     if (encoder_preset->tag_id3v2 || encoder_preset->tag_id3v1 || encoder_preset->tag_apev2 || encoder_preset->tag_flac || encoder_preset->tag_oggvorbis) {
1209         out_it = deadbeef->pl_item_alloc ();
1210         deadbeef->pl_item_copy (out_it, it);
1211         deadbeef->pl_set_item_flags (out_it, 0);
1212         DB_metaInfo_t *m = deadbeef->pl_get_metadata_head (out_it);
1213         while (m) {
1214             DB_metaInfo_t *next = m->next;
1215             if (m->key[0] == ':' || m->key[0] == '!' || !strcasecmp (m->key, "cuesheet")) {
1216                 if (strcasestr (m->key, ":REPLAYGAIN_")) {
1217                     deadbeef->pl_delete_metadata (out_it, m);
1218                 }
1219             }
1220             m = next;
1221         }
1222         deadbeef->pl_replace_meta (out_it, ":URI", out);
1223     }
1224 
1225     uint32_t tagflags = 0;
1226     if (encoder_preset->tag_id3v2) {
1227         tagflags |= JUNK_WRITE_ID3V2;
1228     }
1229     if (encoder_preset->tag_id3v1) {
1230         tagflags |= JUNK_WRITE_ID3V1;
1231     }
1232     if (encoder_preset->tag_apev2) {
1233         tagflags |= JUNK_WRITE_APEV2;
1234     }
1235 
1236     if (tagflags) {
1237         tagflags |= JUNK_STRIP_ID3V2 | JUNK_STRIP_APEV2 | JUNK_STRIP_ID3V1;
1238         deadbeef->junk_rewrite_tags (out_it, tagflags, encoder_preset->id3v2_version + 3, "iso8859-1");
1239     }
1240 
1241     // write flac tags
1242     if (encoder_preset->tag_flac) {
1243         // find flac decoder plugin
1244         DB_decoder_t **plugs = deadbeef->plug_get_decoder_list ();
1245         DB_decoder_t *flac = NULL;
1246         for (int i = 0; plugs[i]; i++) {
1247             if (!strcmp (plugs[i]->plugin.id, "stdflac")) {
1248                 flac = plugs[i];
1249                 break;
1250             }
1251         }
1252         if (!flac) {
1253             fprintf (stderr, "converter: flac plugin not found, cannot write flac metadata\n");
1254         }
1255         else {
1256             if (0 != flac->write_metadata (out_it)) {
1257                 fprintf (stderr, "converter: failed to write flac metadata, not a flac file?\n");
1258             }
1259         }
1260     }
1261 
1262     // write vorbis tags
1263     if (encoder_preset->tag_oggvorbis) {
1264         // find flac decoder plugin
1265         DB_decoder_t **plugs = deadbeef->plug_get_decoder_list ();
1266         int res = -1;
1267         for (int i = 0; plugs[i]; i++) {
1268             if (!strcmp (plugs[i]->plugin.id, "stdogg")
1269                     || !strcmp (plugs[i]->plugin.id, "stdopus")) {
1270                 res = plugs[i]->write_metadata (out_it);
1271                 if (!res) {
1272                     break;
1273                 }
1274             }
1275         }
1276         if (res) {
1277             fprintf (stderr, "converter: failed to write ogg metadata, not an ogg file?\n");
1278         }
1279     }
1280     if (out_it) {
1281         deadbeef->pl_item_unref (out_it);
1282     }
1283 
1284     return err;
1285 }
1286 
1287 int
convert_1_0(DB_playItem_t * it,const char * outfolder,const char * outfile,int output_bps,int output_is_float,int preserve_folder_structure,const char * root_folder,ddb_encoder_preset_t * encoder_preset,ddb_dsp_preset_t * dsp_preset,int * abort)1288 convert_1_0 (DB_playItem_t *it, const char *outfolder, const char *outfile, int output_bps, int output_is_float, int preserve_folder_structure, const char *root_folder, ddb_encoder_preset_t *encoder_preset, ddb_dsp_preset_t *dsp_preset, int *abort) {
1289     fprintf (stderr, "converter: warning: old version of \"convert\" has been called, please update your plugins which depend on converter 1.1\n");
1290     return -1;
1291 }
1292 
1293 int
converter_cmd(int cmd,...)1294 converter_cmd (int cmd, ...) {
1295     return -1;
1296 }
1297 
1298 int
converter_start(void)1299 converter_start (void) {
1300     load_encoder_presets ();
1301     load_dsp_presets ();
1302 
1303     return 0;
1304 }
1305 
1306 int
converter_stop(void)1307 converter_stop (void) {
1308     free_encoder_presets ();
1309     free_dsp_presets ();
1310     return 0;
1311 }
1312 
1313 // define plugin interface
1314 static ddb_converter_t plugin = {
1315     .misc.plugin.api_vmajor = 1,
1316     .misc.plugin.api_vminor = 0,
1317     .misc.plugin.version_major = 1,
1318     .misc.plugin.version_minor = 4,
1319     .misc.plugin.type = DB_PLUGIN_MISC,
1320     .misc.plugin.name = "Converter",
1321     .misc.plugin.id = "converter",
1322     .misc.plugin.descr = "Converts any supported formats to other formats.\n"
1323         "Requires separate GUI plugin, e.g. Converter GTK UI\n",
1324     .misc.plugin.copyright =
1325         "Converter for DeaDBeeF Player\n"
1326         "Copyright (C) 2009-2015 Alexey Yakovenko and other contributors\n"
1327         "\n"
1328         "This software is provided 'as-is', without any express or implied\n"
1329         "warranty.  In no event will the authors be held liable for any damages\n"
1330         "arising from the use of this software.\n"
1331         "\n"
1332         "Permission is granted to anyone to use this software for any purpose,\n"
1333         "including commercial applications, and to alter it and redistribute it\n"
1334         "freely, subject to the following restrictions:\n"
1335         "\n"
1336         "1. The origin of this software must not be misrepresented; you must not\n"
1337         " claim that you wrote the original software. If you use this software\n"
1338         " in a product, an acknowledgment in the product documentation would be\n"
1339         " appreciated but is not required.\n"
1340         "\n"
1341         "2. Altered source versions must be plainly marked as such, and must not be\n"
1342         " misrepresented as being the original software.\n"
1343         "\n"
1344         "3. This notice may not be removed or altered from any source distribution.\n"
1345     ,
1346     .misc.plugin.website = "http://deadbeef.sf.net",
1347     .misc.plugin.start = converter_start,
1348     .misc.plugin.stop = converter_stop,
1349     .misc.plugin.command = converter_cmd,
1350     .encoder_preset_alloc = encoder_preset_alloc,
1351     .encoder_preset_free = encoder_preset_free,
1352     .encoder_preset_load = encoder_preset_load,
1353     .encoder_preset_save = encoder_preset_save,
1354     .encoder_preset_copy = encoder_preset_copy,
1355     .encoder_preset_get_list = encoder_preset_get_list,
1356     .encoder_preset_get_for_idx = encoder_preset_get_for_idx,
1357     .encoder_preset_append = encoder_preset_append,
1358     .encoder_preset_remove = encoder_preset_remove,
1359     .encoder_preset_replace = encoder_preset_replace,
1360     .dsp_preset_alloc = dsp_preset_alloc,
1361     .dsp_preset_free = dsp_preset_free,
1362     .dsp_preset_load = dsp_preset_load,
1363     .dsp_preset_save = dsp_preset_save,
1364     .dsp_preset_copy = dsp_preset_copy,
1365     .dsp_preset_get_list = dsp_preset_get_list,
1366     .dsp_preset_get_for_idx = dsp_preset_get_for_idx,
1367     .dsp_preset_append = dsp_preset_append,
1368     .dsp_preset_remove = dsp_preset_remove,
1369     .dsp_preset_replace = dsp_preset_replace,
1370     .get_output_path_1_0 = get_output_path_1_0,
1371     .convert_1_0 = convert_1_0,
1372     // 1.1 entry points
1373     .load_encoder_presets = load_encoder_presets,
1374     .load_dsp_presets = load_dsp_presets,
1375     .free_encoder_presets = free_encoder_presets,
1376     .free_dsp_presets = free_dsp_presets,
1377     // 1.2 entry points
1378     .convert = convert,
1379     .get_output_path = get_output_path,
1380     // 1.4 entry points
1381     .get_output_path2 = get_output_path2,
1382 };
1383 
1384 DB_plugin_t *
converter_load(DB_functions_t * api)1385 converter_load (DB_functions_t *api) {
1386     deadbeef = api;
1387     return DB_PLUGIN (&plugin);
1388 }
1389