Project

General

Profile

filewriter.cc

Modified v3.6 source - Jim Turner, March 16, 2015 19:53

 
1
/*  FileWriter-Plugin
2
 *  (C) copyright 2007 merging of Disk Writer and Out-Lame by Michael Färber
3
 *
4
 *  Original Out-Lame-Plugin:
5
 *  (C) copyright 2002 Lars Siebold <khandha5@gmx.net>
6
 *  (C) copyright 2006-2007 porting to audacious by Yoshiki Yazawa <yaz@cc.rim.or.jp>
7
 *
8
 *  This program is free software; you can redistribute it and/or modify
9
 *  it under the terms of the GNU General Public License as published by
10
 *  the Free Software Foundation; either version 2 of the License, or
11
 *  (at your option) any later version.
12
 *
13
 *  This program is distributed in the hope that it will be useful,
14
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 *  GNU General Public License for more details.
17
 *
18
 *  You should have received a copy of the GNU General Public License
19
 *  along with this program; if not, write to the Free Software
20
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21
 */
22

    
23
#include <string.h>
24
#include <gtk/gtk.h>
25

    
26
#include <libaudcore/audstrings.h>
27
#include <libaudcore/i18n.h>
28
#include <libaudcore/plugin.h>
29
#include <libaudcore/preferences.h>
30
#include <libaudcore/runtime.h>
31

    
32
#include "filewriter.h"
33
#include "plugins.h"
34
#include "convert.h"
35

    
36
class FileWriter : public OutputPlugin
37
{
38
public:
39
    static const char about[];
40
    static const char * const defaults[];
41
    static const PreferencesWidget widgets[];
42
    static const PluginPreferences prefs;
43

    
44
    static constexpr PluginInfo info = {
45
        N_("FileWriter Plugin"),
46
        PACKAGE,
47
        about,
48
        & prefs
49
    };
50

    
51
    constexpr FileWriter () : OutputPlugin (info, 0, true) {}
52

    
53
    bool init ();
54
    void cleanup ();
55

    
56
    StereoVolume get_volume () { return {0, 0}; }
57
    void set_volume (StereoVolume v) {}
58

    
59
    void set_info (const char * filename, const Tuple & tuple);
60
    bool open_audio (int fmt, int rate, int nch);
61
    void close_audio ();
62

    
63
    void period_wait () {}
64
    int write_audio (const void * ptr, int length);
65
    void drain () {}
66

    
67
    int get_delay ()
68
        { return 0; }
69

    
70
    void pause (bool pause) {}
71
    void flush () {}
72
};
73

    
74
EXPORT FileWriter aud_plugin_instance;
75

    
76
static String in_filename;
77
static Tuple in_tuple;
78

    
79
struct format_info input;
80

    
81
static GtkWidget * path_hbox, * path_dirbrowser;
82
static GtkWidget * fileext_combo, * plugin_button;
83

    
84
enum fileext_t
85
{
86
    WAV = 0,
87
#ifdef FILEWRITER_MP3
88
    MP3,
89
#endif
90
#ifdef FILEWRITER_VORBIS
91
    VORBIS,
92
#endif
93
#ifdef FILEWRITER_FLAC
94
    FLAC,
95
#endif
96
    FILEEXT_MAX
97
};
98

    
99
static int fileext;
100
static const char *fileext_str[FILEEXT_MAX] =
101
{
102
    "wav",
103
#ifdef FILEWRITER_MP3
104
    "mp3",
105
#endif
106
#ifdef FILEWRITER_VORBIS
107
    "ogg",
108
#endif
109
#ifdef FILEWRITER_FLAC
110
    "flac"
111
#endif
112
};
113

    
114
static FileWriterImpl *plugin;
115

    
116
static gboolean save_original;
117

    
118
static GtkWidget *filenamefrom_hbox, *filenamefrom_label;
119
static gboolean filenamefromtags;
120

    
121
static GtkWidget *use_suffix_toggle = nullptr;
122
static gboolean use_suffix;
123

    
124
static gboolean use_stdout;  /* JWT */
125

    
126
static GtkWidget *prependnumber_toggle = nullptr;
127
static gboolean prependnumber;
128

    
129
static GtkWidget * saveplace1 = nullptr;
130
static GtkWidget * saveplace2 = nullptr;
131
static String file_path;
132

    
133
static VFSFile output_file;
134

    
135
FileWriterImpl *plugins[FILEEXT_MAX] = {
136
    &wav_plugin,
137
#ifdef FILEWRITER_MP3
138
    &mp3_plugin,
139
#endif
140
#ifdef FILEWRITER_VORBIS
141
    &vorbis_plugin,
142
#endif
143
#ifdef FILEWRITER_FLAC
144
    &flac_plugin,
145
#endif
146
};
147

    
148
static void set_plugin(void)
149
{
150
    if (fileext < 0 || fileext >= FILEEXT_MAX)
151
        fileext = 0;
152

    
153
    plugin = plugins[fileext];
154
}
155

    
156
const char * const FileWriter::defaults[] = {
157
 "fileext", "0", /* WAV */
158
 "filenamefromtags", "TRUE",
159
 "prependnumber", "FALSE",
160
 "save_original", "TRUE",
161
 "use_suffix", "FALSE",
162
 "use_stdout", "FALSE",   /* JWT */
163
 nullptr};
