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