164

    
165
bool FileWriter::init ()
166
{
167
    aud_config_set_defaults ("filewriter", defaults);
168

    
169
    fileext = aud_get_int ("filewriter", "fileext");
170
    filenamefromtags = aud_get_bool ("filewriter", "filenamefromtags");
171
    file_path = aud_get_str ("filewriter", "file_path");
172
    prependnumber = aud_get_bool ("filewriter", "prependnumber");
173
    save_original = aud_get_bool ("filewriter", "save_original");
174
    use_suffix = aud_get_bool ("filewriter", "use_suffix");
175
    use_stdout = aud_get_bool ("filewriter", "use_stdout");   /* JWT */
176

    
177
    if (! file_path[0])
178
    {
179
        file_path = String (filename_to_uri (g_get_home_dir ()));
180
        g_return_val_if_fail (file_path != nullptr, false);
181
    }
182

    
183
    set_plugin();
184
    if (plugin->init)
185
        plugin->init();
186

    
187
    return true;
188
}
189

    
190
void FileWriter::cleanup ()
191
{
192
    file_path = String ();
193
}
194

    
195
static VFSFile safe_create (const char * filename)
196
{
197
    if (use_stdout || ! VFSFile::test_file (filename, VFS_EXISTS))
198
        return VFSFile (filename, "w");
199

    
200
    const char * extension = strrchr (filename, '.');
201

    
202
    for (int count = 1; count < 100; count ++)
203
    {
204
        StringBuf scratch = extension ?
205
         str_printf ("%.*s-%d%s", (int) (extension - filename), filename, count, extension) :
206
         str_printf ("%s-%d", filename, count);
207

    
208
        if (! VFSFile::test_file (scratch, VFS_EXISTS))
209
            return VFSFile (scratch, "w");
210
    }
211

    
212
    return VFSFile ();
213
}
214

    
215
void FileWriter::set_info (const char * filename, const Tuple & tuple)
216
{
217
    in_filename = String (filename);
218
    in_tuple = tuple.ref ();
219
}
220

    
221
bool FileWriter::open_audio (int fmt, int rate, int nch)
222
{
223
    String filename, directory;
224

    
225
    input.format = fmt;
226
    input.frequency = rate;
227
    input.channels = nch;
228

    
229
    if (use_stdout)   /* JWT */
230
    {
231
#ifdef _WINDOWS
232
               filename = String ("file://CON");
233
#endif
234
#ifndef _WINDOWS
235
               filename = String ("file:///dev/stdout");
236
#endif
237

    
238
    }
239
    else
240
    {
241
        if (filenamefromtags)
242
        {
243
            String title = in_tuple.get_str (Tuple::FormattedTitle);
244
            StringBuf buf = str_encode_percent (title);
245
            str_replace_char (buf, '/', '-');
246
            filename = String (buf);
247
        }
248
        else
249
        {
250
            const char * original = strrchr (in_filename, '/');
251
            g_return_val_if_fail (original != nullptr, 0);
252
            filename = String (original + 1);
253

    
254
            if (! use_suffix)
255
            {
256
                const char * temp;
257
                if ((temp = strrchr (filename, '.')))
258
                    filename = String (str_copy (filename, temp - filename));
259
            }
260
        }
261

    
262
        if (prependnumber)
263
        {
264
            int number = in_tuple.get_int (Tuple::Track);
265
            if (number >= 0)
266
                filename = String (str_printf ("%d%%20%s", number, (const char *) filename));
267
        }
268

    
269
        if (save_original)
270
        {
271
            const char * temp = strrchr (in_filename, '/');
272
            g_return_val_if_fail (temp != nullptr, 0);
273
            directory = String (str_copy (in_filename, temp + 1 - in_filename));
274
        }
275
        else
276
        {
277
            g_return_val_if_fail (file_path[0], 0);
278
            if (file_path[strlen (file_path) - 1] == '/')
279
                directory = String (file_path);
280
            else
281
                directory = String (str_concat ({file_path, "/"}));
282
        }
283

    
284
        filename = String (str_printf ("%s%s.%s", (const char *) directory,
285
         (const char *) filename, fileext_str[fileext]));
286
    }
287

    
288
    output_file = safe_create (filename);
289
    if (! output_file)
290
        goto err;
291

    
292
    convert_init (fmt, plugin->format_required (fmt));
293

    
294
    if (plugin->open (output_file, input, in_tuple))
295
        return true;
296

    
297
err:
298
    in_filename = String ();
299
    in_tuple = Tuple ();
300
    return false;
301
}
302

    
303
int FileWriter::write_audio (const void * ptr, int length)
304
{
305
    auto & buf = convert_process (ptr, length);
306
    plugin->write (output_file, buf.begin (), buf.len ());
307

    
308
    return length;
309
}
310

    
311
void FileWriter::close_audio ()
312
{
313
    plugin->close (output_file);
314
    convert_free ();
315

    
316
    output_file = VFSFile ();
317
    in_filename = String ();
318
    in_tuple = Tuple ();
319
}
320

    
321
static void configure_response_cb (void)
322
{
323
    fileext = gtk_combo_box_get_active(GTK_COMBO_BOX(fileext_combo));
324

    
325
    char * temp = gtk_file_chooser_get_uri ((GtkFileChooser *) path_dirbrowser);
326
    file_path = String (temp);
327
    g_free (temp);
328

    
329
    use_suffix =
330
        gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(use_suffix_toggle));
331

    
332
    prependnumber =
333
        gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(prependnumber_toggle));
334

    
335
    aud_set_int ("filewriter", "fileext", fileext);
336
    aud_set_bool ("filewriter", "filenamefromtags", filenamefromtags);
337
    aud_set_str ("filewriter", "file_path", file_path);
338
    aud_set_bool ("filewriter", "prependnumber", prependnumber);
339
    aud_set_bool ("filewriter", "save_original", save_original);
340
    aud_set_bool ("filewriter", "use_suffix", use_suffix);
341
    aud_set_bool ("filewriter", "use_stdout", use_stdout);   /* JWT */
342
}
343

    
344
static void fileext_cb(GtkWidget *combo, void * data)
345
{
346
    fileext = gtk_combo_box_get_active(GTK_COMBO_BOX(fileext_combo));
347
    set_plugin();
348
    if (plugin->init)
349
        plugin->init();
350

    
351
    gtk_widget_set_sensitive(plugin_button, plugin->configure != nullptr);
352
}
353

    
354
static void plugin_configure_cb(GtkWidget *button, void * data)
355
{
356
    if (plugin->configure)
357
        plugin->configure();
358
}
359

    
360

    
361
static void saveplace_original_cb(GtkWidget *button, void * data)
362
{
363
    if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)))
364
    {
365
        gtk_widget_set_sensitive(path_hbox, FALSE);
366
        save_original = TRUE;
367
    }
368
}
369

    
370
static void saveplace_custom_cb(GtkWidget *button, void * data)
371
{
372
    if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)))
373
    {
374
        gtk_widget_set_sensitive(path_hbox, TRUE);
375
        save_original = FALSE;
376
    }
377
}
378

    
379
static void filenamefromtags_cb(GtkWidget *button, void * data)
380
{
381
    if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)))
382
    {
383
        gtk_widget_set_sensitive(use_suffix_toggle, FALSE);
384
        gtk_widget_set_sensitive(saveplace1, TRUE);   /* JWT:NEXT 3 ADDED TO GREY OUT FILE OPTIONS WHEN USING stdout */
385
        gtk_widget_set_sensitive(saveplace2, TRUE);
386
        gtk_widget_set_sensitive(prependnumber_toggle, TRUE);
387
        gtk_widget_set_sensitive(path_hbox, TRUE);
388
        use_suffix = FALSE;
389
        filenamefromtags = TRUE;
390
        use_stdout = FALSE;
391
    }
392
}
393

    
394
static void filenamefromfilename_cb(GtkWidget *button, void * data)
395
{
396
    if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)))
397
    {
398
        gtk_widget_set_sensitive(use_suffix_toggle, TRUE);
399
        gtk_widget_set_sensitive(saveplace1, TRUE);   /* JWT:NEXT 4 ADDED TO GREY OUT FILE OPTIONS WHEN USING stdout */
400
        gtk_widget_set_sensitive(saveplace2, TRUE);
401
        gtk_widget_set_sensitive(prependnumber_toggle, TRUE);
402
        gtk_widget_set_sensitive(path_hbox, TRUE);
403
        filenamefromtags = FALSE;
404
        use_stdout = FALSE;
405
    }
406
}
407

    
408
static void filenamefromstdout_cb(GtkWidget *button, void * data)  /* JWT:ADDED TO GREY OUT FILE OPTIONS WHEN USING stdout */
409
{
410
    if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)))
411
    {
412
        gtk_widget_set_sensitive(path_hbox, FALSE);
413
        gtk_widget_set_sensitive(saveplace1, FALSE);
414
        gtk_widget_set_sensitive(saveplace2, FALSE);
415
        gtk_widget_set_sensitive(use_suffix_toggle, FALSE);
416
        gtk_widget_set_sensitive(prependnumber_toggle, FALSE);
417
        use_suffix = FALSE;
418
        filenamefromtags = TRUE;
419
        use_stdout = TRUE;
420
    }
421
}
422

    
423
static void * file_configure (void)
424
{
425
        GtkWidget * configure_vbox = gtk_vbox_new (FALSE, 6);
426

    
427
        GtkWidget * fileext_hbox = gtk_hbox_new (FALSE, 5);
428
        gtk_box_pack_start(GTK_BOX(configure_vbox), fileext_hbox, FALSE, FALSE, 0);
429

    
430
        GtkWidget * fileext_label = gtk_label_new (_("Output file format:"));
431
        gtk_box_pack_start(GTK_BOX(fileext_hbox), fileext_label, FALSE, FALSE, 0);
432

    
433
        fileext_combo = gtk_combo_box_text_new ();
434
        gtk_combo_box_text_append_text ((GtkComboBoxText *) fileext_combo, "WAV");
435
#ifdef FILEWRITER_MP3
436
        gtk_combo_box_text_append_text ((GtkComboBoxText *) fileext_combo, "MP3");
437
#endif
438
#ifdef FILEWRITER_VORBIS
439
        gtk_combo_box_text_append_text ((GtkComboBoxText *) fileext_combo, "Vorbis");
440
#endif
441
#ifdef FILEWRITER_FLAC
442
        gtk_combo_box_text_append_text ((GtkComboBoxText *) fileext_combo, "FLAC");
443
#endif
444
        gtk_box_pack_start(GTK_BOX(fileext_hbox), fileext_combo, FALSE, FALSE, 0);
445
        gtk_combo_box_set_active(GTK_COMBO_BOX(fileext_combo), fileext);
446

    
447
        plugin_button = gtk_button_new_with_label(_("Configure"));
448
        gtk_widget_set_sensitive(plugin_button, plugin->configure != nullptr);
449
        gtk_box_pack_end(GTK_BOX(fileext_hbox), plugin_button, FALSE, FALSE, 0);
450

    
451
        gtk_box_pack_start(GTK_BOX(configure_vbox), gtk_hseparator_new(), FALSE, FALSE, 0);
452

    
453
        GtkWidget * saveplace_hbox = gtk_hbox_new (FALSE, 5);
454
        gtk_container_add(GTK_CONTAINER(configure_vbox), saveplace_hbox);
455

    
456
        saveplace1 = gtk_radio_button_new_with_label (nullptr,
457
         _("Save into original directory"));
458
        gtk_box_pack_start ((GtkBox *) saveplace_hbox, saveplace1, FALSE, FALSE, 0);
459

    
460
        saveplace2 = gtk_radio_button_new_with_label_from_widget
461
         ((GtkRadioButton *) saveplace1, _("Save into custom directory"));
462

    
463
        if (!save_original)
464
            gtk_toggle_button_set_active ((GtkToggleButton *) saveplace2, TRUE);
465

    
466
        gtk_box_pack_start ((GtkBox *) saveplace_hbox, saveplace2, FALSE, FALSE, 0);
467

    
468
        path_hbox = gtk_hbox_new (FALSE, 5);
469
        gtk_box_pack_start(GTK_BOX(configure_vbox), path_hbox, FALSE, FALSE, 0);
470

    
471
        GtkWidget * path_label = gtk_label_new (_("Output file folder:"));
472
        gtk_box_pack_start ((GtkBox *) path_hbox, path_label, FALSE, FALSE, 0);
473

    
474
        path_dirbrowser =
475
            gtk_file_chooser_button_new (_("Pick a folder"),
476
                                         GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
477
        gtk_file_chooser_set_uri ((GtkFileChooser *) path_dirbrowser, file_path);
478
        gtk_box_pack_start(GTK_BOX(path_hbox), path_dirbrowser, TRUE, TRUE, 0);
479

    
480
        if (save_original)
481
            gtk_widget_set_sensitive(path_hbox, FALSE);
482

    
483
        gtk_box_pack_start(GTK_BOX(configure_vbox), gtk_hseparator_new(), FALSE, FALSE, 0);
484

    
485
        filenamefrom_hbox = gtk_hbox_new (FALSE, 5);
486
        gtk_container_add(GTK_CONTAINER(configure_vbox), filenamefrom_hbox);
487

    
488
        filenamefrom_label = gtk_label_new(_("Generate file name from:"));
489
        gtk_box_pack_start(GTK_BOX(filenamefrom_hbox), filenamefrom_label, FALSE, FALSE, 0);
490

    
491
        GtkWidget * filenamefrom_toggle1 = gtk_radio_button_new_with_label
492
         (nullptr, _("file tag"));
493
        gtk_box_pack_start ((GtkBox *) filenamefrom_hbox, filenamefrom_toggle1, FALSE, FALSE, 0);
494

    
495
        GtkWidget * filenamefrom_toggle2 =
496
         gtk_radio_button_new_with_label_from_widget
497
         ((GtkRadioButton *) filenamefrom_toggle1, _("file name"));
498
        gtk_box_pack_start ((GtkBox *) filenamefrom_hbox, filenamefrom_toggle2, FALSE, FALSE, 0);
499

    
500
        GtkWidget * filenamefrom_toggle3 =   /* JWT */
501
         gtk_radio_button_new_with_label_from_widget
502
         ((GtkRadioButton *) filenamefrom_toggle1, _("stdout"));
503
        gtk_box_pack_start ((GtkBox *) filenamefrom_hbox, filenamefrom_toggle3, FALSE, FALSE, 0);
504

    
505
        if (use_stdout)   /* JWT */
506
        {
507
            gtk_widget_set_sensitive (saveplace1, FALSE);
508
            gtk_widget_set_sensitive (saveplace2, FALSE);
509
            gtk_toggle_button_set_active ((GtkToggleButton *) filenamefrom_toggle3, TRUE);
510
        }
511
        else if (!filenamefromtags)
512
            gtk_toggle_button_set_active ((GtkToggleButton *) filenamefrom_toggle2, TRUE);
513

    
514
        use_suffix_toggle = gtk_check_button_new_with_label(_("Include original file name extension"));
515
        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(use_suffix_toggle), use_suffix);
516
        gtk_box_pack_start(GTK_BOX(configure_vbox), use_suffix_toggle, FALSE, FALSE, 0);
517

    
518
        if (filenamefromtags || use_stdout)
519
            gtk_widget_set_sensitive(use_suffix_toggle, FALSE);
520

    
521
        gtk_box_pack_start(GTK_BOX(configure_vbox), gtk_hseparator_new(), FALSE, FALSE, 0);
522

    
523
        prependnumber_toggle = gtk_check_button_new_with_label(_("Prepend track number to file name"));
524
        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(prependnumber_toggle), prependnumber);
525
        gtk_box_pack_start(GTK_BOX(configure_vbox), prependnumber_toggle, FALSE, FALSE, 0);
526

    
527
        if (use_stdout)  /* JWT */
528
        {
529
            gtk_widget_set_sensitive(path_hbox, FALSE);
530
            gtk_widget_set_sensitive(prependnumber_toggle, FALSE);
531
        }
532

    
533
        g_signal_connect (fileext_combo, "changed", (GCallback) fileext_cb, nullptr);
534
        g_signal_connect (plugin_button, "clicked", (GCallback) plugin_configure_cb, nullptr);
535
        g_signal_connect (saveplace1, "toggled", (GCallback) saveplace_original_cb, nullptr);
536
        g_signal_connect (saveplace2, "toggled", (GCallback) saveplace_custom_cb, nullptr);
537
        g_signal_connect (filenamefrom_toggle1, "toggled", (GCallback) filenamefromtags_cb, nullptr);
538
        g_signal_connect (filenamefrom_toggle2, "toggled", (GCallback) filenamefromfilename_cb, nullptr);
539
        g_signal_connect (filenamefrom_toggle3, "toggled", (GCallback) filenamefromstdout_cb, nullptr);  /* JWT */
540

    
541
        return configure_vbox;
542
}
543

    
544
const char FileWriter::about[] =
545
 N_("This program is free software; you can redistribute it and/or modify\n"
546
    "it under the terms of the GNU General Public License as published by\n"
547
    "the Free Software Foundation; either version 2 of the License, or\n"
548
    "(at your option) any later version.\n"
549
    "\n"
550
    "This program is distributed in the hope that it will be useful,\n"
551
    "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
552
    "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
553
    "GNU General Public License for more details.\n"
554
    "\n"
555
    "You should have received a copy of the GNU General Public License\n"
556
    "along with this program; if not, write to the Free Software\n"
557
    "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,\n"
558
    "USA.");
559

    
560
const PreferencesWidget FileWriter::widgets[] = {
561
    WidgetCustomGTK (file_configure)
562
};
563

    
564
const PluginPreferences FileWriter::prefs = {
565
    {widgets},
566
    nullptr,  // init
567
    configure_response_cb
568
};